@@ -1,9 +1,487 @@
1−module Optimize where
1+module Optimize (optimize) where
3+import Control.Applicative
3−import qualified AST.Module as M
4+import qualified Control.Monad as M
4−import qualified Optimize.TailCalls as TC
5+import qualified Control.Monad.State as State
6+import qualified Data.Map as Map
7+import qualified Data.Traversable as T
9+import qualified AST.Expression.General as Expr
10+import qualified AST.Expression.Canonical as Can
11+import qualified AST.Expression.Optimized as Opt
12+import qualified AST.Module as Module
13+import qualified AST.Module.Name as ModuleName
14+import qualified AST.Pattern as P
15+import qualified AST.Variable as Var
16+import qualified Optimize.Cases as Cases
17+import qualified Reporting.Annotation as A
18+import qualified Reporting.Region as R
21+-- OPTIMIZE FOR TAIL CALLS
7−optimize :: M.CanonicalModule -> M.Optimized
23+optimize :: Module.CanonicalModule -> Module.Optimized
8−optimize =
24+optimize module_ =
30+ flattenLets [] (Module.program (Module.body module_))
33+ (concat <$> mapM (definition (Just home)) defs)
37+flattenLets :: [Can.Def] -> Can.Expr -> ([Can.Def], Can.Expr)
38+flattenLets defs annExpr@(A.A _ expr) =
41+ flattenLets (defs ++ ds) body
47+-- TRACKER TO THREAD FINDINGS THROUGH TRAVERSAL
54+ { _hasTailCall :: Bool
59+setTailCall :: Bool -> Optimizer ()
61+ do uid <- State.gets _uid
62+ State.put (Env bool uid)
65+mapSnd :: M.Functor box => (a -> box b) -> (x, a) -> box (x, b)
70+-- CONVERT DEFINITIONS
72+definition :: Maybe ModuleName.Canonical -> Can.Def -> Optimizer [Opt.Def]
73+definition home (Can.Definition (Can.Facts deps) pattern@(A.A _ ptrn) expression _) =
76+ Expr.collectLambdas expression
79+ (P.Var name, _ : _) ->
81+ do (Env htc uid) <- State.get
82+ State.put (Env False uid)
84+ (args, body) <- optimizeFunction (Just name) args rawBody
86+ (Env isTailCall uid') <- State.get
87+ State.put (Env htc uid')
89+ let facts = Opt.Facts home deps
94+ [ Opt.TailDef facts name args body ]
98+ [ Opt.Def facts name (Opt.Function args body) ]
102+ do hasTailCall <- State.gets _hasTailCall
103+ body <- findTailCalls Nothing rawBody
104+ setTailCall hasTailCall
105+ return [ Opt.Def (Opt.Facts home deps) name body ]
109+ do name <- freshName
110+ optBody <- findTailCalls Nothing rawBody
111+ let optDef = Opt.Def (Opt.Facts home deps) name optBody
112+ return (optDef : patternToDefs home name pattern)
115+ error "there should never be a function where the name is not a P.Var"
122+ -> [P.CanonicalPattern]
124+ -> Optimizer ([String], Opt.Expr)
125+optimizeFunction maybeName patterns givenBody =
126+ do (argNames, body) <- M.foldM depatternArgs ([], givenBody) (reverse patterns)
127+ optBody <- findTailCalls (makeContext argNames =<< maybeName) body
128+ return (argNames, optBody)
131+makeContext :: [String] -> String -> Context
132+makeContext argNames name =
133+ Just (name, length argNames, argNames)
137+ :: ([String], Can.Expr)
138+ -> P.CanonicalPattern
139+ -> Optimizer ([String], Can.Expr)
140+depatternArgs (names, rawExpr) ptrn =
141+ do (name, expr) <- depattern ptrn rawExpr
142+ return (name : names, expr)
146+ :: Maybe ModuleName.Canonical
148+ -> P.CanonicalPattern
150+patternToDefs home rootName pattern =
156+ , Opt.Var (Var.local rootName)
160+ ( [ Var.TopLevelVar moduleName rootName ]
161+ , Opt.Var (Var.topLevel moduleName rootName)
164+ toDef (name, accessExpr) =
165+ Opt.Def (Opt.Facts home deps) name accessExpr
167+ map toDef (substitutions rootExpr pattern)
170+substitutions :: Opt.Expr -> P.CanonicalPattern -> [(String, Opt.Expr)]
171+substitutions expr (A.A _ pattern) =
180+ map (\name -> (name, Opt.Access expr name)) fields
185+ P.Alias alias realPattern ->
186+ (alias, expr) : substitutions expr realPattern
188+ P.Data _ patterns ->
189+ concat (zipWith (substitutions . Opt.DataAccess expr) [0..] patterns)
192+-- CONVERT EXPRESSIONS
194+type Context = Maybe (String, Int, [String])
197+findTailCalls :: Context -> Can.Expr -> Optimizer Opt.Expr
198+findTailCalls context annExpr@(A.A region expression) =
201+ findTailCalls context
202+ -- this *might* find a tail call, use this when something is a potential
206+ findTailCalls Nothing
207+ -- this will never find tail calls, use this when you are working with
208+ -- expressions that cannot be turned into tail calls
212+ pure (Opt.Literal lit)
217+ Expr.Range lowExpr highExpr ->
219+ <$> justConvert lowExpr
220+ <*> justConvert highExpr
222+ Expr.ExplicitList elements ->
224+ <$> T.traverse justConvert elements
226+ Expr.Binop op leftExpr rightExpr ->
227+ optimizeBinop context region op leftExpr rightExpr
232+ Expr.collectLambdas annExpr
234+ uncurry Opt.Function <$> optimizeFunction Nothing patterns body
239+ Expr.collectApps annExpr
241+ case isTailCall context func (length args) of
242+ Just (name, argNames) ->
243+ do optArgs <- T.traverse justConvert args
245+ return (Opt.TailCall name argNames optArgs)
248+ do hasTailCall <- State.gets _hasTailCall
249+ optFunc <- justConvert func
250+ optArgs <- T.traverse justConvert args
251+ setTailCall hasTailCall
252+ return (Opt.Call optFunc optArgs)
255+ Expr.If branches finally ->
257+ crawlBranch (cond,branch) =
258+ (,) <$> justConvert cond <*> keepLooking branch
261+ <$> T.traverse crawlBranch branches
262+ <*> keepLooking finally
264+ Expr.Let defs body ->
265+ do optDefs <- concat <$> T.traverse (definition Nothing) defs
266+ Opt.Let optDefs <$> keepLooking body
268+ Expr.Case expr rawBranches ->
270+ indexify index (pattern, branch) =
275+ (patterns, indexedBranches) =
276+ unzip (zipWith indexify [0..] rawBranches)
279+ Cases.compile (error "need a VariantDict") patterns
282+ Map.fromList indexedBranches
285+ <$> justConvert expr
286+ <*> pure decisionTree
287+ <*> T.traverse keepLooking branches
289+ Expr.Data name exprs ->
290+ Opt.Data name <$> T.traverse justConvert exprs
292+ Expr.Access record field ->
294+ <$> justConvert record
297+ Expr.Update record fields ->
299+ <$> justConvert record
300+ <*> T.traverse (mapSnd justConvert) fields
302+ Expr.Record fields ->
304+ <$> T.traverse (mapSnd justConvert) fields
306+ Expr.GLShader uid src gltipe ->
307+ pure (Opt.GLShader uid src gltipe)
312+ Expr.In name tipe ->
313+ pure (Expr.In name tipe)
315+ Expr.Out name expr tipe ->
316+ (\e -> Expr.Out name e tipe) <$> justConvert expr
318+ Expr.Task name expr tipe ->
319+ (\e -> Expr.Task name e tipe) <$> justConvert expr
324+isTailCall :: Context -> Can.Expr -> Int -> Maybe (String, [String])
325+isTailCall context (A.A _ function) arity =
326+ case (context, function) of
327+ ( Just (ctxName, ctxArity, argNames), Expr.Var (Var.Canonical home name) ) ->
329+ if name == ctxName && arity == ctxArity && Var.isLocalHome home then
330+ Just (name, argNames)
341+-- given a pattern and an expression, push the actual pattern matching into the
342+-- expression as a case-expression if necessary. The pattern compiler will work
345+depattern :: P.CanonicalPattern -> Can.Expr -> Optimizer (String, Can.Expr)
346+depattern canPattern@(A.A _ pattern) expr =
349+ A.A (error "the annotation added in 'depattern' should not be observed!")
351+ caseExpr e branches =
352+ ann (Expr.Case e branches)
355+ ann (Expr.Var (Var.local name))
358+ do name <- freshName
361+ , caseExpr (var name) [ (canPattern, expr) ]
375+ do name <- freshName
385+freshName :: Optimizer String
387+ do (Env htc uid) <- State.get
388+ State.put (Env htc (uid + 1))
389+ return ("_p" ++ show uid)
400+ -> Optimizer Opt.Expr
401+optimizeBinop context region op leftExpr rightExpr =
404+ binop = ann (Expr.Binop op leftExpr rightExpr)
406+ if op == forwardApply then
408+ findTailCalls context (collect id left forwardApply binop)
410+ else if op == backwardApply then
412+ findTailCalls context (collect id right backwardApply binop)
414+ else if op == forwardCompose then
417+ let makeRoot func = ann (Expr.App func (ann (Expr.Var (Var.local var))))
418+ let body = collect makeRoot left forwardCompose binop
419+ findTailCalls context (ann (Expr.Lambda (ann (P.Var var)) body))
421+ else if op == backwardCompose then
424+ let makeRoot func = ann (Expr.App func (ann (Expr.Var (Var.local var))))
425+ let body = collect makeRoot right backwardCompose binop
426+ findTailCalls context (ann (Expr.Lambda (ann (P.Var var)) body))
428+ else if op == Var.Canonical Var.BuiltIn "::" then
430+ findTailCalls context (ann (Expr.Data "::" [ leftExpr, rightExpr ]))
435+ <$> findTailCalls Nothing leftExpr
436+ <*> findTailCalls Nothing rightExpr
439+-- left-associative ((x |> f) |> g)
440+forwardApply :: Var.Canonical
442+ Var.inCore ["Basics"] "|>"
445+-- right-associative (g <| (f <| x))
446+backwardApply :: Var.Canonical
448+ Var.inCore ["Basics"] "<|"
451+-- left-associative ((f >> g) >> h)
452+forwardCompose :: Var.Canonical
454+ Var.inCore ["Basics"] ">>"
457+-- right-associative (h << (g << f))
458+backwardCompose :: Var.Canonical
460+ Var.inCore ["Basics"] "<<"
464+ Can.Expr -> Can.Expr -> (Can.Expr, Can.Expr)
477+collect :: (Can.Expr -> Can.Expr) -> Assoc -> Var.Canonical -> Can.Expr -> Can.Expr
478+collect makeRoot assoc desiredOp annExpr@(A.A ann expr) =
480+ Expr.Binop op leftExpr rightExpr | op == desiredOp ->
482+ (func, arg) = assoc leftExpr rightExpr
484+ A.A ann (Expr.App func (collect makeRoot assoc desiredOp arg))