1{-# OPTIONS_GHC -Wall #-}
2module Optimize.Case (optimize) where
4import Control.Arrow (second)
5import qualified Data.Map as Map
7import qualified Data.Maybe as Maybe
10import qualified AST.Expression.Optimized as Opt
11import qualified AST.Pattern as P
12import qualified Optimize.DecisionTree as DT
16-- OPTIMIZE A CASE EXPRESSION
19optimize :: DT.VariantDict -> Text -> [(P.Canonical, Opt.Expr)] -> Opt.Expr
20optimize variantDict exprName optBranches =
22 (patterns, indexedBranches) =
23 unzip (zipWith indexify [0..] optBranches)
26 DT.compile variantDict patterns
28 treeToExpr exprName decisionTree indexedBranches
31indexify :: Int -> (a,b) -> ((a,Int), (Int,b))
32indexify index (pattern, branch) =
39-- CONVERT A TREE TO AN EXPRESSION
42treeToExpr :: Text -> DT.DecisionTree -> [(Int, Opt.Expr)] -> Opt.Expr
43treeToExpr name decisionTree allJumps =
46 treeToDecider decisionTree
51 (choices, maybeJumps) =
52 unzip (map (createChoices targetCounts) allJumps)
56 (insertChoices (Map.fromList choices) decider)
57 (Maybe.catMaybes maybeJumps)
63-- Decision trees may have some redundancies, so we convert them to a Decider
64-- which has special constructs to avoid code duplication when possible.
67treeToDecider :: DT.DecisionTree -> Opt.Decider Int
74 DT.Decision _ [] Nothing ->
75 error "compiler bug, somehow created an empty decision tree"
78 DT.Decision _ [(_, subTree)] Nothing ->
81 DT.Decision _ [] (Just subTree) ->
85 DT.Decision path [(test, successTree)] (Just failureTree) ->
86 toChain path test successTree failureTree
88 DT.Decision path [(test, successTree), (_, failureTree)] Nothing ->
89 toChain path test successTree failureTree
92 DT.Decision path edges Nothing ->
94 (necessaryTests, fallback) =
95 (init edges, snd (last edges))
99 (map (second treeToDecider) necessaryTests)
100 (treeToDecider fallback)
102 DT.Decision path edges (Just fallback) ->
103 Opt.FanOut path (map (second treeToDecider) edges) (treeToDecider fallback)
106toChain :: DT.Path -> DT.Test -> DT.DecisionTree -> DT.DecisionTree -> Opt.Decider Int
107toChain path test successTree failureTree =
110 treeToDecider failureTree
112 case treeToDecider successTree of
113 Opt.Chain testChain success subFailure | failure == subFailure ->
114 Opt.Chain ((path, test) : testChain) success failure
117 Opt.Chain [(path, test)] success failure
123-- If a target appears exactly once in a Decider, the corresponding expression
124-- can be inlined. Whether things are inlined or jumps is called a "choice".
127countTargets :: Opt.Decider Int -> Map.Map Int Int
128countTargets decisionTree =
131 Map.singleton target 1
133 Opt.Chain _ success failure ->
134 Map.unionWith (+) (countTargets success) (countTargets failure)
136 Opt.FanOut _ tests fallback ->
137 Map.unionsWith (+) (map countTargets (fallback : map snd tests))
143 -> ( (Int, Opt.Choice), Maybe (Int, Opt.Expr) )
144createChoices targetCounts (target, branch) =
145 if targetCounts ! target == 1 then
146 ( (target, Opt.Inline branch)
151 ( (target, Opt.Jump target)
152 , Just (target, branch)
157 :: Map.Map Int Opt.Choice
159 -> Opt.Decider Opt.Choice
160insertChoices choiceDict decider =
163 insertChoices choiceDict
167 Opt.Leaf (choiceDict ! target)
169 Opt.Chain testChain success failure ->
170 Opt.Chain testChain (go success) (go failure)
172 Opt.FanOut path tests fallback ->
173 Opt.FanOut path (map (second go) tests) (go fallback)
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list