on master help sign in
Use unique names for all pattern vars, inline when there's only 1 occurrence

Fix #1086 and fix #1090

commit81566e2f5bba2177e4caf94fa6f42334cbadce78 authorEvan Czaplicki <info@elm-lang.org> dateFri 9 Oct 2015, 03:36 (11 years ago) parentac43b4c
7 files changed+556−155
elm-compiler.cabal+1 src/AST/Expression/Optimized.hs+18−6 src/Generate/JavaScript/Expression.hs+54−68 src/Optimize.hs+33−7 src/Optimize/Inline.hs+291 src/Optimize/Patterns.hs+152−67 src/Optimize/Patterns/DecisionTree.hs+7−7
elm-compiler.cabal+1
@@ -88,6 +88,7 @@ Library
8888 Nitpick.TopLevelTypes,
8989 Optimize,
9090 Optimize.Environment,
91+ Optimize.Inline,
9192 Optimize.Patterns,
9293 Optimize.Patterns.DecisionTree,
9394 Parse.Binop,
src/AST/Expression/Optimized.hs+18−6
@@ -1,7 +1,8 @@
11{-# OPTIONS_GHC -Wall #-}
22module AST.Expression.Optimized
33 ( Def(..), Facts(..), dummyFacts
4− , Expr(..), Decision(..)
4+ , Expr(..)
5+ , Decider(..), Choice(..)
56 ) where
67
78import qualified AST.Expression.General as General
@@ -19,14 +20,12 @@ import qualified Reporting.Region as R
1920data Def
2021 = Def Facts String Expr
2122 | TailDef Facts String [String] Expr
22− deriving (Eq)
2323
2424
2525data Facts = Facts
2626 { home :: Maybe ModuleName.Canonical
2727 , dependencies :: [Var.TopLevel]
2828 }
29− deriving (Eq)
3029
3130
3231dummyFacts :: Facts
@@ -47,7 +46,7 @@ data Expr
4746 | TailCall String [String] [Expr]
4847 | If [(Expr, Expr)] Expr
4948 | Let [Def] Expr
50− | Case String (DT.DecisionTree Decision) [(Int, Expr)]
49+ | Case String (Decider Choice) [(Int, Expr)]
5150 | Data String [Expr]
5251 | DataAccess Expr Int
5352 | Access Expr String
@@ -56,10 +55,23 @@ data Expr
5655 | Port (General.PortImpl Expr Type.Canonical)
5756 | GLShader String String Literal.GLShaderTipe
5857 | Crash R.Region (Maybe String)
58+
59+
60+data Decider a
61+ = Leaf a
62+ | Chain
63+ { _testChain :: [(DT.Path, DT.Test)]
64+ , _success :: Decider a
65+ , _failure :: Decider a
66+ }
67+ | FanOut
68+ { _path :: DT.Path
69+ , _tests :: [(DT.Test, Decider a)]
70+ , _fallback :: Decider a
71+ }
5972 deriving (Eq)
6073
6174
62−data Decision
75+data Choice
6376 = Inline Expr
6477 | Jump Int
65− deriving (Eq)
src/Generate/JavaScript/Expression.hs+54−68
@@ -174,8 +174,8 @@ generateCode expr =
174174 If branches finally ->
175175 generateIf branches finally
176176
177− Case root decisionTree branches ->
178− JsBlock <$> generateCase root decisionTree branches
177+ Case exprName decider jumps ->
178+ JsBlock <$> generateCase exprName decider jumps
179179
180180 ExplicitList elements ->
181181 do jsElements <- mapM generateJsExpr elements
@@ -310,91 +310,75 @@ crushIfsHelp visitedBranches unvisitedBranches finally =
310310
311311generateCase
312312 :: String
313− -> DT.DecisionTree Opt.Decision
313+ -> Opt.Decider Opt.Choice
314314 -> [(Int, Opt.Expr)]
315315 -> State Int [Statement ()]
316−generateCase root decisionTree jumpBranches =
317− do decider <- generateDecisionTree root decisionTree
318− foldM (goto root) decider jumpBranches
316+generateCase exprName decider jumps =
317+ do labelRoot <- Var.fresh
318+ decider <- generateDecider exprName labelRoot decider
319+ foldM (goto labelRoot) decider jumps
319320
320321
322+-- handle any jumps
323+
321324goto :: String -> [Statement ()] -> (Int, Opt.Expr) -> State Int [Statement ()]
322−goto root decider (target, branch) =
325+goto labelRoot deciderStmts (target, branch) =
323326 let
324− deciderStmt =
327+ labeledDeciderStmt =
325328 LabelledStmt ()
326− (targetLabel root target)
327− (DoWhileStmt () (BlockStmt () decider) (BoolLit () False))
329+ (toLabel labelRoot target)
330+ (DoWhileStmt () (BlockStmt () deciderStmts) (BoolLit () False))
328331 in
329332 do code <- generateCode branch
330− return (deciderStmt : toStatementList code)
333+ return (labeledDeciderStmt : toStatementList code)
331334
332335
333−targetLabel :: String -> Int -> Id ()
334−targetLabel root target =
336+toLabel :: String -> Int -> Id ()
337+toLabel root target =
335338 Id () (root ++ "_" ++ show target)
336339
337340
338−generateDecisionTree :: String -> DT.DecisionTree Opt.Decision -> State Int [Statement ()]
339−generateDecisionTree root decisionTree =
341+-- turn deciders into ifs and switches
342+
343+generateDecider
344+ :: String
345+ -> String
346+ -> Opt.Decider Opt.Choice
347+ -> State Int [Statement ()]
348+generateDecider exprName labelRoot decisionTree =
340349 case decisionTree of
341− DT.Match (Opt.Inline branch) ->
350+ Opt.Leaf (Opt.Inline branch) ->
342351 toStatementList <$> generateCode branch
343352
344− DT.Match (Opt.Jump target) ->
345− return [ BreakStmt () (Just (targetLabel root target)) ]
353+ Opt.Leaf (Opt.Jump target) ->
354+ return [ BreakStmt () (Just (toLabel labelRoot target)) ]
346355
347− DT.Decision testPath [(test, subTree)] (Just fallback) ->
356+ Opt.Chain testChain success failure ->
348357 let
349− (tests, deepestSubTree) =
350− collapseTests [(testPath, test)] fallback subTree
351−
352358 makeTest (path, test) =
353− do testExpr <- pathToTestableExpr root path test
359+ do testExpr <- pathToTestableExpr exprName path test
354360 return (InfixExpr () OpStrictEq testExpr (testToExpr test))
355361 in
356− do testExprs <- mapM makeTest tests
362+ do testExprs <- mapM makeTest testChain
357363 let cond = List.foldl1' (InfixExpr () OpLAnd) testExprs
358− thenBranch <- generateDecisionTree root deepestSubTree
359− elseBranch <- generateDecisionTree root fallback
364+ thenBranch <- generateDecider exprName labelRoot success
365+ elseBranch <- generateDecider exprName labelRoot failure
360366 return [ IfStmt () cond (BlockStmt () thenBranch) (BlockStmt () elseBranch) ]
361367
362− DT.Decision testPath edges fallback ->
363− do testExpr <- pathToTestableExpr root testPath (fst (head edges))
364− caseClauses <- mapM (edgeToCaseClause root) edges
365− caseDefault <- fallbackToDefault root fallback
366− return [ SwitchStmt () testExpr (caseClauses ++ caseDefault) ]
367−
368−
369−collapseTests
370− :: [(DT.Path, DT.Test)]
371− -> DT.DecisionTree Opt.Decision
372− -> DT.DecisionTree Opt.Decision
373− -> ([(DT.Path, DT.Test)], DT.DecisionTree Opt.Decision)
374−collapseTests tests outerFallback decisionTree =
375− case decisionTree of
376− DT.Decision testPath [(test, subTree)] (Just fallback)
377− | fallback == outerFallback ->
378− collapseTests ((testPath, test) : tests) outerFallback subTree
368+ Opt.FanOut path edges fallback ->
369+ do testExpr <- pathToTestableExpr exprName path (fst (head edges))
370+ caseClauses <- mapM (edgeToCaseClause exprName labelRoot) edges
371+ caseDefault <- CaseDefault () <$> generateDecider exprName labelRoot fallback
372+ return [ SwitchStmt () testExpr (caseClauses ++ [caseDefault]) ]
379373
380− _ ->
381− (tests, decisionTree)
382−
383−
384−edgeToCaseClause :: String -> (DT.Test, DT.DecisionTree Opt.Decision) -> State Int (CaseClause ())
385−edgeToCaseClause root (test, subTree) =
386− CaseClause () (testToExpr test) <$> generateDecisionTree root subTree
387−
388−
389−pathToTestableExpr :: String -> DT.Path -> DT.Test -> State Int (Expression ())
390−pathToTestableExpr root path exampleTest =
391− do accessExpr <- generateJsExpr (pathToExpr root path)
392− case exampleTest of
393− DT.Constructor _ ->
394− return (DotRef () accessExpr (Id () "ctor"))
395374
396− DT.Literal _ ->
397− return accessExpr
375+edgeToCaseClause
376+ :: String
377+ -> String
378+ -> (DT.Test, Opt.Decider Opt.Choice)
379+ -> State Int (CaseClause ())
380+edgeToCaseClause exprName labelRoot (test, subTree) =
381+ CaseClause () (testToExpr test) <$> generateDecider exprName labelRoot subTree
398382
399383
400384testToExpr :: DT.Test -> Expression ()
@@ -410,15 +394,17 @@ testToExpr test =
410394 Literal.literal lit
411395
412396
413−fallbackToDefault :: String -> Maybe (DT.DecisionTree Opt.Decision) -> State Int [CaseClause ()]
414−fallbackToDefault root fallback =
415− case fallback of
416− Nothing ->
417− return []
397+-- work with paths
418398
419− Just subTree ->
420− do stmts <- generateDecisionTree root subTree
421− return [ CaseDefault () stmts ]
399+pathToTestableExpr :: String -> DT.Path -> DT.Test -> State Int (Expression ())
400+pathToTestableExpr root path exampleTest =
401+ do accessExpr <- generateJsExpr (pathToExpr root path)
402+ case exampleTest of
403+ DT.Constructor _ ->
404+ return (DotRef () accessExpr (Id () "ctor"))
405+
406+ DT.Literal _ ->
407+ return accessExpr
422408
423409
424410pathToExpr :: String -> DT.Path -> Opt.Expr
src/Optimize.hs+33−7
@@ -1,6 +1,7 @@
11module Optimize (optimize) where
22
33import qualified Control.Monad as M
4+import qualified Data.Map as Map
45import qualified Data.Traversable as T
56
67import qualified AST.Expression.General as Expr
@@ -11,6 +12,7 @@ import qualified AST.Module.Name as ModuleName
1112import qualified AST.Pattern as P
1213import qualified AST.Variable as Var
1314import qualified Optimize.Environment as Env
15+import qualified Optimize.Inline as Inline
1416import qualified Optimize.Patterns as Patterns
1517import qualified Optimize.Patterns.DecisionTree as DT
1618import qualified Reporting.Annotation as A
@@ -138,11 +140,13 @@ patternToDefs home rootName pattern =
138140 toDef (name, accessExpr) =
139141 Opt.Def (Opt.Facts home deps) name accessExpr
140142 in
141− map toDef (substitutions rootExpr pattern)
143+ map toDef (patternToSubstitutions rootExpr pattern)
142144
143145
144−substitutions :: Opt.Expr -> P.CanonicalPattern -> [(String, Opt.Expr)]
145−substitutions expr (A.A _ pattern) =
146+-- TURN A PATTERN INTO A BUNCH OF SUBSTITUTIONS
147+
148+patternToSubstitutions :: Opt.Expr -> P.CanonicalPattern -> [(String, Opt.Expr)]
149+patternToSubstitutions expr (A.A _ pattern) =
146150 case pattern of
147151 P.Var name ->
148152 [ (name, expr) ]
@@ -157,10 +161,10 @@ substitutions expr (A.A _ pattern) =
157161 []
158162
159163 P.Alias alias realPattern ->
160− (alias, expr) : substitutions expr realPattern
164+ (alias, expr) : patternToSubstitutions expr realPattern
161165
162166 P.Data _ patterns ->
163− concat (zipWith (substitutions . Opt.DataAccess expr) [0..] patterns)
167+ concat (zipWith (patternToSubstitutions . Opt.DataAccess expr) [0..] patterns)
164168
165169
166170-- OPTIMIZE EXPRESSIONS
@@ -249,9 +253,12 @@ optimizeExpr context annExpr@(A.A region expression) =
249253
250254 Expr.Case expr branches ->
251255 do optExpr <- optimizeExpr Nothing expr
252− optBranches <- T.traverse (mapSnd (optimizeExpr context)) branches
253256 variantDict <- Env.getVariantDict
254− Patterns.optimize variantDict region optExpr optBranches
257+
258+ name <- Env.freshName
259+ optBranches <- T.traverse (optimizeBranch context name) branches
260+ optCase <- Patterns.optimize variantDict region name optBranches
261+ return $ Opt.Let [ Opt.Def Opt.dummyFacts name optExpr ] optCase
255262
256263 Expr.Data name exprs ->
257264 Opt.Data name <$> T.traverse justConvert exprs
@@ -291,6 +298,25 @@ mapSnd func (x, a) =
291298 (,) x <$> func a
292299
293300
301+-- OPTIMIZE CASE BRANCHES
302+
303+optimizeBranch
304+ :: Context
305+ -> String
306+ -> (P.CanonicalPattern, Can.Expr)
307+ -> Env.Optimizer (P.CanonicalPattern, Opt.Expr)
308+optimizeBranch context exprName (pattern, canonicalExpr) =
309+ let
310+ root =
311+ Opt.Var (Var.Canonical Var.Local exprName)
312+
313+ substitutions =
314+ Map.fromList (patternToSubstitutions root pattern)
315+ in
316+ do optExpr <- optimizeExpr context canonicalExpr
317+ (,) pattern <$> Inline.inline substitutions optExpr
318+
319+
294320-- DETECT TAIL CALL
295321
296322isTailCall :: Context -> Can.Expr -> Int -> Maybe (String, [String])
src/Optimize/Inline.hsnew file+291
@@ -0,0 +1,291 @@
1+{-# OPTIONS_GHC -Wall #-}
2+module Optimize.Inline (inline) where
3+
4+import Control.Arrow (second, (***))
5+import Control.Monad (foldM)
6+import qualified Data.List as List
7+import qualified Data.Map as Map
8+
9+import AST.Expression.General (PortImpl(..))
10+import AST.Expression.Optimized (Expr(..), Decider(..), Choice(..))
11+import qualified AST.Expression.Optimized as Opt
12+import qualified AST.Variable as Var
13+import qualified Optimize.Environment as Env
14+
15+
16+inline :: Map.Map String Opt.Expr -> Opt.Expr -> Env.Optimizer Opt.Expr
17+inline rawSubs expression =
18+ let
19+ counts =
20+ count expression
21+
22+ annotatedSubs =
23+ Map.toList (Map.intersectionWith (,) counts rawSubs)
24+ in
25+ do (Subs subs defs) <- foldM processSubs (Subs Map.empty []) annotatedSubs
26+ let inlinedExpr = replace subs expression
27+ return $
28+ if null defs then
29+ inlinedExpr
30+
31+ else
32+ Let (map (\(name, expr) -> Opt.Def Opt.dummyFacts name expr) defs) inlinedExpr
33+
34+
35+data Subs = Subs
36+ { _substitutions :: Map.Map String Opt.Expr
37+ , _definitions :: [(String, Opt.Expr)]
38+ }
39+
40+
41+processSubs :: Subs -> (String, (Int, Opt.Expr)) -> Env.Optimizer Subs
42+processSubs (Subs subs defs) (name, (n, expr)) =
43+ if n == 1 then
44+ return $ Subs (Map.insert name expr subs) defs
45+
46+ else
47+ do uniqueName <- Env.freshName
48+ return $ Subs
49+ (Map.insert name (Opt.Var (Var.local uniqueName)) subs)
50+ ((uniqueName, expr) : defs)
51+
52+-- HELPERS
53+
54+deleteBatch :: [String] -> Map.Map String a -> Map.Map String a
55+deleteBatch names dict =
56+ List.foldl' (flip Map.delete) dict names
57+
58+
59+getDefName :: Opt.Def -> String
60+getDefName def =
61+ case def of
62+ Opt.Def _ name _ ->
63+ name
64+
65+ Opt.TailDef _ name _ _ ->
66+ name
67+
68+
69+-- COUNT VARIABLE APPEARANCES
70+
71+count :: Opt.Expr -> Map.Map String Int
72+count expression =
73+ let
74+ count2 a b =
75+ Map.unionWith (+) (count a) (count b)
76+
77+ countMany xs =
78+ Map.unionsWith (+) (map count xs)
79+ in
80+ case expression of
81+ Literal _ ->
82+ Map.empty
83+
84+ Var (Var.Canonical home name) ->
85+ case home of
86+ Var.Local ->
87+ Map.singleton name 1
88+
89+ Var.BuiltIn ->
90+ Map.empty
91+
92+ Var.Module _ ->
93+ Map.empty
94+
95+ Var.TopLevel _ ->
96+ Map.empty
97+
98+ Range lo hi ->
99+ count2 lo hi
100+
101+ ExplicitList exprs ->
102+ countMany exprs
103+
104+ Binop _op left right ->
105+ count2 left right
106+
107+ Function args body ->
108+ deleteBatch args (count body)
109+
110+ Call func args ->
111+ Map.unionWith (+) (count func) (countMany args)
112+
113+ TailCall _name _argNames args ->
114+ countMany args
115+
116+ If branches finally ->
117+ Map.unionsWith (+) (count finally : map (uncurry count2) branches)
118+
119+ Let defs expr ->
120+ let
121+ countDef def =
122+ case def of
123+ Opt.Def _ name body ->
124+ Map.delete name (count body)
125+
126+ Opt.TailDef _ name argNames body ->
127+ deleteBatch (name:argNames) (count body)
128+
129+ exprCount =
130+ deleteBatch (map getDefName defs) (count expr)
131+ in
132+ Map.unionsWith (+) (exprCount : map countDef defs)
133+
134+ Case _ decider jumps ->
135+ let
136+ countDecider dcdr =
137+ case dcdr of
138+ Leaf (Inline expr) ->
139+ count expr
140+
141+ Leaf (Jump _) ->
142+ Map.empty
143+
144+ Chain _ success failure ->
145+ Map.unionWith (+) (countDecider success) (countDecider failure)
146+
147+ FanOut _path tests fallback ->
148+ Map.unionsWith (+) (map countDecider (fallback : map snd tests))
149+ in
150+ Map.unionsWith (+) (countDecider decider : map (count . snd) jumps)
151+
152+ Data _tag values ->
153+ countMany values
154+
155+ DataAccess root _index ->
156+ count root
157+
158+ Access record _field ->
159+ count record
160+
161+ Update record fields ->
162+ Map.unionWith (+) (count record) (countMany (map snd fields))
163+
164+ Record fields ->
165+ countMany (map snd fields)
166+
167+ Port impl ->
168+ case impl of
169+ In _ _ ->
170+ Map.empty
171+
172+ Out _name expr _tipe ->
173+ count expr
174+
175+ Task _name expr _tipe ->
176+ count expr
177+
178+ GLShader _ _ _ ->
179+ Map.empty
180+
181+ Crash _ _ ->
182+ Map.empty
183+
184+
185+-- REPLACE
186+
187+replace :: Map.Map String Opt.Expr -> Opt.Expr -> Opt.Expr
188+replace substitutions expression =
189+ let
190+ go = replace substitutions
191+ in
192+ case expression of
193+ Literal _ ->
194+ expression
195+
196+ Var (Var.Canonical Var.Local name) ->
197+ maybe expression id (Map.lookup name substitutions)
198+
199+ Var _ ->
200+ expression
201+
202+ Range lo hi ->
203+ Range (go lo) (go hi)
204+
205+ ExplicitList exprs ->
206+ ExplicitList (map go exprs)
207+
208+ Binop op left right ->
209+ Binop op (go left) (go right)
210+
211+ Function args body ->
212+ Function args (replace (deleteBatch args substitutions) body)
213+
214+ Call func args ->
215+ Call (go func) (map go args)
216+
217+ TailCall name argNames args ->
218+ TailCall name argNames (map go args)
219+
220+ If branches finally ->
221+ If (map (go *** go) branches) (go finally)
222+
223+ Let defs expr ->
224+ let
225+ replaceDef def =
226+ case def of
227+ Opt.Def facts name body ->
228+ Opt.Def facts name
229+ (replace (Map.delete name substitutions) body)
230+
231+ Opt.TailDef facts name argNames body ->
232+ Opt.TailDef facts name argNames
233+ (replace (deleteBatch (name:argNames) substitutions) body)
234+
235+ boundNames =
236+ map getDefName defs
237+ in
238+ Let
239+ (map replaceDef defs)
240+ (replace (deleteBatch boundNames substitutions) expr)
241+
242+ Case exprName decider jumps ->
243+ let
244+ goDecider dcdr =
245+ case dcdr of
246+ Leaf (Inline expr) ->
247+ Leaf (Inline (go expr))
248+
249+ Leaf (Jump _target) ->
250+ dcdr
251+
252+ Chain chain success failure ->
253+ Chain chain (goDecider success) (goDecider failure)
254+
255+ FanOut path tests fallback ->
256+ FanOut path (map (second goDecider) tests) (goDecider fallback)
257+ in
258+ Case exprName (goDecider decider) (map (second go) jumps)
259+
260+ Data tag values ->
261+ Data tag (map go values)
262+
263+ DataAccess root index ->
264+ DataAccess (go root) index
265+
266+ Access record field ->
267+ Access (go record) field
268+
269+ Update record fields ->
270+ Update (go record) (map (second go) fields)
271+
272+ Record fields ->
273+ Record (map (second go) fields)
274+
275+ Port impl ->
276+ Port $
277+ case impl of
278+ In _ _ ->
279+ impl
280+
281+ Out name expr tipe ->
282+ Out name (go expr) tipe
283+
284+ Task name expr tipe ->
285+ Task name (go expr) tipe
286+
287+ GLShader _ _ _ ->
288+ expression
289+
290+ Crash _ _ ->
291+ expression
src/Optimize/Patterns.hs+152−67
@@ -8,88 +8,166 @@ import qualified Data.Maybe as Maybe
88
99import qualified AST.Expression.Optimized as Opt
1010import qualified AST.Pattern as P
11−import Elm.Utils ((|>))
1211import qualified Optimize.Environment as Env
1312import qualified Optimize.Patterns.DecisionTree as DT
1413import qualified Reporting.Annotation as A
1514import qualified Reporting.Region as R
1615
1716
17+
18+-- OPTIMIZE A CASE EXPRESSION
19+
20+
1821optimize
1922 :: DT.VariantDict
2023 -> R.Region
21− -> Opt.Expr
24+ -> String
2225 -> [(P.CanonicalPattern, Opt.Expr)]
2326 -> Env.Optimizer Opt.Expr
24−optimize variantDict region optExpr optBranches =
25− let
26− indexify index (pattern, branch) =
27− ( (pattern, index)
28− , (index, branch)
29− )
30− in
31− do crashBranches <-
32− case last optBranches of
33− (A.A ann P.Anything, Opt.Call (Opt.Crash _ _) [arg]) ->
34− do name <- Env.freshName
35− let newPattern = A.A ann (P.Var name)
36− let newCall = Opt.Call (Opt.Crash region (Just name)) [arg]
37− return (init optBranches ++ [(newPattern, newCall)])
38−
39− _ ->
40− return optBranches
41−
42− let (patterns, indexedBranches) =
43− unzip (zipWith indexify [0..] crashBranches)
44−
45− let decisionTree =
46− DT.compile variantDict patterns
47−
48− tempName <- Env.freshName
49−
50− return $
51− Opt.Let
52− [ Opt.Def Opt.dummyFacts tempName optExpr ]
53− (inlinedCase tempName decisionTree indexedBranches)
54−
55−
56−inlinedCase
57− :: String
58− -> DT.DecisionTree Int
59− -> [(Int, Opt.Expr)]
60− -> Opt.Expr
61−inlinedCase name decisionTree jumpTargets =
27+optimize variantDict region exprName optBranches =
28+ do crashBranches <-
29+ mapM (tagCrashBranches region) optBranches
30+
31+ let (patterns, indexedBranches) =
32+ unzip (zipWith indexify [0..] crashBranches)
33+
34+ let decisionTree =
35+ DT.compile variantDict patterns
36+
37+ return (treeToExpr exprName decisionTree indexedBranches)
38+
39+
40+tagCrashBranches
41+ :: R.Region
42+ -> (P.CanonicalPattern, Opt.Expr)
43+ -> Env.Optimizer (P.CanonicalPattern, Opt.Expr)
44+tagCrashBranches region branch@(pattern@(A.A pr _), expr) =
45+ case expr of
46+ Opt.Call (Opt.Crash _ _) [arg] ->
47+ do name <- Env.freshName
48+ return
49+ ( A.A pr (P.Alias name pattern)
50+ , Opt.Call (Opt.Crash region (Just name)) [arg]
51+ )
52+
53+ _ ->
54+ return branch
55+
56+
57+indexify :: Int -> (a,b) -> ((a,Int), (Int,b))
58+indexify index (pattern, branch) =
59+ ( (pattern, index)
60+ , (index, branch)
61+ )
62+
63+
64+
65+-- CONVERT A TREE TO AN EXPRESSION
66+
67+
68+treeToExpr :: String -> DT.DecisionTree -> [(Int, Opt.Expr)] -> Opt.Expr
69+treeToExpr name decisionTree allJumps =
6270 let
71+ decider =
72+ treeToDecider decisionTree
73+
6374 targetCounts =
64− countTargets decisionTree
75+ countTargets decider
6576
66− (decisions, sharedBranches) =
67− unzip (map (toDecisions targetCounts) jumpTargets)
77+ (choices, maybeJumps) =
78+ unzip (map (createChoices targetCounts) allJumps)
6879 in
6980 Opt.Case
7081 name
71− (inlineDecisions (Map.fromList decisions) decisionTree)
72− (Maybe.catMaybes sharedBranches)
82+ (insertChoices (Map.fromList choices) decider)
83+ (Maybe.catMaybes maybeJumps)
7384
7485
75−countTargets :: DT.DecisionTree Int -> Map.Map Int Int
76−countTargets decisionTree =
77− case decisionTree of
78− DT.Decision _ edges fallback ->
79− map snd edges
80− |> maybe id (:) fallback
81− |> map countTargets
82− |> Map.unionsWith (+)
8386
87+-- TREE TO DECIDER
88+--
89+-- Decision trees may have some redundancies, so we convert them to a Decider
90+-- which has special constructs to avoid code duplication when possible.
91+
92+
93+treeToDecider :: DT.DecisionTree -> Opt.Decider Int
94+treeToDecider tree =
95+ case tree of
8496 DT.Match target ->
97+ Opt.Leaf target
98+
99+ -- zero options
100+ DT.Decision _ [] Nothing ->
101+ error "compiler bug, somehow created an empty decision tree"
102+
103+ -- one option
104+ DT.Decision _ [(_, subTree)] Nothing ->
105+ treeToDecider subTree
106+
107+ DT.Decision _ [] (Just subTree) ->
108+ treeToDecider subTree
109+
110+ -- two options
111+ DT.Decision path [(test, successTree)] (Just failureTree) ->
112+ toChain path test successTree failureTree
113+
114+ DT.Decision path [(test, successTree), (_, failureTree)] Nothing ->
115+ toChain path test successTree failureTree
116+
117+ -- many options
118+ DT.Decision path edges Nothing ->
119+ let
120+ (necessaryTests, fallback) =
121+ (init edges, snd (last edges))
122+ in
123+ Opt.FanOut
124+ path
125+ (map (second treeToDecider) necessaryTests)
126+ (treeToDecider fallback)
127+
128+ DT.Decision path edges (Just fallback) ->
129+ Opt.FanOut path (map (second treeToDecider) edges) (treeToDecider fallback)
130+
131+
132+toChain :: DT.Path -> DT.Test -> DT.DecisionTree -> DT.DecisionTree -> Opt.Decider Int
133+toChain path test successTree failureTree =
134+ let
135+ failure =
136+ treeToDecider failureTree
137+ in
138+ case treeToDecider successTree of
139+ Opt.Chain testChain success subFailure | failure == subFailure ->
140+ Opt.Chain ((path, test) : testChain) success failure
141+
142+ success ->
143+ Opt.Chain [(path, test)] success failure
144+
145+
146+
147+-- INSERT CHOICES
148+--
149+-- If a target appears exactly once in a Decider, the corresponding expression
150+-- can be inlined. Whether things are inlined or jumps is called a "choice".
151+
152+
153+countTargets :: Opt.Decider Int -> Map.Map Int Int
154+countTargets decisionTree =
155+ case decisionTree of
156+ Opt.Leaf target ->
85157 Map.singleton target 1
86158
159+ Opt.Chain _ success failure ->
160+ Map.unionWith (+) (countTargets success) (countTargets failure)
87161
88−toDecisions
162+ Opt.FanOut _ tests fallback ->
163+ Map.unionsWith (+) (map countTargets (fallback : map snd tests))
164+
165+
166+createChoices
89167 :: Map.Map Int Int
90168 -> (Int, Opt.Expr)
91− -> ( (Int, Opt.Decision), Maybe (Int, Opt.Expr) )
92−toDecisions targetCounts (target, branch) =
169+ -> ( (Int, Opt.Choice), Maybe (Int, Opt.Expr) )
170+createChoices targetCounts (target, branch) =
93171 if targetCounts ! target == 1 then
94172 ( (target, Opt.Inline branch)
95173 , Nothing
@@ -101,18 +179,25 @@ toDecisions targetCounts (target, branch) =
101179 )
102180
103181
104−inlineDecisions
105− :: Map.Map Int Opt.Decision
106− -> DT.DecisionTree Int
107− -> DT.DecisionTree Opt.Decision
108−inlineDecisions jumpDict decisionTree =
182+insertChoices
183+ :: Map.Map Int Opt.Choice
184+ -> Opt.Decider Int
185+ -> Opt.Decider Opt.Choice
186+insertChoices choiceDict decider =
109187 let
110188 go =
111− inlineDecisions jumpDict
189+ insertChoices choiceDict
112190 in
113− case decisionTree of
114− DT.Decision test edges fallback ->
115− DT.Decision test (map (second go) edges) (fmap go fallback)
191+ case decider of
192+ Opt.Leaf target ->
193+ Opt.Leaf (choiceDict ! target)
194+
195+ Opt.Chain testChain success failure ->
196+ Opt.Chain testChain (go success) (go failure)
197+
198+ Opt.FanOut path tests fallback ->
199+ Opt.FanOut path (map (second go) tests) (go fallback)
200+
201+
202+
116203
117− DT.Match target ->
118− DT.Match (jumpDict ! target)
src/Optimize/Patterns/DecisionTree.hs+7−7
@@ -38,7 +38,7 @@ If 2 or more leaves point to the same label, we need to do some tricks in JS to
3838make that work nicely. When is JS getting goto?! ;) That is outside the scope
3939of this module though.
4040-}
41−compile :: VariantDict -> [(CPattern, Int)] -> DecisionTree Int
41+compile :: VariantDict -> [(CPattern, Int)] -> DecisionTree
4242compile variantDict rawBranches =
4343 let
4444 format (pattern, index) =
@@ -66,12 +66,12 @@ type VariantDict =
6666
6767-- DECISION TREES
6868
69−data DecisionTree a
70− = Match a
69+data DecisionTree
70+ = Match Int
7171 | Decision
7272 { _test :: Path
73− , _edges :: [(Test, DecisionTree a)]
74− , _default :: Maybe (DecisionTree a)
73+ , _edges :: [(Test, DecisionTree)]
74+ , _default :: Maybe DecisionTree
7575 }
7676 deriving (Eq)
7777
@@ -125,7 +125,7 @@ data Branch =
125125 }
126126
127127
128−toDecisionTree :: VariantDict -> [Branch] -> DecisionTree Int
128+toDecisionTree :: VariantDict -> [Branch] -> DecisionTree
129129toDecisionTree variantDict rawBranches =
130130 let
131131 branches =
@@ -461,4 +461,4 @@ smallBranchingFactor variantDict branches path =
461461 (edges, fallback) =
462462 gatherEdges variantDict branches path
463463 in
464− length edges + (if null fallback then 0 else 1)
464+ length edges + (if null fallback then 0 else 1)