@@ -8,117 +8,196 @@ 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 DT.Jump
68+treeToExpr :: String -> DT.DecisionTree -> [(Int, Opt.Expr)] -> Opt.Expr
61−inlinedCase name decisionTree jumpTargets =
69+treeToExpr name decisionTree allJumps =
63− substitutionDict =
71+ decider =
64− gatherSubstitutions decisionTree
72+ treeToDecider decisionTree
66− (jumps, sharedBranches) =
77+ (choices, maybeJumps) =
67− unzip (map (toJumps substitutionDict) jumpTargets)
78+ unzip (map (createChoices targetCounts) allJumps)
71− (inlineJumps (Map.fromList jumps) decisionTree)
82+ (insertChoices (Map.fromList choices) decider)
72− (Maybe.catMaybes sharedBranches)
83+ (Maybe.catMaybes maybeJumps)
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
100+ DT.Decision _ [] Nothing ->
101+ error "compiler bug, somehow created an empty decision tree"
75−gatherSubstitutions
103+ -- one option
76− :: DT.DecisionTree DT.Jump
104+ DT.Decision _ [(_, subTree)] Nothing ->
77− -> Map.Map Int [[(String, DT.Path)]]
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
78−gatherSubstitutions decisionTree =
154+countTargets decisionTree =
79 case decisionTree of
155 case decisionTree of
80− DT.Decision _ edges fallback ->
156+ Opt.Leaf target ->
81− map snd edges
157+ Map.singleton target 1
82− |> maybe id (:) fallback
158+
83− |> map gatherSubstitutions
159+ Opt.Chain _ success failure ->
84− |> Map.unionsWith (++)
160+ Map.unionWith (+) (countTargets success) (countTargets failure)
86− DT.Match (DT.Jump target substitutions) ->
162+ Opt.FanOut _ tests fallback ->
87− Map.singleton target [substitutions]
163+ Map.unionsWith (+) (map countTargets (fallback : map snd tests))
90−toJumps
166+createChoices
91− :: Map.Map Int [[(String, DT.Path)]]
167+ :: Map.Map Int Int
92 -> (Int, Opt.Expr)
168 -> (Int, Opt.Expr)
93− -> ( (Int, Opt.Jump), Maybe (Int, Opt.Branch) )
169+ -> ( (Int, Opt.Choice), Maybe (Int, Opt.Expr) )
94−toJumps substitutionDict (target, branch) =
170+createChoices targetCounts (target, branch) =
95− case substitutionDict ! target of
171+ if targetCounts ! target == 1 then
97− ( (target, Opt.Inline (Opt.Branch substitutions branch))
172+ ( (target, Opt.Inline branch)
98− , Nothing
173+ , Nothing
101− substitutions : _ ->
176+ else
102− ( (target, Opt.Jump target)
177+ ( (target, Opt.Jump target)
103− , Just (target, Opt.Branch substitutions branch)
178+ , Just (target, branch)
107− error "it is not possible for there to be no substitutions"
182+insertChoices
111− :: Map.Map Int Opt.Jump
183+ :: Map.Map Int Opt.Choice
112− -> DT.DecisionTree DT.Jump
184+ -> Opt.Decider Int
113− -> DT.DecisionTree Opt.Jump
185+ -> Opt.Decider Opt.Choice
114−inlineJumps jumpDict decisionTree =
186+insertChoices choiceDict decider =
117− inlineJumps jumpDict
189+ insertChoices choiceDict
119− case decisionTree of
191+ case decider of
120− 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 ->
121− DT.Decision test (map (second go) edges) (fmap go fallback)
199+ Opt.FanOut path (map (second go) tests) (go fallback)
123− DT.Match (DT.Jump target _) ->
124− DT.Match (jumpDict ! target)