Skip to content

Commit 965d1fe

Browse files
committed
Annotation-based inlining + a few optimisations.
1 parent e255c36 commit 965d1fe

File tree

37 files changed

+572
-495
lines changed

37 files changed

+572
-495
lines changed

lib/Language/PureScript/Backend.hs

+5-8
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import Language.PureScript.Backend.Lua.Types qualified as Lua
1313
import Language.PureScript.Backend.Types (AppOrModule (..), entryPointModule)
1414
import Language.PureScript.CoreFn.Reader qualified as CoreFn
1515
import Path (Abs, Dir, Path, SomeBase)
16+
import Text.Pretty.Simple (pPrint)
1617
import Prelude hiding (show)
1718

1819
data CompilationResult = CompilationResult
@@ -32,8 +33,8 @@ compileModules
3233
AppOrModule
3334
ExceptT (Variant e) IO CompilationResult
3435
compileModules outputDir foreignDir appOrModule = do
35-
cfnModules
36-
CoreFn.readModuleRecursively outputDir (entryPointModule appOrModule)
36+
let entryModuleName = entryPointModule appOrModule
37+
cfnModules CoreFn.readModuleRecursively outputDir entryModuleName
3738
let dataDecls = IR.collectDataDeclarations cfnModules
3839
irResults forM (Map.toList cfnModules) \(_psModuleName, cfnModule)
3940
Oops.hoistEither $ IR.mkModule cfnModule dataDecls
@@ -42,12 +43,8 @@ compileModules outputDir foreignDir appOrModule = do
4243
Linker.makeUberModule (linkerMode appOrModule) irModules
4344
& optimizedUberModule
4445
let needsRuntimeLazy = Tagged (any untag needsRuntimeLazys)
45-
46-
unoptimizedChunk
47-
Lua.fromUberModule foreignDir needsRuntimeLazy appOrModule uberModule
48-
pure
49-
CompilationResult {lua = optimizeChunk unoptimizedChunk, ir = uberModule}
50-
46+
chunk Lua.fromUberModule foreignDir needsRuntimeLazy appOrModule uberModule
47+
pure CompilationResult {lua = optimizeChunk chunk, ir = uberModule}
5148
linkerMode AppOrModule Linker.LinkMode
5249
linkerMode = \case
5350
AsModule psModuleName Linker.LinkAsModule psModuleName

lib/Language/PureScript/Backend/IR.hs

