1{-# OPTIONS_GHC -Wall #-}
2{-# LANGUAGE OverloadedStrings #-}
3module Optimize (optimize) where
5import Control.Arrow (second)
6import qualified Control.Monad as M
7import Data.Monoid ((<>))
8import qualified Data.Map as Map
9import qualified Data.Set as Set
10import qualified Data.Text as Text
11import Data.Text (Text)
13import qualified AST.Expression.Canonical as Can
14import qualified AST.Expression.Optimized as Opt
15import qualified AST.Module.Name as ModuleName
16import qualified AST.Pattern as P
17import qualified AST.Variable as Var
18import qualified Optimize.Case as Case
19import qualified Optimize.DecisionTree as DT
20import qualified Optimize.Environment as Env
21import qualified Optimize.Inline as Inline
22import qualified Reporting.Annotation as A
23import qualified Reporting.Region as R
30optimize :: DT.VariantDict -> ModuleName.Canonical -> [Can.Def] -> [(Text, Opt.Decl)]
31optimize variantDict home defs =
32 concat $ zipWith (optimizeDecl variantDict home) [1 .. length defs] defs
39optimizeDecl :: DT.VariantDict -> ModuleName.Canonical -> Int -> Can.Def -> [(Text, Opt.Decl)]
40optimizeDecl variantDict home index (Can.Def _ pattern@(A.A _ ptrn) expression _) =
43 Can.collectLambdas expression
45 case (ptrn, rawArgs) of
47 (:[]) $ Env.run variantDict home $
48 optimizeNamedDef name rawArgs rawBody
52 name = "_" <> Text.pack (show index)
53 deps = Set.singleton (Var.Global home name)
55 Env.run variantDict home $
56 ((,) name . Opt.Def) <$> optimizeExpr Nothing rawBody
58 destruct (Opt.VarGlobal home name) pattern
60 decl : map (second (Opt.Decl deps Nothing)) destructors
63 error "bug manifesting in Optimize.optimizeDecl, please report"
70optimizeLetDef :: Can.Def -> Env.Optimizer [(Text, Opt.Def)]
71optimizeLetDef (Can.Def _ pattern@(A.A _ ptrn) expression _) =
74 Can.collectLambdas expression
76 case (ptrn, rawArgs) of
78 do def <- optimizeNamedDef name rawArgs rawBody
82 do name <- Env.freshName
83 body <- optimizeExpr Nothing rawBody
84 return $ (name, Opt.Def body) : destruct (Opt.VarLocal name) pattern
87 error "bug manifesting in Optimize.optimizeLetDef, please report"
90optimizeNamedDef :: Text -> [P.Canonical] -> Can.Expr -> Env.Optimizer (Text, Opt.Def)
91optimizeNamedDef name rawArgs rawBody =
94 do hasTailCall <- Env.getTailCall
95 body <- optimizeExpr Nothing rawBody
96 Env.setTailCall hasTailCall
97 return (name, Opt.Def body)
100 do htc <- Env.getTailCall
101 Env.setTailCall False
103 (args, body) <- optimizeFunction (Just name) rawArgs rawBody
105 isTailCall <- Env.getTailCall
110 Opt.TailDef args body
112 Opt.Def (Opt.Function args body)
115destruct :: Opt.Expr -> P.Canonical -> [(Text, Opt.Def)]
116destruct root pattern =
117 map (second Opt.Def) $
118 patternToSubstitutions root pattern
129 -> Env.Optimizer ([Text], Opt.Expr)
130optimizeFunction maybeName patterns givenBody =
131 do (argNames, body) <- M.foldM depatternArgs ([], givenBody) (reverse patterns)
132 optBody <- optimizeExpr (makeContext argNames =<< maybeName) body
133 return (argNames, optBody)
136makeContext :: [Text] -> Text -> Context
137makeContext argNames name =
138 Just (name, length argNames, argNames)
141depatternArgs :: ([Text], Can.Expr) -> P.Canonical -> Env.Optimizer ([Text], Can.Expr)
142depatternArgs (names, rawExpr) ptrn =
143 do (name, expr) <- depattern ptrn rawExpr
144 return (name : names, expr)
148-- TURN A PATTERN INTO A BUNCH OF SUBSTITUTIONS
151patternToSubstitutions :: Opt.Expr -> P.Canonical -> [(Text, Opt.Expr)]
152patternToSubstitutions expr (A.A _ pattern) =
161 map (\name -> (name, Opt.Access expr name)) fields
166 P.Alias alias realPattern ->
167 (alias, expr) : patternToSubstitutions expr realPattern
170 concat (zipWith (patternToSubstitutions . Opt.CtorAccess expr) [0..] patterns)
174-- OPTIMIZE EXPRESSIONS
177type Context = Maybe (Text, Int, [Text])
180{-| This optimizer detects tail calls. The `Context` holds a function name,
181function arity, and list of argument names. If a tail call is possible, the
182`Context` will be filled in and we check any function we run into. If we go
183down a route that cannot possibly have tail calls, we remove the context.
185optimizeExpr :: Context -> Can.Expr -> Env.Optimizer Opt.Expr
186optimizeExpr context annExpr@(A.A region expression) =
190 -- this *might* find a tail call, use this when something is a potential
195 -- this will never find tail calls, use this when you are working with
196 -- expressions that cannot be turned into tail calls
200 pure (Opt.Literal lit)
203 optimizeVariable region var
207 <$> traverse justConvert elements
209 Can.Binop op leftExpr rightExpr ->
210 optimizeBinop context region op leftExpr rightExpr
215 Can.collectLambdas annExpr
217 uncurry Opt.Function <$> optimizeFunction Nothing patterns body
222 Can.collectApps annExpr
224 case getTailCallInfo context func (length args) of
225 Just (name, argNames) ->
226 do optArgs <- traverse justConvert args
228 return (Opt.TailCall name argNames optArgs)
231 do hasTailCall <- Env.getTailCall
232 optFunc <- justConvert func
233 optArgs <- traverse justConvert args
234 Env.setTailCall hasTailCall
235 return (Opt.Call optFunc optArgs)
237 Can.If branches finally ->
239 crawlBranch (cond,branch) =
240 (,) <$> justConvert cond <*> keepLooking branch
243 <$> traverse crawlBranch branches
244 <*> keepLooking finally
247 do optDefs <- concat <$> traverse optimizeLetDef defs
248 Opt.Let optDefs <$> keepLooking body
250 Can.Case expr branches ->
251 do optExpr <- optimizeExpr Nothing expr
252 variantDict <- Env.getVariantDict
254 name <- Env.freshName
255 optBranches <- traverse (optimizeBranch context region name) branches
256 let optCase = Case.optimize variantDict name optBranches
257 return $ Opt.Let [ (name, Opt.Def optExpr) ] optCase
259 Can.Ctor (Var.Canonical _ name) exprs ->
260 Opt.Ctor name <$> traverse justConvert exprs
262 Can.Access record field ->
264 <$> justConvert record
267 Can.Update record fields ->
269 <$> justConvert record
270 <*> traverse (traverse justConvert) fields
274 <$> traverse (traverse justConvert) fields
276 Can.Cmd moduleName manager ->
277 Opt.Cmd moduleName <$> Env.registerEffects manager
279 Can.Sub moduleName manager ->
280 Opt.Sub moduleName <$> Env.registerEffects manager
282 Can.OutgoingPort name tipe ->
283 pure (Opt.OutgoingPort name tipe)
285 Can.IncomingPort name tipe ->
286 pure (Opt.IncomingPort name tipe)
288 Can.Program kind expr ->
289 Opt.Program kind <$> justConvert expr
292 error "save_the_environment should never make it to optimization phase"
294 Can.GLShader _ src _ ->
295 pure (Opt.GLShader src)
302optimizeVariable :: R.Region -> Var.Canonical -> Env.Optimizer Opt.Expr
303optimizeVariable region (Var.Canonical home name) =
306 pure (Opt.VarLocal name)
309 pure (Opt.VarLocal name)
311 Var.TopLevel modul ->
312 do Env.register (Var.Global modul name)
313 pure (Opt.VarGlobal modul name)
316 do Env.register (Var.Global modul name)
317 if name == "crash" && modul == ModuleName.inCore "Debug"
319 do here <- Env.getHome
320 pure (Opt.Crash here region Nothing)
322 pure (Opt.VarGlobal modul name)
326-- OPTIMIZE CASE BRANCHES
333 -> (P.Canonical, Can.Expr)
334 -> Env.Optimizer (P.Canonical, Opt.Expr)
335optimizeBranch context region exprName (rawPattern, canBranch) =
336 do optBranch <- optimizeExpr context canBranch
337 (pattern, branch) <- tagCrashBranch region rawPattern optBranch
339 let substitutions = Map.fromList $
340 patternToSubstitutions (Opt.VarLocal exprName) pattern
342 (,) pattern <$> Inline.inline substitutions branch
349 -> Env.Optimizer (P.Canonical, Opt.Expr)
350tagCrashBranch region pattern@(A.A pr _) expr =
352 Opt.Call (Opt.Crash home _ _) [arg] ->
353 do name <- Env.freshName
355 ( A.A pr (P.Alias name pattern)
356 , Opt.Call (Opt.Crash home region (Just (Opt.VarLocal name))) [arg]
360 return (pattern, expr)
367getTailCallInfo :: Context -> Can.Expr -> Int -> Maybe (Text, [Text])
368getTailCallInfo context (A.A _ function) arity =
369 case (context, function) of
370 ( Just (ctxName, ctxArity, argNames), Can.Var (Var.Canonical home name) ) ->
372 if name == ctxName && arity == ctxArity && Var.isLocalHome home then
373 Just (name, argNames)
384-- given a pattern and an expression, push the actual pattern matching into the
385-- expression as a case-expression if necessary. The pattern compiler will work
389depattern :: P.Canonical -> Can.Expr -> Env.Optimizer (Text, Can.Expr)
390depattern canPattern@(A.A _ pattern) expr =
393 A.A (error "the annotation added in 'depattern' should not be observed!")
395 caseExpr e branches =
396 ann (Can.Case e branches)
399 ann (Can.Var (Var.local name))
402 do name <- Env.freshName
405 , caseExpr (var name) [ (canPattern, expr) ]
419 do name <- Env.freshName
439 -> Env.Optimizer Opt.Expr
440optimizeBinop context region op leftExpr rightExpr =
443 binop = ann (Can.Binop op leftExpr rightExpr)
445 if op == forwardApply then
447 optimizeExpr context (collect id left forwardApply binop)
449 else if op == backwardApply then
451 optimizeExpr context (collect id right backwardApply binop)
453 else if op == forwardCompose then
455 do var <- Env.freshName
456 let makeRoot func = ann (Can.App func (ann (Can.Var (Var.local var))))
457 let body = collect makeRoot left forwardCompose binop
458 optimizeExpr context (ann (Can.Lambda (ann (P.Var var)) body))
460 else if op == backwardCompose then
462 do var <- Env.freshName
463 let makeRoot func = ann (Can.App func (ann (Can.Var (Var.local var))))
464 let body = collect makeRoot right backwardCompose binop
465 optimizeExpr context (ann (Can.Lambda (ann (P.Var var)) body))
467 else if op == cons then
469 optimizeExpr context (ann (Can.Ctor cons [ leftExpr, rightExpr ]))
474 <$> optimizeExpr Nothing leftExpr
475 <*> optimizeExpr Nothing rightExpr
478-- left-associative ((x |> f) |> g)
479forwardApply :: Var.Canonical
481 Var.inCore "Basics" "|>"
484-- right-associative (g <| (f <| x))
485backwardApply :: Var.Canonical
487 Var.inCore "Basics" "<|"
490-- left-associative ((f >> g) >> h)
491forwardCompose :: Var.Canonical
493 Var.inCore "Basics" ">>"
496-- right-associative (h << (g << f))
497backwardCompose :: Var.Canonical
499 Var.inCore "Basics" "<<"
504 Var.Canonical Var.BuiltIn "::"
508 Can.Expr -> Can.Expr -> (Can.Expr, Can.Expr)
521collect :: (Can.Expr -> Can.Expr) -> Assoc -> Var.Canonical -> Can.Expr -> Can.Expr
522collect makeRoot assoc desiredOp annExpr@(A.A ann expr) =
524 Can.Binop op leftExpr rightExpr | op == desiredOp ->
526 (func, arg) = assoc leftExpr rightExpr
528 A.A ann (Can.App func (collect makeRoot assoc desiredOp arg))
534makeOptBinop :: Var.Canonical -> Opt.Expr -> Opt.Expr -> Opt.Expr
535makeOptBinop (Var.Canonical home op) leftExpr rightExpr =
538 error "bug manifesting in Optimize.makeOptBinop"
541 error "bug manifesting in Optimize.makeOptBinop"
544 Opt.Binop name op leftExpr rightExpr
547 Opt.Binop name op leftExpr rightExpr
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