git.iko.soy/rowtype-yoga/purescript-backend-optimizer
on main help sign in
Enable backends to extend `coreForeignSemantics` with backend-specific FFI inliner (#18)

* Extend core foreign semantics with backend-specific ones * Add FFI semantics for toCharCode, almost fromCharCode * Replace 'unionWith const' with union * Rename prop to foreignSemantics * Opt-in to using coreForeignSemantics * Drop fromCharCode FFI semantics

commit28bbb8c8dee64b29eeedb79ad9d10530dddc77fd authorJordan Martinez <jordan.martinez@arista.com> dateTue 16 Aug 2022, 17:46 (4 years ago) parent05a55ba
10 files changed+106−5
backend-es/src/Main.purs+3 backend-es/src/PureScript/Backend/Optimizer/Codegen/EcmaScript/Foreign.purs+58 backend-es/test/Main.purs+4−1 backend-es/test/snapshots-out/Snapshot.BackendSemantics01.js+6 backend-es/test/snapshots-out/Snapshot.BackendSemantics02.js+3 backend-es/test/snapshots/Snapshot.BackendSemantics01.purs+15 backend-es/test/snapshots/Snapshot.BackendSemantics02.purs+6 src/PureScript/Backend/Optimizer/Builder.purs+4−1 src/PureScript/Backend/Optimizer/Builder/Cli.purs+4−1 src/PureScript/Backend/Optimizer/Convert.purs+3−2
backend-es/src/Main.purs+3
@@ -37,7 +37,9 @@ import Node.Process as Process
3737import Node.Stream as Stream
3838import PureScript.Backend.Optimizer.Builder.Cli (basicBuildMain, externalDirectivesFromFile)
3939import PureScript.Backend.Optimizer.Codegen.EcmaScript (esCodegenModule, esModulePath)
40+import PureScript.Backend.Optimizer.Codegen.EcmaScript.Foreign (esForeignSemantics)
4041import PureScript.Backend.Optimizer.CoreFn (Module(..), ModuleName(..))
42+import PureScript.Backend.Optimizer.Semantics.Foreign (coreForeignSemantics)
4143import PureScript.CST.Lexer as Lexer
4244import PureScript.CST.Types (Token(..))
4345import Unsafe.Coerce (unsafeCoerce)
@@ -190,6 +192,7 @@ main cliRoot =
190192 buildCmd args = basicBuildMain
191193 { resolveCoreFnDirectory: pure args.coreFnDir
192194 , resolveExternalDirectives: map (fromMaybe Map.empty) $ traverse externalDirectivesFromFile args.directivesFile
195+ , foreignSemantics: Map.union coreForeignSemantics esForeignSemantics
193196 , onCodegenBefore: do
194197 mkdirp args.outputDir
195198 writeTextFile UTF8 (Path.concat [ args.outputDir, "package.json" ]) esModulePackageJson
backend-es/src/PureScript/Backend/Optimizer/Codegen/EcmaScript/Foreign.pursnew file+58
@@ -0,0 +1,58 @@
1+module PureScript.Backend.Optimizer.Codegen.EcmaScript.Foreign where
2+
3+import Prelude
4+
5+import Data.Enum (fromEnum)
6+import Data.Map (Map)
7+import Data.Map as Map
8+import Data.Maybe (Maybe(..))
9+import Data.Tuple (Tuple(..))
10+import PureScript.Backend.Optimizer.CoreFn (Ident, Literal(..), Qualified)
11+import PureScript.Backend.Optimizer.Semantics (BackendSemantics(..), ExternSpine(..))
12+import PureScript.Backend.Optimizer.Semantics.Foreign (ForeignEval, ForeignSemantics, qualified)
13+
14+esForeignSemantics :: Map (Qualified Ident) ForeignEval
15+esForeignSemantics = Map.fromFoldable semantics
16+ where
17+ semantics =
18+ [ data_bounded_topInt
19+ , data_bounded_bottomInt
20+ , data_bounded_topChar
21+ , data_bounded_bottomChar
22+ , data_enum_toCharCode
23+ ]
24+
25+data_bounded_topInt :: ForeignSemantics
26+data_bounded_topInt = Tuple (qualified "Data.Bounded" "topInt") go
27+ where
28+ go _ _ = const $ Just $ litInt top
29+
30+data_bounded_bottomInt :: ForeignSemantics
31+data_bounded_bottomInt = Tuple (qualified "Data.Bounded" "bottomInt") go
32+ where
33+ go _ _ = const $ Just $ litInt bottom
34+
35+data_bounded_topChar :: ForeignSemantics
36+data_bounded_topChar = Tuple (qualified "Data.Bounded" "topChar") go
37+ where
38+ go _ _ = const $ Just $ litChar top
39+
40+data_bounded_bottomChar :: ForeignSemantics
41+data_bounded_bottomChar = Tuple (qualified "Data.Bounded" "bottomChar") go
42+ where
43+ go _ _ = const $ Just $ litChar bottom
44+
45+data_enum_toCharCode :: ForeignSemantics
46+data_enum_toCharCode = Tuple (qualified "Data.Enum" "toCharCode") go
47+ where
48+ go _ _ = case _ of
49+ [ ExternApp [ NeutLit (LitChar c) ] ] ->
50+ Just $ NeutLit $ LitInt $ fromEnum c
51+ _ ->
52+ Nothing
53+
54+litInt :: Int -> BackendSemantics
55+litInt = NeutLit <<< LitInt
56+
57+litChar :: Char -> BackendSemantics
58+litChar = NeutLit <<< LitChar
backend-es/test/Main.purs+4−1
@@ -33,9 +33,11 @@ import Node.Path as Path
3333import Node.Process as Process
3434import PureScript.Backend.Optimizer.Builder (buildModules, coreFnModulesFromOutput)
3535import PureScript.Backend.Optimizer.Codegen.EcmaScript (esCodegenModule)
36+import PureScript.Backend.Optimizer.Codegen.EcmaScript.Foreign (esForeignSemantics)
37+import PureScript.Backend.Optimizer.CoreFn (Module(..), ModuleName(..))
3638import PureScript.Backend.Optimizer.Directives (parseDirectiveFile)
3739import PureScript.Backend.Optimizer.Directives.Defaults (defaultDirectives)
38−import PureScript.Backend.Optimizer.CoreFn (Module(..), ModuleName(..))
40+import PureScript.Backend.Optimizer.Semantics.Foreign (coreForeignSemantics)
3941import Test.Utils (bufferToUTF8, execWithStdin, spawnFromParent)
4042
4143type TestArgs =
@@ -87,6 +89,7 @@ runSnapshotTests { accept, filter } = do
8789 | otherwise = \name -> Array.any (isJust <<< flip String.stripPrefix (String.toLower name) <<< Pattern) filter
8890 coreFnModules # buildModules
8991 { directives
92+ , foreignSemantics: Map.union coreForeignSemantics esForeignSemantics
9093 , onCodegenModule: \build (Module { name: ModuleName name, path }) backend ->
9194 if Set.member (Path.concat [ snapshotDir, path ]) snapshotPaths && shouldCompare name then do
9295 let formatted = Dodo.print Dodo.plainText (Dodo.twoSpaces { pageWidth = 180, ribbonRatio = 1.0 }) $ esCodegenModule { intTags: false } build.implementations backend
backend-es/test/snapshots-out/Snapshot.BackendSemantics01.jsnew file+6
@@ -0,0 +1,6 @@
1+import * as $runtime from "../runtime.js";
2+const test4 = "\u0000";
3+const test3 = "￿";
4+const test2 = -2147483647;
5+const test1 = 2147483646;
6+export {test1, test2, test3, test4};
backend-es/test/snapshots-out/Snapshot.BackendSemantics02.jsnew file+3
@@ -0,0 +1,3 @@
1+import * as $runtime from "../runtime.js";
2+const test1 = 97;
3+export {test1};
backend-es/test/snapshots/Snapshot.BackendSemantics01.pursnew file+15
@@ -0,0 +1,15 @@
1+module Snapshot.BackendSemantics01 where
2+
3+import Prelude
4+
5+test1 :: Int
6+test1 = top - 1
7+
8+test2 :: Int
9+test2 = bottom + 1
10+
11+test3 :: Char
12+test3 = top
13+
14+test4 :: Char
15+test4 = bottom
backend-es/test/snapshots/Snapshot.BackendSemantics02.pursnew file+6
@@ -0,0 +1,6 @@
1+module Snapshot.BackendSemantics02 where
2+
3+import Data.Char (toCharCode)
4+
5+test1 :: Int
6+test1 = toCharCode 'a'
src/PureScript/Backend/Optimizer/Builder.purs+4−1
@@ -30,10 +30,11 @@ import Node.Glob.Basic (expandGlobs)
3030import Node.Path (FilePath)
3131import PureScript.Backend.Optimizer.Analysis (BackendAnalysis)
3232import PureScript.Backend.Optimizer.Convert (BackendModule, toBackendModule)
33−import PureScript.Backend.Optimizer.Semantics (EvalRef, ExternImpl, InlineDirective)
3433import PureScript.Backend.Optimizer.CoreFn (Ann, Ident, Module(..), Qualified)
3534import PureScript.Backend.Optimizer.CoreFn.Json (decodeModule)
3635import PureScript.Backend.Optimizer.CoreFn.Sort (sortModules)
36+import PureScript.Backend.Optimizer.Semantics (EvalRef, ExternImpl, InlineDirective)
37+import PureScript.Backend.Optimizer.Semantics.Foreign (ForeignEval)
3738
3839type BuildEnv =
3940 { implementations :: Map (Qualified Ident) (Tuple BackendAnalysis ExternImpl)
@@ -43,6 +44,7 @@ type BuildEnv =
4344
4445type BuildOptions =
4546 { directives :: Map EvalRef InlineDirective
47+ , foreignSemantics :: Map (Qualified Ident) ForeignEval
4648 , onPrepareModule :: BuildEnv -> Module Ann -> Aff (Module Ann)
4749 , onCodegenModule :: BuildEnv -> Module Ann -> BackendModule -> Aff Unit
4850 }
@@ -82,6 +84,7 @@ buildModules options coreFnModules =
8284 , moduleImplementations: Map.empty
8385 , directives
8486 , dataTypes: Map.empty
87+ , foreignSemantics: options.foreignSemantics
8588 , rewriteLimit: 10_000
8689 }
8790 newImplementations =
src/PureScript/Backend/Optimizer/Builder/Cli.purs+4−1
@@ -16,11 +16,12 @@ import Node.Path (FilePath)
1616import Node.Process as Process
1717import PureScript.Backend.Optimizer.Builder (BuildEnv, buildModules, coreFnModulesFromOutput)
1818import PureScript.Backend.Optimizer.Convert (BackendModule)
19+import PureScript.Backend.Optimizer.CoreFn (Ann, Ident, Module, Qualified)
1920import PureScript.Backend.Optimizer.Directives (parseDirectiveFile)
2021import PureScript.Backend.Optimizer.Directives.Defaults as Defaults
2122import PureScript.Backend.Optimizer.Semantics (EvalRef, InlineDirective)
23+import PureScript.Backend.Optimizer.Semantics.Foreign (ForeignEval)
2224import PureScript.CST.Errors (printParseError)
23−import PureScript.Backend.Optimizer.CoreFn (Ann, Module)
2425
2526externalDirectivesFromFile :: FilePath -> Aff (Map EvalRef InlineDirective)
2627externalDirectivesFromFile filePath = do
@@ -35,6 +36,7 @@ externalDirectivesFromFile filePath = do
3536basicBuildMain
3637 :: { resolveCoreFnDirectory :: Aff FilePath
3738 , resolveExternalDirectives :: Aff (Map EvalRef InlineDirective)
39+ , foreignSemantics :: Map (Qualified Ident) ForeignEval
3840 , onCodegenBefore :: Aff Unit
3941 , onCodegenAfter :: Aff Unit
4042 , onCodegenModule :: BuildEnv -> Module Ann -> BackendModule -> Aff Unit
@@ -57,6 +59,7 @@ basicBuildMain options = do
5759 options.onCodegenBefore
5860 coreFnModules # buildModules
5961 { directives: allDirectives
62+ , foreignSemantics: options.foreignSemantics
6063 , onCodegenModule: options.onCodegenModule
6164 , onPrepareModule: options.onPrepareModule
6265 }
src/PureScript/Backend/Optimizer/Convert.purs+3−2
@@ -73,7 +73,7 @@ import PureScript.Backend.Optimizer.Analysis (BackendAnalysis)
7373import 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)
7474import PureScript.Backend.Optimizer.Directives (DirectiveHeaderResult, parseDirectiveHeader)
7575import PureScript.Backend.Optimizer.Semantics (BackendExpr(..), BackendSemantics, Ctx, DataTypeMeta, Env(..), EvalRef(..), ExternImpl(..), ExternSpine, InlineDirective(..), NeutralExpr(..), build, evalExternFromImpl, freeze, optimize)
76−import PureScript.Backend.Optimizer.Semantics.Foreign (coreForeignSemantics)
76+import PureScript.Backend.Optimizer.Semantics.Foreign (ForeignEval)
7777import PureScript.Backend.Optimizer.Syntax (BackendAccessor(..), BackendOperator(..), BackendOperator1(..), BackendOperator2(..), BackendOperatorOrd(..), BackendSyntax(..), Level(..), Pair(..))
7878import PureScript.Backend.Optimizer.Utils (foldl1Array)
7979import Safe.Coerce (coerce)
@@ -106,6 +106,7 @@ type ConvertEnv =
106106 , implementations :: BackendImplementations
107107 , moduleImplementations :: BackendImplementations
108108 , directives :: Map EvalRef InlineDirective
109+ , foreignSemantics :: Map (Qualified Ident) ForeignEval
109110 , rewriteLimit :: Int
110111 }
111112
@@ -270,7 +271,7 @@ makeExternEval :: ConvertEnv -> Env -> Qualified Ident -> Array ExternSpine -> M
270271makeExternEval conv env qual spine = do
271272 let
272273 result = do
273− fn <- Map.lookup qual coreForeignSemantics
274+ fn <- Map.lookup qual conv.foreignSemantics
274275 fn env qual spine
275276 case result of
276277 Nothing -> do