@@ -8,88 +8,166 @@ import qualified Data.Maybe as Maybe
9import qualified AST.Expression.Optimized as Opt
9import qualified AST.Expression.Optimized as Opt
10import qualified AST.Pattern as P
10import qualified AST.Pattern as P
11−import Elm.Utils ((|>))
12import qualified Optimize.Environment as Env
11import qualified Optimize.Environment as Env
13import qualified Optimize.Patterns.DecisionTree as DT
12import qualified Optimize.Patterns.DecisionTree as DT
14import qualified Reporting.Annotation as A
13import qualified Reporting.Annotation as A
15import qualified Reporting.Region as R
14import qualified Reporting.Region as R
18+-- OPTIMIZE A CASE EXPRESSION
19 :: DT.VariantDict
22 :: DT.VariantDict
20 -> R.Region
23 -> R.Region
21− -> Opt.Expr
24+ -> String
22 -> [(P.CanonicalPattern, Opt.Expr)]
25 -> [(P.CanonicalPattern, Opt.Expr)]
23 -> Env.Optimizer Opt.Expr
26 -> Env.Optimizer Opt.Expr
24−optimize variantDict region optExpr optBranches =
27+optimize variantDict region exprName optBranches =
26− indexify index (pattern, branch) =
31− do crashBranches <-
28+ 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)])
40− return optBranches
29+ mapM (tagCrashBranches region) optBranches
42− let (patterns, indexedBranches) =
31+ let (patterns, indexedBranches) =
43− unzip (zipWith indexify [0..] crashBranches)
32+ unzip (zipWith indexify [0..] crashBranches)
45− let decisionTree =
34+ let decisionTree =
46− DT.compile variantDict patterns
35+ DT.compile variantDict patterns
37+ return (treeToExpr exprName decisionTree indexedBranches)
42+ -> (P.CanonicalPattern, Opt.Expr)
43+ -> Env.Optimizer (P.CanonicalPattern, Opt.Expr)
44+tagCrashBranches region branch@(pattern@(A.A pr _), expr) =
46+ Opt.Call (Opt.Crash _ _) [arg] ->
48− tempName <- Env.freshName
47+ do name <- Env.freshName
50− return $
49+ ( A.A pr (P.Alias name pattern)
51− Opt.Let
50+ , Opt.Call (Opt.Crash region (Just name)) [arg]
52− [ Opt.Def Opt.dummyFacts tempName optExpr ]
51+ )
53− (inlinedCase tempName decisionTree indexedBranches)
52+
57+indexify :: Int -> (a,b) -> ((a,Int), (Int,b))
58+indexify index (pattern, branch) =
65+-- CONVERT A TREE TO AN EXPRESSION
58− -> DT.DecisionTree Int
68+treeToExpr :: String -> DT.DecisionTree -> [(Int, Opt.Expr)] -> Opt.Expr
61−inlinedCase name decisionTree jumpTargets =
69+treeToExpr name decisionTree allJumps =
72+ treeToDecider decisionTree
63 targetCounts =
74 targetCounts =
64− countTargets decisionTree
75+ countTargets decider
66− (decisions, sharedBranches) =
77+ (choices, maybeJumps) =
67− unzip (map (toDecisions targetCounts) jumpTargets)
78+ unzip (map (createChoices targetCounts) allJumps)
71− (inlineDecisions (Map.fromList decisions) decisionTree)
82+ (insertChoices (Map.fromList choices) decider)
72− (Maybe.catMaybes sharedBranches)
83+ (Maybe.catMaybes maybeJumps)
75−countTargets :: DT.DecisionTree Int -> Map.Map Int Int
76−countTargets decisionTree =
78− DT.Decision _ edges fallback ->
80− |> maybe id (:) fallback
82− |> Map.unionsWith (+)
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.
93+treeToDecider :: DT.DecisionTree -> Opt.Decider Int
84 DT.Match target ->
96 DT.Match target ->
100+ DT.Decision _ [] Nothing ->
101+ error "compiler bug, somehow created an empty decision tree"
104+ DT.Decision _ [(_, subTree)] Nothing ->
105+ treeToDecider subTree
107+ DT.Decision _ [] (Just subTree) ->
108+ treeToDecider subTree
111+ DT.Decision path [(test, successTree)] (Just failureTree) ->
112+ toChain path test successTree failureTree
114+ DT.Decision path [(test, successTree), (_, failureTree)] Nothing ->
115+ toChain path test successTree failureTree
118+ DT.Decision path edges Nothing ->
120+ (necessaryTests, fallback) =
121+ (init edges, snd (last edges))
125+ (map (second treeToDecider) necessaryTests)
126+ (treeToDecider fallback)
128+ DT.Decision path edges (Just fallback) ->
129+ Opt.FanOut path (map (second treeToDecider) edges) (treeToDecider fallback)
132+toChain :: DT.Path -> DT.Test -> DT.DecisionTree -> DT.DecisionTree -> Opt.Decider Int
133+toChain path test successTree failureTree =
136+ treeToDecider failureTree
138+ case treeToDecider successTree of
139+ Opt.Chain testChain success subFailure | failure == subFailure ->
140+ Opt.Chain ((path, test) : testChain) success failure
143+ Opt.Chain [(path, test)] success failure
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".
153+countTargets :: Opt.Decider Int -> Map.Map Int Int
154+countTargets decisionTree =
155+ case decisionTree of
85 Map.singleton target 1
157 Map.singleton target 1
159+ Opt.Chain _ success failure ->
160+ Map.unionWith (+) (countTargets success) (countTargets failure)
88−toDecisions
162+ Opt.FanOut _ tests fallback ->
163+ Map.unionsWith (+) (map countTargets (fallback : map snd tests))
89 :: Map.Map Int Int
167 :: Map.Map Int Int
90 -> (Int, Opt.Expr)
168 -> (Int, Opt.Expr)
91− -> ( (Int, Opt.Decision), Maybe (Int, Opt.Expr) )
169+ -> ( (Int, Opt.Choice), Maybe (Int, Opt.Expr) )
92−toDecisions targetCounts (target, branch) =
170+createChoices targetCounts (target, branch) =
93 if targetCounts ! target == 1 then
171 if targetCounts ! target == 1 then
94 ( (target, Opt.Inline branch)
172 ( (target, Opt.Inline branch)
95 , Nothing
173 , Nothing
@@ -101,18 +179,25 @@ toDecisions targetCounts (target, branch) =
104−inlineDecisions
182+insertChoices
105− :: Map.Map Int Opt.Decision
183+ :: Map.Map Int Opt.Choice
106− -> DT.DecisionTree Int
184+ -> Opt.Decider Int
107− -> DT.DecisionTree Opt.Decision
185+ -> Opt.Decider Opt.Choice
108−inlineDecisions jumpDict decisionTree =
186+insertChoices choiceDict decider =
111− inlineDecisions jumpDict
189+ insertChoices choiceDict
113− case decisionTree of
191+ case decider of
114− DT.Decision test edges fallback ->
192+ Opt.Leaf target ->
193+ Opt.Leaf (choiceDict ! target)
195+ Opt.Chain testChain success failure ->
196+ Opt.Chain testChain (go success) (go failure)
198+ Opt.FanOut path tests fallback ->
115− DT.Decision test (map (second go) edges) (fmap go fallback)
199+ Opt.FanOut path (map (second go) tests) (go fallback)
118− DT.Match (jumpDict ! target)