+32-8
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,13 @@ runRepM
6969
Context
7070
RepM a
7171
Either CoreFnError (Tagged "needsRuntimeLazy" Bool, a)
72-
runRepM ctx (RepM m) =
73-
runStateT m ctx <&> \(a, ctx')
74-
(Tagged . getAny $ needsRuntimeLazy ctx', a)
72+
runRepM ctx (RepM m) = do
73+
(a, ctx') runStateT m ctx
74+
let remainingAnnotations = annotations ctx'
75+
unless (Map.null remainingAnnotations) do
76+
Left . CoreFnError (Cfn.moduleName (contextModule ctx)) $
77+
UnusedAnnotations remainingAnnotations
78+
pure (Tagged . getAny $ needsRuntimeLazy ctx', a)
7579

7680
mkModule
7781
Cfn.Module Cfn.Ann
@@ -119,6 +123,15 @@ parseAnnotations currentModule =
119123
& first
120124
(CoreFnError (Cfn.moduleName currentModule) . AnnotationParsingError)
121125

126+
useAnnotation Name RepM (Maybe Annotation)
127+
useAnnotation name = do
128+
ctx get
129+
let (ann, annotations') =
130+
-- delete the annotation from the map returning the value
131+
Map.updateLookupWithKey (\_ _ Nothing) name (annotations ctx)
132+
put $ ctx {annotations = annotations'}
133+
pure ann
134+
122135
mkImports RepM [ModuleName]
123136
mkImports = do
124137
Cfn.Module {moduleName, moduleImports} gets contextModule
@@ -135,8 +148,13 @@ mkReExports =
135148
Map.fromAscList . fmap (identToName <<$>>) . Map.toAscList
136149
<$> gets (contextModule >>> Cfn.moduleReExports)
137150

138-
mkForeign RepM [Name]
139-
mkForeign = identToName <<$>> gets (contextModule >>> Cfn.moduleForeign)
151+
mkForeign RepM [(Ann, Name)]
152+
mkForeign = do
153+
idents gets (contextModule >>> Cfn.moduleForeign)
154+
forM idents \ident do
155+
let name = identToName ident
156+
ann useAnnotation name
157+
pure (ann, name)
140158

141159
collectDataDeclarations
142160
Map ModuleName (Cfn.Module Cfn.Ann)
@@ -179,7 +197,7 @@ mkBinding ∷ Cfn.Bind Cfn.Ann → RepM Binding
179197
mkBinding = \case
180198
Cfn.NonRec _ann ident cfnExpr do
181199
let name = identToName ident
182-
ann gets $ annotations >>> Map.lookup name
200+
ann useAnnotation name
183201
expr makeExprAnnotated ann cfnExpr
184202
pure $ Standalone (noAnn, name, expr)
185203
Cfn.Rec bindingGroup do
@@ -324,7 +342,7 @@ mkLet ann binds expr = do
324342
-- The algorithm is based on this document: ------------------------------------
325343
-- https://julesjacobs.com/notes/patternmatching/patternmatching.pdf -----------
326344

327-
mkCase Ann -> [CfnExp] NonEmpty (Cfn.CaseAlternative Cfn.Ann) RepM Exp
345+
mkCase Ann [CfnExp] NonEmpty (Cfn.CaseAlternative Cfn.Ann) RepM Exp
328346
mkCase ann cfnExpressions alternatives = do
329347
expressions traverse makeExpr cfnExpressions
330348
-- Before making clauses, we need to prepare bindings
@@ -699,7 +717,10 @@ algebraicTy modName tyName = do
699717
--------------------------------------------------------------------------------
700718
-- Errors ----------------------------------------------------------------------
701719

702-
throwContextualError CoreFnErrorReason RepM a
720+
throwContextualError
721+
(MonadState Context m, MonadError CoreFnError m)
722+
CoreFnErrorReason
723+
m a
703724
throwContextualError e = do
704725
currentModule gets (contextModule >>> Cfn.moduleName)
705726
throwError $ CoreFnError currentModule e
@@ -730,6 +751,7 @@ data CoreFnErrorReason
730751
TyName
731752
| UnicodeDecodeError UnicodeException
732753
| AnnotationParsingError (Megaparsec.ParseErrorBundle Text Void)
754+
| UnusedAnnotations (Map Name Annotation)
733755

734756
instance Show CoreFnErrorReason where
735757
show = \case
@@ -762,3 +784,5 @@ instance Show CoreFnErrorReason where
762784
"Unicode decode error: " <> displayException e
763785
AnnotationParsingError bundle
764786
"Annotation parsing error: " <> Megaparsec.errorBundlePretty bundle
787+
UnusedAnnotations anns
788+
"Unused annotations: " <> toString (pShow anns)

lib/Language/PureScript/Backend/IR/Inliner.hs

+2-20
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,13 @@
11
module Language.PureScript.Backend.IR.Inliner where
22

3-
import Control.Monad.Combinators (choice)
43
import Language.PureScript.Backend.IR.Names (Name, nameParser)
54
import Text.Megaparsec qualified as Megaparsec
65
import Text.Megaparsec.Char qualified as MC
76
import Text.Megaparsec.Char.Lexer qualified as ML
87

98
type Pragma = (Name, Annotation)
109

11-
data Annotation = Annotation InlineScope InlineRecipe
12-
deriving stock (Show, Eq, Ord)
13-
14-
data InlineScope = InModule | Global
15-
deriving stock (Show, Eq, Ord)
16-
17-
data InlineRecipe = Default | Always | Never
10+
data Annotation = Always | Never
1811
deriving stock (Show, Eq, Ord)
1912

2013
type Parser = Megaparsec.Parsec Void Text
@@ -25,18 +18,7 @@ pragmaParser = do
2518
(,) <$> (nameParser <* sc) <*> annotationParser
2619

2720
annotationParser Parser Annotation
28-
annotationParser = Annotation <$> scopeParser <*> recipeParser
29-
30-
recipeParser Parser InlineRecipe
31-
recipeParser =
32-
choice
33-
[ Default <$ symbol "default"
34-
, Always <$ symbol "always"
35-
, Never <$ symbol "never"
36-
]
37-
38-
scopeParser Parser InlineScope
39-
scopeParser = maybe InModule (const Global) <$> optional (symbol "export")
21+
annotationParser = (Always <$ symbol "always") <|> (Never <$ symbol "never")
4022

4123
symbol Text Parser ()
4224
symbol = void . ML.symbol sc

lib/Language/PureScript/Backend/IR/Linker.hs

+7-4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ module Language.PureScript.Backend.IR.Linker where
44

55
import Data.Graph (graphFromEdges', reverseTopSort)
66
import Data.Map qualified as Map
7+
import Language.PureScript.Backend.IR.Inliner qualified as Inline
78
import Language.PureScript.Backend.IR.Names
89
( ModuleName
910
, Name (..)
@@ -22,7 +23,6 @@ import Language.PureScript.Backend.IR.Types
2223
, RawExp (..)
2324
, bindingNames
2425
, noAnn
25-
, objectProp
2626
, refImported
2727
)
2828

@@ -81,10 +81,13 @@ foreignBindings Module {moduleName, modulePath, moduleForeigns} =
8181
]
8282

8383
foreignNamesBindings [Grouping (QName, Exp)] =
84-
moduleForeigns <&> \name
84+
moduleForeigns <&> \(_ann, name)
8585
Standalone
8686
( QName moduleName name
87-
, objectProp foreignModuleRef (PropName (nameToText name))
87+
, ObjectProp
88+
(Just Inline.Always)
89+
foreignModuleRef
90+
(PropName (nameToText name))
8891
)
8992

9093
qualifiedModuleBindings Module [Grouping (QName, Exp)]
@@ -98,7 +101,7 @@ qualifiedModuleBindings Module {moduleName, moduleBindings, moduleForeigns} =
98101
(QName moduleName name, qualifyTopRefs moduleName topRefs expr)
99102
where
100103
topRefs Map Name Index = Map.fromList do
101-
(,0) <$> ((moduleBindings >>= bindingNames) <> moduleForeigns)
104+
(,0) <$> ((moduleBindings >>= bindingNames) <> fmap snd moduleForeigns)
102105

103106
qualifyTopRefs ModuleName Map Name Index Exp Exp
104107
qualifyTopRefs moduleName = go

lib/Language/PureScript/Backend/IR/Optimizer.hs

+19-11
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,7 @@ import Data.List.NonEmpty qualified as NE
44
import Data.Map qualified as Map
55
import Data.Set qualified as Set
66
import Language.PureScript.Backend.IR.DCE qualified as DCE
7-
import Language.PureScript.Backend.IR.Inliner
8-
( Annotation (..)
9-
, InlineRecipe (..)
10-
, InlineScope (InModule)
11-
)
7+
import Language.PureScript.Backend.IR.Inliner (Annotation (..))
128
import Language.PureScript.Backend.IR.Linker (UberModule (..))
139
import Language.PureScript.Backend.IR.Names
1410
( Name (..)
@@ -228,6 +224,7 @@ optimizedExpression =
228224
rewriteExpTopDown $
229225
constantFolding
230226
`thenRewrite` betaReduce
227+
`thenRewrite` etaReduce
231228
`thenRewrite` betaReduceUnusedParams
232229
`thenRewrite` removeUnreachableThenBranch
233230
`thenRewrite` removeUnreachableElseBranch
@@ -239,6 +236,9 @@ constantFolding =
239236
pure . \case
240237
Eq _ (LiteralBool _ a) (LiteralBool _ b)
241238
Rewritten Stop $ literalBool $ a == b
239+
Eq _ (LiteralBool _ True) b
240+
-- 'b' must be of type Bool
241+
Rewritten Stop b
242242
Eq _ (LiteralInt _ a) (LiteralInt _ b)
243243
Rewritten Stop $ literalBool $ a == b
244244
Eq _ (LiteralFloat _ a) (LiteralFloat _ b)
@@ -249,12 +249,20 @@ constantFolding =
249249
Rewritten Stop $ literalBool $ a == b
250250
_ NoChange
251251

252-
-- \x -> (\y -> y + 1) x ===> (\y -> y + 1)
252+
-- (λx. M) N ===> M[x := N]
253253
betaReduce RewriteRule Ann
254254
betaReduce =
255255
pure . \case
256-
Abs _ (ParamNamed _ _) (App _ f (Ref _ (Local _) 0))
257-
Rewritten Recurse f
256+
App _ (Abs _ (ParamNamed _ param) body) r
257+
Rewritten Recurse $ substitute (Local param) 0 r body
258+
_ NoChange
259+
260+
-- (λx. M x) where x not free in M ===> M
261+
etaReduce RewriteRule Ann
262+
etaReduce =
263+
pure . \case
264+
Abs _ (ParamNamed _ _param) (App _ m (Ref _ (Local _) 0))
265+
Rewritten Recurse m
258266
_ NoChange
259267

260268
betaReduceUnusedParams RewriteRule Ann
@@ -317,6 +325,6 @@ isInlinableExpr expr =
317325
hasInlineAnnotation Exp Bool
318326
hasInlineAnnotation =
319327
getAnn >>> \case
320-
Just (Annotation InModule Always) True
321-
Just (Annotation InModule Never) False
322-
_ False
328+
Just Always True
329+
Just Never False
330+
Nothing False

lib/Language/PureScript/Backend/IR/Types.hs

+6-3
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ data Module = Module
3333
, moduleImports [ModuleName]
3434
, moduleExports [Name]
3535
, moduleReExports Map ModuleName [Name]
36-
, moduleForeigns [Name]
36+
, moduleForeigns [(Ann, Name)]
3737
, modulePath FilePath
3838
}
3939

@@ -83,7 +83,7 @@ data RawExp ann
8383
| Let ann (NonEmpty (Grouping (ann, Name, RawExp ann))) (RawExp ann)
8484
| IfThenElse ann (RawExp ann) (RawExp ann) (RawExp ann)
8585
| Exception ann Text
86-
| ForeignImport ann ModuleName FilePath [Name]
86+
| ForeignImport ann ModuleName FilePath [(ann, Name)]
8787

8888
deriving stock instance Show ann Show (RawExp ann)
8989
deriving stock instance Eq ann Eq (RawExp ann)
@@ -359,7 +359,10 @@ annotateExpM around annotateExp annotateParam annotateName =
359359
pure $ IfThenElse ann i' t' e'
360360
Ctor _ann mn aty ty ctr fs pure $ Ctor ann mn aty ty ctr fs
361361
Exception _ann m pure $ Exception ann m
362-
ForeignImport _ann m p ns pure $ ForeignImport ann m p ns
362+
ForeignImport _ann m p ns do
363+
anns traverse (uncurry annotateName) ns
364+
let ns' = zip anns (fmap snd ns)
365+
pure $ ForeignImport ann m p ns'
363366
where
364367
mkAnn RawExp ann m (RawExp ann')
365368
mkAnn = annotateExpM around annotateExp annotateParam annotateName

0 commit comments

Comments
 (0)