1module Optimize (optimize) where
3import qualified Control.Monad as M
4import qualified Data.Map as Map
5import qualified Data.Traversable as T
7import qualified AST.Expression.General as Expr
8import qualified AST.Expression.Canonical as Can
9import qualified AST.Expression.Optimized as Opt
10import qualified AST.Module.Name as ModuleName
11import qualified AST.Pattern as P
12import qualified AST.Variable as Var
13import qualified Optimize.Case as Case
14import qualified Optimize.DecisionTree as DT
15import qualified Optimize.Environment as Env
16import qualified Optimize.Inline as Inline
17import qualified Reporting.Annotation as A
18import qualified Reporting.Region as R
25optimize :: DT.VariantDict -> ModuleName.Canonical -> [Can.Def] -> [Opt.Def]
26optimize variantDict home defs =
27 Env.run variantDict home (concat <$> mapM (optimizeDef True) defs)
34optimizeDef :: Bool -> Can.Def -> Env.Optimizer [Opt.Def]
35optimizeDef isRoot (Can.Def (Can.Facts deps) pattern expression _) =
38 Expr.collectLambdas expression
47 do home <- maybeGetHome
48 optimizeDefHelp home deps pattern args canBody
52 :: Maybe ModuleName.Canonical
57 -> Env.Optimizer [Opt.Def]
58optimizeDefHelp home deps pattern@(A.A _ ptrn) args rawBody =
64 (P.Var name, _ : _) ->
66 do htc <- Env.getTailCall
69 (args, body) <- optimizeFunction (Just name) args rawBody
71 isTailCall <- Env.getTailCall
76 [ Opt.TailDef facts name args body ]
79 [ Opt.Def facts name (Opt.Function args body) ]
83 do hasTailCall <- Env.getTailCall
84 body <- optimizeExpr Nothing rawBody
85 Env.setTailCall hasTailCall
86 return [ Opt.Def facts name body ]
90 do name <- Env.freshName
91 optBody <- optimizeExpr Nothing rawBody
92 let optDef = Opt.Def facts name optBody
93 return (optDef : patternToDefs home name pattern)
96 error "there should never be a function where the name is not a P.Var"
107 -> Env.Optimizer ([String], Opt.Expr)
108optimizeFunction maybeName patterns givenBody =
109 do (argNames, body) <- M.foldM depatternArgs ([], givenBody) (reverse patterns)
110 optBody <- optimizeExpr (makeContext argNames =<< maybeName) body
111 return (argNames, optBody)
114makeContext :: [String] -> String -> Context
115makeContext argNames name =
116 Just (name, length argNames, argNames)
119depatternArgs :: ([String], Can.Expr) -> P.Canonical -> Env.Optimizer ([String], Can.Expr)
120depatternArgs (names, rawExpr) ptrn =
121 do (name, expr) <- depattern ptrn rawExpr
122 return (name : names, expr)
125patternToDefs :: Maybe ModuleName.Canonical -> String -> P.Canonical -> [Opt.Def]
126patternToDefs home rootName pattern =
132 , Opt.Var (Var.local rootName)
136 ( [ Var.TopLevelVar moduleName rootName ]
137 , Opt.Var (Var.topLevel moduleName rootName)
140 toDef (name, accessExpr) =
141 Opt.Def (Opt.Facts home deps) name accessExpr
143 map toDef (patternToSubstitutions rootExpr pattern)
147-- TURN A PATTERN INTO A BUNCH OF SUBSTITUTIONS
150patternToSubstitutions :: Opt.Expr -> P.Canonical -> [(String, Opt.Expr)]
151patternToSubstitutions expr (A.A _ pattern) =
160 map (\name -> (name, Opt.Access expr name)) fields
165 P.Alias alias realPattern ->
166 (alias, expr) : patternToSubstitutions expr realPattern
169 concat (zipWith (patternToSubstitutions . Opt.DataAccess expr) [0..] patterns)
173-- OPTIMIZE EXPRESSIONS
176type Context = Maybe (String, Int, [String])
179{-| This optimizer detects tail calls. The `Context` holds a function name,
180function arity, and list of argument names. If a tail call is possible, the
181`Context` will be filled in and we check any function we run into. If we go
182down a route that cannot possibly have tail calls, we remove the context.
184optimizeExpr :: Context -> Can.Expr -> Env.Optimizer Opt.Expr
185optimizeExpr context annExpr@(A.A region expression) =
189 -- this *might* find a tail call, use this when something is a potential
194 -- this will never find tail calls, use this when you are working with
195 -- expressions that cannot be turned into tail calls
199 pure (Opt.Literal lit)
202 if name == Var.inCore ["Debug"] "crash" then
203 do home <- Env.getHome
204 pure (Opt.Crash home region Nothing)
209 Expr.ExplicitList elements ->
211 <$> T.traverse justConvert elements
213 Expr.Binop op leftExpr rightExpr ->
214 optimizeBinop context region op leftExpr rightExpr
219 Expr.collectLambdas annExpr
221 uncurry Opt.Function <$> optimizeFunction Nothing patterns body
226 Expr.collectApps annExpr
228 case isTailCall context func (length args) of
229 Just (name, argNames) ->
230 do optArgs <- T.traverse justConvert args
232 return (Opt.TailCall name argNames optArgs)
235 do hasTailCall <- Env.getTailCall
236 optFunc <- justConvert func
237 optArgs <- T.traverse justConvert args
238 Env.setTailCall hasTailCall
239 return (Opt.Call optFunc optArgs)
241 Expr.If branches finally ->
243 crawlBranch (cond,branch) =
244 (,) <$> justConvert cond <*> keepLooking branch
247 <$> T.traverse crawlBranch branches
248 <*> keepLooking finally
250 Expr.Let defs body ->
251 do optDefs <- concat <$> T.traverse (optimizeDef False) defs
252 Opt.Let optDefs <$> keepLooking body
254 Expr.Case expr branches ->
255 do optExpr <- optimizeExpr Nothing expr
256 variantDict <- Env.getVariantDict
258 name <- Env.freshName
259 optBranches <- T.traverse (optimizeBranch context region name) branches
260 let optCase = Case.optimize variantDict name optBranches
261 return $ Opt.Let [ Opt.Def Opt.dummyFacts name optExpr ] optCase
263 Expr.Data name exprs ->
264 Opt.Data name <$> T.traverse justConvert exprs
266 Expr.Access record field ->
268 <$> justConvert record
271 Expr.Update record fields ->
273 <$> justConvert record
274 <*> T.traverse (mapSnd justConvert) fields
276 Expr.Record fields ->
278 <$> T.traverse (mapSnd justConvert) fields
280 Expr.Cmd moduleName ->
281 pure (Opt.Cmd moduleName)
283 Expr.Sub moduleName ->
284 pure (Opt.Sub moduleName)
286 Expr.OutgoingPort name tipe ->
287 pure (Opt.OutgoingPort name tipe)
289 Expr.IncomingPort name tipe ->
290 pure (Opt.IncomingPort name tipe)
292 Expr.Program kind expr ->
293 Opt.Program kind <$> justConvert expr
296 error "save_the_environment should never make it to optimization phase"
298 Expr.GLShader uid src gltipe ->
299 pure (Opt.GLShader uid src gltipe)
302mapSnd :: M.Functor box => (a -> box b) -> (x, a) -> box (x, b)
308-- OPTIMIZE CASE BRANCHES
315 -> (P.Canonical, Can.Expr)
316 -> Env.Optimizer (P.Canonical, Opt.Expr)
317optimizeBranch context region exprName (rawPattern, canBranch) =
320 Opt.Var (Var.Canonical Var.Local exprName)
322 do optBranch <- optimizeExpr context canBranch
323 (pattern, branch) <- tagCrashBranch region rawPattern optBranch
326 Map.fromList (patternToSubstitutions root pattern)
328 (,) pattern <$> Inline.inline substitutions branch
335 -> Env.Optimizer (P.Canonical, Opt.Expr)
336tagCrashBranch region pattern@(A.A pr _) expr =
338 Opt.Call (Opt.Crash home _ _) [arg] ->
339 do name <- Env.freshName
341 ( A.A pr (P.Alias name pattern)
342 , Opt.Call (Opt.Crash home region (Just (Opt.Var (Var.local name)))) [arg]
346 return (pattern, expr)
353isTailCall :: Context -> Can.Expr -> Int -> Maybe (String, [String])
354isTailCall context (A.A _ function) arity =
355 case (context, function) of
356 ( Just (ctxName, ctxArity, argNames), Expr.Var (Var.Canonical home name) ) ->
358 if name == ctxName && arity == ctxArity && Var.isLocalHome home then
359 Just (name, argNames)
370-- given a pattern and an expression, push the actual pattern matching into the
371-- expression as a case-expression if necessary. The pattern compiler will work
375depattern :: P.Canonical -> Can.Expr -> Env.Optimizer (String, Can.Expr)
376depattern canPattern@(A.A _ pattern) expr =
379 A.A (error "the annotation added in 'depattern' should not be observed!")
381 caseExpr e branches =
382 ann (Expr.Case e branches)
385 ann (Expr.Var (Var.local name))
388 do name <- Env.freshName
391 , caseExpr (var name) [ (canPattern, expr) ]
405 do name <- Env.freshName
425 -> Env.Optimizer Opt.Expr
426optimizeBinop context region op leftExpr rightExpr =
429 binop = ann (Expr.Binop op leftExpr rightExpr)
431 if op == forwardApply then
433 optimizeExpr context (collect id left forwardApply binop)
435 else if op == backwardApply then
437 optimizeExpr context (collect id right backwardApply binop)
439 else if op == forwardCompose then
441 do var <- Env.freshName
442 let makeRoot func = ann (Expr.App func (ann (Expr.Var (Var.local var))))
443 let body = collect makeRoot left forwardCompose binop
444 optimizeExpr context (ann (Expr.Lambda (ann (P.Var var)) body))
446 else if op == backwardCompose then
448 do var <- Env.freshName
449 let makeRoot func = ann (Expr.App func (ann (Expr.Var (Var.local var))))
450 let body = collect makeRoot right backwardCompose binop
451 optimizeExpr context (ann (Expr.Lambda (ann (P.Var var)) body))
453 else if op == Var.Canonical Var.BuiltIn "::" then
455 optimizeExpr context (ann (Expr.Data "::" [ leftExpr, rightExpr ]))
460 <$> optimizeExpr Nothing leftExpr
461 <*> optimizeExpr Nothing rightExpr
464-- left-associative ((x |> f) |> g)
465forwardApply :: Var.Canonical
467 Var.inCore ["Basics"] "|>"
470-- right-associative (g <| (f <| x))
471backwardApply :: Var.Canonical
473 Var.inCore ["Basics"] "<|"
476-- left-associative ((f >> g) >> h)
477forwardCompose :: Var.Canonical
479 Var.inCore ["Basics"] ">>"
482-- right-associative (h << (g << f))
483backwardCompose :: Var.Canonical
485 Var.inCore ["Basics"] "<<"
489 Can.Expr -> Can.Expr -> (Can.Expr, Can.Expr)
502collect :: (Can.Expr -> Can.Expr) -> Assoc -> Var.Canonical -> Can.Expr -> Can.Expr
503collect makeRoot assoc desiredOp annExpr@(A.A ann expr) =
505 Expr.Binop op leftExpr rightExpr | op == desiredOp ->
507 (func, arg) = assoc leftExpr rightExpr
509 A.A ann (Expr.App func (collect makeRoot assoc desiredOp arg))
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