git.iko.soy/rowtype-yoga/purescript-backend-optimizer
on main help sign in
Add hook for custom analysis (#99)
commit78a20c087c49379cf2d821bf79ff6f13ea4b88b3 authorNathan Faubion <natefaubion@users.noreply.github.com> dateSun 1 Oct 2023, 08:36 (3 years ago) parent54b82ac
7 files changed+87−55
backend-es/src/Main.purs+1 backend-es/src/PureScript/Backend/Optimizer/Codegen/EcmaScript/Builder.purs+7−3 backend-es/test/Main.purs+1 src/PureScript/Backend/Optimizer/Analysis.purs+20−11 src/PureScript/Backend/Optimizer/Builder.purs+7−3 src/PureScript/Backend/Optimizer/Convert.purs+21−16 src/PureScript/Backend/Optimizer/Semantics.purs+30−22
backend-es/src/Main.purs+1
@@ -250,6 +250,7 @@ main cliRoot =
250250 buildCmd args = liftEffect makeBuildState >>= \state -> basicBuildMain
251251 { resolveCoreFnDirectory: pure args.coreFnDir
252252 , resolveExternalDirectives: map (fromMaybe Map.empty) $ traverse externalDirectivesFromFile args.directivesFile
253+ , analyzeCustom: \_ _ -> Nothing
253254 , foreignSemantics: Map.union coreForeignSemantics esForeignSemantics
254255 , onCodegenBefore: do
255256 liftEffect $ flip Ref.write state.codegenStartTime <<< Just =<< now
backend-es/src/PureScript/Backend/Optimizer/Codegen/EcmaScript/Builder.purs+7−3
@@ -15,7 +15,7 @@ import Data.Lazy as Lazy
1515import Data.List (List)
1616import Data.Map (Map)
1717import Data.Map as Map
18−import Data.Maybe (maybe)
18+import Data.Maybe (Maybe, maybe)
1919import Data.Set (Set)
2020import Data.Set as Set
2121import Data.Set.NonEmpty as NonEmptySet
@@ -28,6 +28,7 @@ import Node.FS.Aff as FS
2828import Node.Glob.Basic (expandGlobs)
2929import Node.Path (FilePath)
3030import Node.Process as Process
31+import PureScript.Backend.Optimizer.Analysis (BackendAnalysis)
3132import PureScript.Backend.Optimizer.Builder (BuildEnv, buildModules)
3233import PureScript.Backend.Optimizer.Convert (BackendModule, OptimizationSteps)
3334import PureScript.Backend.Optimizer.CoreFn (Ann, Ident, Module, ModuleName(..), Qualified)
@@ -35,8 +36,9 @@ import PureScript.Backend.Optimizer.CoreFn.Json (decodeModule)
3536import PureScript.Backend.Optimizer.CoreFn.Sort (emptyPull, pullResult, resumePull, sortModules)
3637import PureScript.Backend.Optimizer.Directives (parseDirectiveFile)
3738import PureScript.Backend.Optimizer.Directives.Defaults as Defaults
38−import PureScript.Backend.Optimizer.Semantics (InlineDirectiveMap)
39+import PureScript.Backend.Optimizer.Semantics (BackendExpr, Ctx, InlineDirectiveMap)
3940import PureScript.Backend.Optimizer.Semantics.Foreign (ForeignEval)
41+import PureScript.Backend.Optimizer.Syntax (BackendSyntax)
4042import PureScript.CST.Errors (printParseError)
4143
4244coreFnModulesFromOutput :: String -> NonEmptyArray String -> Aff (Either (NonEmptyArray (Tuple FilePath String)) (List (Module Ann)))
@@ -83,6 +85,7 @@ externalDirectivesFromFile filePath = do
8385basicBuildMain
8486 :: { resolveCoreFnDirectory :: Aff FilePath
8587 , resolveExternalDirectives :: Aff InlineDirectiveMap
88+ , analyzeCustom :: Ctx -> BackendSyntax BackendExpr -> Maybe BackendAnalysis
8689 , foreignSemantics :: Map (Qualified Ident) ForeignEval
8790 , onCodegenBefore :: Aff Unit
8891 , onCodegenAfter :: Aff Unit
@@ -106,7 +109,8 @@ basicBuildMain options = do
106109 Right coreFnModules -> do
107110 options.onCodegenBefore
108111 coreFnModules # buildModules
109− { directives: allDirectives
112+ { analyzeCustom: options.analyzeCustom
113+ , directives: allDirectives
110114 , foreignSemantics: options.foreignSemantics
111115 , onCodegenModule: options.onCodegenModule
112116 , onPrepareModule: options.onPrepareModule
backend-es/test/Main.purs+1
@@ -120,6 +120,7 @@ runSnapshotTests { accept, filter, traceIdents } = do
120120 stepsRef <- liftEffect $ Ref.new []
121121 coreFnModules # buildModules
122122 { directives
123+ , analyzeCustom: \_ _ -> Nothing
123124 , foreignSemantics: Map.union coreForeignSemantics esForeignSemantics
124125 , onCodegenModule: \build (Module { name: ModuleName name, path }) backendMod optimizationSteps -> do
125126 let
src/PureScript/Backend/Optimizer/Analysis.purs+20−11
@@ -200,14 +200,19 @@ class HasAnalysis a where
200200resultOf :: forall a. HasAnalysis a => a -> ResultTerm
201201resultOf = analysisOf >>> unwrap >>> _.result
202202
203−analyze :: forall a. HasAnalysis a => HasSyntax a => (Tuple (Qualified Ident) (Maybe BackendAccessor) -> BackendAnalysis) -> BackendSyntax a -> BackendAnalysis
203+analyze :: forall a. HasAnalysis a => HasSyntax a => (Qualified Ident -> Maybe String -> Maybe BackendAnalysis) -> BackendSyntax a -> BackendAnalysis
204204analyze externAnalysis expr = case expr of
205− Var qi -> do
206− let BackendAnalysis { args } = externAnalysis (Tuple qi Nothing)
207− withArgs args
208− $ bump
209− $ externs
210− $ usedDep qi mempty
205+ Var qi ->
206+ case externAnalysis qi Nothing of
207+ Just (BackendAnalysis { args }) ->
208+ withArgs args analysis
209+ Nothing ->
210+ analysis
211+ where
212+ analysis =
213+ bump
214+ $ externs
215+ $ usedDep qi mempty
211216 Local _ lvl ->
212217 bump
213218 $ used lvl
@@ -351,9 +356,13 @@ analyze externAnalysis expr = case expr of
351356 analysis
352357 Just (Local _ lvl) ->
353358 accessed lvl $ complex Deref analysis
354− Just (Var qi) -> do
355− let BackendAnalysis { args } = externAnalysis (Tuple qi (Just acc))
356− withArgs args $ complex Trivial analysis
359+ Just (Var qi) ->
360+ case acc of
361+ GetProp prop
362+ | Just (BackendAnalysis { args }) <- externAnalysis qi (Just prop) ->
363+ withArgs args $ complex Trivial analysis
364+ _ ->
365+ complex Trivial analysis
357366 _ ->
358367 complex Deref analysis
359368 where
@@ -381,7 +390,7 @@ analyze externAnalysis expr = case expr of
381390 withResult KnownNeutral
382391 $ analyzeDefault expr
383392
384−analyzeEffectBlock :: forall a. HasAnalysis a => HasSyntax a => (Tuple (Qualified Ident) (Maybe BackendAccessor) -> BackendAnalysis) -> BackendSyntax a -> BackendAnalysis
393+analyzeEffectBlock :: forall a. HasAnalysis a => HasSyntax a => (Qualified Ident -> Maybe String -> Maybe BackendAnalysis) -> BackendSyntax a -> BackendAnalysis
385394analyzeEffectBlock externAnalysis expr = case expr of
386395 Let _ lvl a b ->
387396 withResult (resultOf b)
src/PureScript/Backend/Optimizer/Builder.purs+7−3
@@ -11,13 +11,15 @@ import Data.List (List, foldM)
1111import Data.List as List
1212import Data.Map (Map)
1313import Data.Map as Map
14+import Data.Maybe (Maybe)
1415import Data.Set (Set)
1516import Data.Tuple (Tuple(..))
1617import PureScript.Backend.Optimizer.Analysis (BackendAnalysis)
1718import PureScript.Backend.Optimizer.Convert (BackendModule, OptimizationSteps, toBackendModule)
1819import PureScript.Backend.Optimizer.CoreFn (Ann, Ident, Module(..), Qualified)
19−import PureScript.Backend.Optimizer.Semantics (ExternImpl, InlineDirectiveMap)
20+import PureScript.Backend.Optimizer.Semantics (BackendExpr, Ctx, ExternImpl, InlineDirectiveMap)
2021import PureScript.Backend.Optimizer.Semantics.Foreign (ForeignEval)
22+import PureScript.Backend.Optimizer.Syntax (BackendSyntax)
2123
2224type BuildEnv =
2325 { implementations :: Map (Qualified Ident) (Tuple BackendAnalysis ExternImpl)
@@ -26,7 +28,8 @@ type BuildEnv =
2628 }
2729
2830type BuildOptions m =
29− { directives :: InlineDirectiveMap
31+ { analyzeCustom :: Ctx -> BackendSyntax BackendExpr -> Maybe BackendAnalysis
32+ , directives :: InlineDirectiveMap
3033 , foreignSemantics :: Map (Qualified Ident) ForeignEval
3134 , onPrepareModule :: BuildEnv -> Module Ann -> m (Module Ann)
3235 , onCodegenModule :: BuildEnv -> Module Ann -> BackendModule -> OptimizationSteps -> m Unit
@@ -45,7 +48,8 @@ buildModules options coreFnModules =
4548 coreFnModule'@(Module { name }) <- options.onPrepareModule buildEnv coreFnModule
4649 let
4750 Tuple optimizationSteps backendMod = toBackendModule coreFnModule'
48− { currentModule: name
51+ { analyzeCustom: options.analyzeCustom
52+ , currentModule: name
4953 , currentLevel: 0
5054 , toLevel: Map.empty
5155 , implementations
src/PureScript/Backend/Optimizer/Convert.purs+21−16
@@ -70,10 +70,10 @@ import Data.Traversable (class Foldable, Accum, foldr, for, mapAccumL, mapAccumR
7070import Data.TraversableWithIndex (forWithIndex)
7171import Data.Tuple (Tuple(..), fst, snd)
7272import Partial.Unsafe (unsafeCrashWith, unsafePartial)
73−import PureScript.Backend.Optimizer.Analysis (BackendAnalysis)
73+import PureScript.Backend.Optimizer.Analysis (BackendAnalysis, analyze, analyzeEffectBlock)
7474import PureScript.Backend.Optimizer.CoreFn (Ann(..), Bind(..), Binder(..), Binding(..), CaseAlternative(..), CaseGuard(..), Comment, ConstructorType(..), Expr(..), Guard(..), Ident(..), Literal(..), Meta(..), Module(..), ModuleName(..), ProperName, Qualified(..), ReExport, findProp, propKey, propValue, qualifiedModuleName, unQualified)
7575import PureScript.Backend.Optimizer.Directives (DirectiveHeaderResult, parseDirectiveHeader)
76−import PureScript.Backend.Optimizer.Semantics (BackendExpr(..), BackendSemantics, Ctx, DataTypeMeta, Env(..), EvalRef(..), ExternImpl(..), ExternSpine, InlineAccessor(..), InlineDirective(..), InlineDirectiveMap, NeutralExpr(..), build, evalExternFromImpl, evalExternRefFromImpl, freeze, optimize)
76+import PureScript.Backend.Optimizer.Semantics (BackendExpr(..), BackendSemantics, Ctx(..), DataTypeMeta, Env(..), EvalRef(..), ExternImpl(..), ExternSpine, InlineAccessor(..), InlineDirective(..), InlineDirectiveMap, NeutralExpr(..), build, evalExternFromImpl, evalExternRefFromImpl, freeze, optimize)
7777import PureScript.Backend.Optimizer.Semantics.Foreign (ForeignEval)
7878import PureScript.Backend.Optimizer.Syntax (BackendAccessor(..), BackendOperator(..), BackendOperator1(..), BackendOperator2(..), BackendOperatorOrd(..), BackendSyntax(..), Level(..), Pair(..))
7979import PureScript.Backend.Optimizer.Utils (foldl1Array)
@@ -100,7 +100,8 @@ type BackendModule =
100100 }
101101
102102type ConvertEnv =
103− { currentLevel :: Int
103+ { analyzeCustom :: Ctx -> BackendSyntax BackendExpr -> Maybe BackendAnalysis
104+ , currentLevel :: Int
104105 , currentModule :: ModuleName
105106 , dataTypes :: Map ProperName DataTypeMeta
106107 , toLevel :: Map Ident Level
@@ -357,29 +358,33 @@ buildM :: BackendSyntax BackendExpr -> ConvertM BackendExpr
357358buildM a env = build (getCtx env) a
358359
359360getCtx :: ConvertEnv -> Ctx
360−getCtx env =
361+getCtx env = Ctx
361362 { currentLevel: env.currentLevel
362363 , lookupExtern
364+ , analyze: \ctx@(Ctx { effect }) expr ->
365+ case env.analyzeCustom ctx expr of
366+ Just s -> s
367+ Nothing ->
368+ if effect then
369+ analyzeEffectBlock lookupExtern expr
370+ else
371+ analyze lookupExtern expr
363372 , effect: false
364373 }
365374 where
366− lookupExtern (Tuple qual acc) = do
375+ lookupExtern qual acc = do
367376 Tuple s impl <- Map.lookup qual env.implementations
368377 case impl of
369− ExternExpr _ a ->
378+ ExternExpr _ _ ->
370379 case acc of
371− Nothing ->
372− Just (Tuple s a)
373− _ ->
374− Nothing
380+ Nothing -> Just s
381+ _ -> Nothing
375382 ExternDict _ a ->
376383 case acc of
377− Just (GetProp prop) ->
378− findProp prop a
379− -- Nothing ->
380− -- Just $ Tuple s $ NeutralExpr $ Lit $ LitRecord (map snd <$> a)
381− _ ->
382− Nothing
384+ Just prop ->
385+ fst <$> findProp prop a
386+ Nothing ->
387+ Just s
383388 ExternCtor _ _ _ _ _ ->
384389 Nothing
385390
src/PureScript/Backend/Optimizer/Semantics.purs+30−22
@@ -22,7 +22,7 @@ import Data.Set as Set
2222import Data.String as String
2323import Data.Tuple (Tuple(..), fst, snd)
2424import Partial.Unsafe (unsafeCrashWith)
25−import PureScript.Backend.Optimizer.Analysis (class HasAnalysis, BackendAnalysis(..), Capture(..), Complexity(..), ResultTerm(..), Usage(..), analysisOf, analyze, analyzeEffectBlock, bound, bump, complex, resultOf, updated, withResult, withRewrite)
25+import PureScript.Backend.Optimizer.Analysis (class HasAnalysis, BackendAnalysis(..), Capture(..), Complexity(..), ResultTerm(..), Usage(..), analysisOf, bound, bump, complex, resultOf, updated, withResult, withRewrite)
2626import PureScript.Backend.Optimizer.CoreFn (ConstructorType, Ident(..), Literal(..), ModuleName, Prop(..), ProperName, Qualified(..), findProp, propKey, propValue)
2727import PureScript.Backend.Optimizer.Syntax (class HasSyntax, BackendAccessor(..), BackendEffect, BackendOperator(..), BackendOperator1(..), BackendOperator2(..), BackendOperatorNum(..), BackendOperatorOrd(..), BackendSyntax(..), Level(..), Pair(..), syntaxOf)
2828import PureScript.Backend.Optimizer.Utils (foldl1Array, foldr1Array)
@@ -1150,14 +1150,25 @@ caseNumber = case _ of
11501150 NeutLit (LitNumber a) -> Just a
11511151 _ -> Nothing
11521152
1153−type Ctx =
1153+newtype Ctx = Ctx
11541154 { currentLevel :: Int
1155− , lookupExtern :: Tuple (Qualified Ident) (Maybe BackendAccessor) -> Maybe (Tuple BackendAnalysis NeutralExpr)
1155+ , lookupExtern :: Qualified Ident -> Maybe String -> Maybe BackendAnalysis
1156+ , analyze :: Ctx -> BackendSyntax BackendExpr -> BackendAnalysis
11561157 , effect :: Boolean
11571158 }
11581159
11591160nextLevel :: Ctx -> Tuple Level Ctx
1160−nextLevel ctx = Tuple (Level ctx.currentLevel) $ ctx { currentLevel = ctx.currentLevel + 1 }
1161+nextLevel (Ctx ctx) = Tuple (Level ctx.currentLevel) $ Ctx ctx { currentLevel = ctx.currentLevel + 1 }
1162+
1163+effectfully :: Ctx -> Ctx
1164+effectfully (Ctx ctx)
1165+ | ctx.effect = Ctx ctx
1166+ | otherwise = Ctx ctx { effect = true }
1167+
1168+purely :: Ctx -> Ctx
1169+purely (Ctx ctx)
1170+ | ctx.effect = Ctx ctx { effect = false }
1171+ | otherwise = Ctx ctx
11611172
11621173quote :: Ctx -> BackendSemantics -> BackendExpr
11631174quote = go
@@ -1166,7 +1177,7 @@ quote = go
11661177 -- Block constructors
11671178 SemLet ident binding k -> do
11681179 let Tuple level ctx' = nextLevel ctx
1169− build ctx $ Let ident level (quote (ctx { effect = false }) binding) $ quote ctx' $ k $ SemRef (EvalLocal ident level) [] $ defer \_ -> deref binding
1180+ build ctx $ Let ident level (quote (purely ctx) binding) $ quote ctx' $ k $ SemRef (EvalLocal ident level) [] $ defer \_ -> deref binding
11701181 SemLetRec bindings k -> do
11711182 let Tuple level ctx' = nextLevel ctx
11721183 -- We are not currently propagating references
@@ -1175,18 +1186,18 @@ quote = go
11751186 -- which we don't currently implement.
11761187 let neutBindings = (\(Tuple ident _) -> Tuple ident $ defer \_ -> NeutLocal (Just ident) level) <$> bindings
11771188 build ctx $ LetRec level
1178− (map (\b -> quote (ctx' { effect = false }) $ b neutBindings) <$> bindings)
1189+ (map (\b -> quote (purely ctx') $ b neutBindings) <$> bindings)
11791190 (quote ctx' $ k neutBindings)
11801191 SemEffectBind ident binding k -> do
1181− let ctx' = ctx { effect = true }
1192+ let ctx' = effectfully ctx
11821193 let Tuple level ctx'' = nextLevel ctx'
11831194 build ctx $ EffectBind ident level (quote ctx' binding) $ quote ctx'' $ k $ NeutLocal ident level
11841195 SemEffectPure sem ->
1185− build ctx $ EffectPure (quote (ctx { effect = false }) sem)
1196+ build ctx $ EffectPure (quote (purely ctx) sem)
11861197 SemEffectDefer sem ->
1187− build ctx $ EffectDefer (quote (ctx { effect = true }) sem)
1198+ build ctx $ EffectDefer (quote (effectfully ctx) sem)
11881199 SemBranch branches def -> do
1189− let ctx' = ctx { effect = false }
1200+ let ctx' = purely ctx
11901201 let quoteCond (SemConditional a b) = Pair (quote ctx' $ force a) (quote ctx $ force b)
11911202 let branches' = quoteCond <$> branches
11921203 foldr (buildBranchCond ctx) (quote ctx <<< force $ def) branches'
@@ -1200,7 +1211,7 @@ quote = go
12001211 go ctx $ neutralSpine (NeutLocal ident lvl) sp
12011212 SemLam ident k -> do
12021213 let Tuple level ctx' = nextLevel ctx
1203− build ctx $ Abs (NonEmptyArray.singleton (Tuple ident level)) $ quote (ctx' { effect = false }) $ k $ NeutLocal ident level
1214+ build ctx $ Abs (NonEmptyArray.singleton (Tuple ident level)) $ quote (purely ctx') $ k $ NeutLocal ident level
12041215 SemMkFn pro -> do
12051216 let
12061217 loop ctx' idents = case _ of
@@ -1208,7 +1219,7 @@ quote = go
12081219 let Tuple lvl ctx'' = nextLevel ctx'
12091220 loop ctx'' (Array.snoc idents (Tuple ident lvl)) (k (NeutLocal ident lvl))
12101221 MkFnApplied body ->
1211− build ctx' $ UncurriedAbs idents $ quote (ctx' { effect = false }) body
1222+ build ctx' $ UncurriedAbs idents $ quote (purely ctx') body
12121223 loop ctx [] pro
12131224 SemMkEffectFn pro -> do
12141225 let
@@ -1217,7 +1228,7 @@ quote = go
12171228 let Tuple lvl ctx'' = nextLevel ctx'
12181229 loop ctx'' (Array.snoc idents (Tuple ident lvl)) (k (NeutLocal ident lvl))
12191230 MkFnApplied body ->
1220− build ctx' $ UncurriedEffectAbs idents $ quote (ctx' { effect = false }) body
1231+ build ctx' $ UncurriedEffectAbs idents $ quote (purely ctx') body
12211232 loop ctx [] pro
12221233 SemAssocOp op spine ->
12231234 foldl1Array
@@ -1240,15 +1251,15 @@ quote = go
12401251 NeutCtorDef _ ct ty tag fields ->
12411252 build ctx $ CtorDef ct ty tag fields
12421253 NeutUncurriedApp hd spine -> do
1243− let ctx' = ctx { effect = false }
1254+ let ctx' = purely ctx
12441255 let hd' = quote ctx' hd
12451256 build ctx $ UncurriedApp hd' (quote ctx' <$> spine)
12461257 NeutUncurriedEffectApp hd spine -> do
1247− let ctx' = ctx { effect = false }
1258+ let ctx' = purely ctx
12481259 let hd' = quote ctx' hd
12491260 build ctx $ UncurriedEffectApp hd' (quote ctx' <$> spine)
12501261 NeutApp hd spine -> do
1251− let ctx' = ctx { effect = false }
1262+ let ctx' = purely ctx
12521263 let hd' = quote ctx' hd
12531264 case NonEmptyArray.fromArray (quote ctx' <$> spine) of
12541265 Nothing ->
@@ -1264,7 +1275,7 @@ quote = go
12641275 NeutPrimOp op ->
12651276 build ctx $ PrimOp (quote ctx <$> op)
12661277 NeutPrimEffect eff ->
1267− build ctx $ PrimEffect (quote (ctx { effect = false }) <$> eff)
1278+ build ctx $ PrimEffect (quote (purely ctx) <$> eff)
12681279 NeutPrimUndefined ->
12691280 build ctx PrimUndefined
12701281 NeutFail err ->
@@ -1397,13 +1408,10 @@ simplifyCondLiftAnd ctx pair def1 = case pair of
13971408 Nothing
13981409
13991410buildStop :: Ctx -> Qualified Ident -> BackendExpr
1400−buildStop ctx stop = ExprRewrite (analyzeDefault ctx (Var stop)) (RewriteStop stop)
1411+buildStop ctx@(Ctx { analyze }) stop = ExprRewrite (analyze ctx (Var stop)) (RewriteStop stop)
14011412
14021413buildDefault :: Ctx -> BackendSyntax BackendExpr -> BackendExpr
1403−buildDefault ctx expr = ExprSyntax (analyzeDefault ctx expr) expr
1404−
1405−analyzeDefault :: Ctx -> BackendSyntax BackendExpr -> BackendAnalysis
1406−analyzeDefault ctx = (if ctx.effect then analyzeEffectBlock else analyze) (foldMap fst <<< ctx.lookupExtern)
1414+buildDefault ctx@(Ctx { analyze }) expr = ExprSyntax (analyze ctx expr) expr
14071415
14081416rewriteInline :: Maybe Ident -> Level -> BackendExpr -> BackendExpr -> BackendExpr
14091417rewriteInline ident level binding body = do