git.iko.soy/rowtype-yoga/purescript-backend-optimizer
on main help sign in
Optimize duplicate branches in Semantics and Codegen (#87)

Fixes #79

commit8fa16b529279f94f82bda5134ce0397db9aa423f authorNathan Faubion <natefaubion@users.noreply.github.com> dateSat 12 Aug 2023, 11:22 (3 years ago) parent99e8250
23 files changed+256−373
backend-es/package.json+1−1 backend-es/src/PureScript/Backend/Optimizer/Codegen/EcmaScript/Syntax.purs+60−9 backend-es/test/Main.purs+1−1 backend-es/test/snapshots-out/Snapshot.BranchSpecialization01.js+1−2 backend-es/test/snapshots-out/Snapshot.CaseArray.js+1−4 backend-es/test/snapshots-out/Snapshot.CaseGrafting.js+1−6 backend-es/test/snapshots-out/Snapshot.CaseGuarded.js+5−38 backend-es/test/snapshots-out/Snapshot.CaseHeuristics.js+9−48 backend-es/test/snapshots-out/Snapshot.CaseJacobs.js−1 backend-es/test/snapshots-out/Snapshot.CaseLeafTco.js+8−20 backend-es/test/snapshots-out/Snapshot.CaseProduct.js+2−12 backend-es/test/snapshots-out/Snapshot.CaseRecord.js+4−12 backend-es/test/snapshots-out/Snapshot.CaseRedBlackTree.js+27−115 backend-es/test/snapshots-out/Snapshot.EsPrecedence01.js+1−1 backend-es/test/snapshots-out/Snapshot.EsSharedElse.js+6 backend-es/test/snapshots-out/Snapshot.InlineArrayIndex.js+1−5 backend-es/test/snapshots-out/Snapshot.KnownConstructors06.js−2 backend-es/test/snapshots/Snapshot.EsSharedElse.purs+15 src/PureScript/Backend/Optimizer/CoreFn.purs+3−1 src/PureScript/Backend/Optimizer/Semantics.purs+99−91 src/PureScript/Backend/Optimizer/Syntax.purs+7 src/PureScript/Backend/Optimizer/Tracer/Printer.purs+3−3 src/PureScript/Backend/Optimizer/Utils.purs+1−1
backend-es/package.json+1−1
@@ -19,7 +19,7 @@
1919 "postversion": "git commit -m \"Bump purs-backend-es version ($npm_package_version)\" && git tag purs-backend-es-v$npm_package_version",
2020 "test": "cd .. && spago test",
2121 "test:output-diff": "npm run build && cd ../ && ./backend-es/index.js build --timing --int-tags --output-dir=output-es2 && diff -r output-es output-es2 && rm -r output-es2",
22− "build": "cd .. && spago build -u '-g corefn,js' && node ./backend-es/index.dev.js bundle-module -m Main --timing --int-tags --platform=node --minify --to=./backend-es/bundle/index.js",
22+ "build": "cd .. && spago build -u '-g corefn' && node ./backend-es/index.dev.js bundle-module -m Main --timing --int-tags --platform=node --minify --to=./backend-es/bundle/index.js",
2323 "prepublishOnly": "rm -rf bundle ../output ../output-es && npm run build"
2424 },
2525 "repository": {
backend-es/src/PureScript/Backend/Optimizer/Codegen/EcmaScript/Syntax.purs+60−9
@@ -48,6 +48,7 @@ import Data.Traversable (traverse)
4848import Data.Tuple (Tuple(..), fst, snd)
4949import Dodo as Dodo
5050import Dodo.Common as Dodo.Common
51+import Partial.Unsafe (unsafePartial)
5152import PureScript.Backend.Optimizer.Codegen.EcmaScript.Common (esAccessor, esApp, esAssign, esBoolean, esEscapeIdent, esEscapeProp, esEscapeSpecial, esIndex, esInt, esModuleName, esNumber, esString, esTernary)
5253import PureScript.Backend.Optimizer.CoreFn (Ident(..), ModuleName(..), Qualified(..))
5354
@@ -92,6 +93,7 @@ data EsSyntax a
9293 | EsContinue
9394 | EsUndefined
9495
96+derive instance Eq a => Eq (EsSyntax a)
9597derive instance Functor EsSyntax
9698
9799instance Foldable EsSyntax where
@@ -128,6 +130,7 @@ data EsArrayElement a
128130 = EsArrayValue a
129131 | EsArraySpread a
130132
133+derive instance Eq a => Eq (EsArrayElement a)
131134derive instance Functor EsArrayElement
132135
133136instance Foldable EsArrayElement where
@@ -142,10 +145,13 @@ data EsObjectElement a
142145 | EsObjectField String a
143146 | EsObjectSpread a
144147
148+derive instance Eq a => Eq (EsObjectElement a)
145149derive instance Functor EsObjectElement
146150
147151data EsBindingPattern = EsBindingIdent EsIdent
148152
153+derive instance Eq EsBindingPattern
154+
149155instance Foldable EsObjectElement where
150156 foldr a = foldrDefault a
151157 foldl a = foldlDefault a
@@ -174,12 +180,16 @@ data EsBinaryOp
174180 | EsEquals
175181 | EsNotEquals
176182
183+derive instance Eq EsBinaryOp
184+
177185data EsUnaryOp
178186 = EsNot
179187 | EsNegate
180188 | EsBitNegate
181189 | EsDelete
182190
191+derive instance Eq EsUnaryOp
192+
183193data EsPrec
184194 = EsPrecStatement
185195 | EsPrecControl
@@ -199,6 +209,7 @@ data EsRuntimeOp a
199209 | EsFail
200210 | EsIntDiv a a
201211
212+derive instance Eq a => Eq (EsRuntimeOp a)
202213derive instance Functor EsRuntimeOp
203214
204215instance Foldable EsRuntimeOp where
@@ -215,10 +226,15 @@ data EsExpr = EsExpr EsAnalysis (EsSyntax EsExpr)
215226instance HasSyntax EsExpr where
216227 syntaxOf (EsExpr _ syn) = syn
217228
229+instance Eq EsExpr where
230+ eq (EsExpr (EsAnalysis s1) a) (EsExpr (EsAnalysis s2) b) =
231+ s1.size == s2.size && a == b
232+
218233newtype EsAnalysis = EsAnalysis
219234 { deps :: Set ModuleName
220235 , runtime :: Boolean
221236 , pure :: Boolean
237+ , size :: Int
222238 }
223239
224240derive instance Newtype EsAnalysis _
@@ -228,10 +244,11 @@ instance Semigroup EsAnalysis where
228244 { deps: a.deps <> b.deps
229245 , runtime: a.runtime || b.runtime
230246 , pure: a.pure && b.pure
247+ , size: a.size + b.size
231248 }
232249
233250instance Monoid EsAnalysis where
234− mempty = EsAnalysis { deps: mempty, runtime: false, pure: true }
251+ mempty = EsAnalysis { deps: mempty, runtime: false, pure: true, size: 0 }
235252
236253needsDep :: ModuleName -> EsAnalysis -> EsAnalysis
237254needsDep mn (EsAnalysis a) = EsAnalysis a { deps = Set.insert mn a.deps }
@@ -245,15 +262,18 @@ notPure (EsAnalysis a) = EsAnalysis a { pure = false }
245262alwaysPure :: EsAnalysis -> EsAnalysis
246263alwaysPure (EsAnalysis a) = EsAnalysis a { pure = true }
247264
265+bumpSize :: EsAnalysis -> EsAnalysis
266+bumpSize (EsAnalysis a) = EsAnalysis a { size = a.size + 1 }
267+
248268esAnalysisOf :: EsExpr -> EsAnalysis
249269esAnalysisOf (EsExpr a _) = a
250270
251271build :: EsSyntax EsExpr -> EsExpr
252272build syn = case syn of
253273 EsIdent (Qualified (Just mn) _) ->
254− EsExpr (needsDep mn mempty) syn
274+ EsExpr (needsDep mn (bumpSize mempty)) syn
255275 EsRuntime op ->
256− EsExpr (needsRuntime (foldMap esAnalysisOf op)) syn
276+ EsExpr (needsRuntime (bumpSize (foldMap esAnalysisOf op))) syn
257277 EsCall (EsExpr _ (EsArrowFunction [] bs)) []
258278 | Just expr <- inlineCallBlock bs ->
259279 expr
@@ -270,7 +290,7 @@ build syn = case syn of
270290 esArrowFunction as bs
271291 EsArrowFunction as bs -> do
272292 let Tuple s bs' = buildStatements bs
273− EsExpr (alwaysPure s) $ EsArrowFunction as bs'
293+ EsExpr (alwaysPure (bumpSize s)) $ EsArrowFunction as bs'
274294 EsIfElse a [ block ] cs
275295 | Just bs <- inlineReturnBlock block ->
276296 build $ EsIfElse a bs cs
@@ -280,21 +300,21 @@ build syn = case syn of
280300 EsIfElse a bs cs -> do
281301 let Tuple s1 bs' = buildStatements bs
282302 let Tuple s2 cs' = buildStatements cs
283− EsExpr (esAnalysisOf a <> s1 <> s2) $ EsIfElse a bs' cs'
303+ EsExpr (bumpSize (esAnalysisOf a <> s1 <> s2)) $ EsIfElse a bs' cs'
284304 EsWhile a bs
285305 | Just bs' <- removeTrailingContinue bs ->
286306 build $ EsWhile a bs'
287307 | otherwise -> do
288308 let Tuple s bs' = buildStatements bs
289− EsExpr (esAnalysisOf a <> s) $ EsWhile a bs'
309+ EsExpr (bumpSize (esAnalysisOf a <> s)) $ EsWhile a bs'
290310 EsForOf a b cs
291311 | Just cs' <- removeTrailingContinue cs ->
292312 build $ EsForOf a b cs'
293313 | otherwise -> do
294314 let Tuple s cs' = buildStatements cs
295− EsExpr (esAnalysisOf b <> s) $ EsForOf a b cs'
315+ EsExpr (bumpSize (esAnalysisOf b <> s)) $ EsForOf a b cs'
296316 _ ->
297− EsExpr (pureAnn (foldMap esAnalysisOf syn)) syn
317+ EsExpr (pureAnn (bumpSize (foldMap esAnalysisOf syn))) syn
298318 where
299319 pureAnn = case syn of
300320 EsAccess _ _ -> notPure
@@ -306,7 +326,7 @@ build syn = case syn of
306326 _ -> identity
307327
308328buildStatements :: Array EsExpr -> Tuple EsAnalysis (Array EsExpr)
309−buildStatements = traverse go
329+buildStatements = traverse go <<< mergeBranchTails
310330 where
311331 go expr = case expr of
312332 _ | Just expr' <- inlineLoopBlockStatement expr ->
@@ -314,6 +334,37 @@ buildStatements = traverse go
314334 _ ->
315335 Tuple (esAnalysisOf expr) expr
316336
337+ -- For nested if/else blocks, merges a duplicate tail of alternatives
338+ -- via fallthrough. If an inner branch has a tail that matches the
339+ -- parent's tail, we can just delete the tail from the inner branch.
340+ -- We can't do this in BackendSyntax because it only has complete
341+ -- if/else, but ES can exploit imperative control-flow.
342+ mergeBranchTails :: Array EsExpr -> Array EsExpr
343+ mergeBranchTails as = do
344+ let
345+ len = Array.length as
346+ loop ix
347+ | ix == len = as
348+ | otherwise = case unsafePartial (Array.unsafeIndex as ix) of
349+ EsExpr _ (EsIfElse hd ds [])
350+ | Just ta <- Array.index as (ix + 1)
351+ , Just tb <- Array.findIndex (eq ta) ds
352+ , Array.drop (tb + 1) ds == Array.drop (ix + 2) as ->
353+ case Array.take tb ds of
354+ [] ->
355+ Array.take ix as <> Array.drop (ix + 1) as
356+ [ EsExpr _ (EsIfElse bb cc []) ] ->
357+ Array.take ix as
358+ <> [ build $ EsIfElse (build (EsBinary EsAnd hd bb)) cc [] ]
359+ <> Array.drop (ix + 1) as
360+ ds' ->
361+ Array.take ix as
362+ <> [ build $ EsIfElse hd ds' [] ]
363+ <> Array.drop (ix + 1) as
364+ _ ->
365+ loop (ix + 1)
366+ loop 0
367+
317368inlineReturnBlock :: EsExpr -> Maybe (Array EsExpr)
318369inlineReturnBlock (EsExpr _ expr) = case expr of
319370 EsReturn (Just (EsExpr _ (EsCall (EsExpr _ (EsArrowFunction [] bs)) []))) ->
backend-es/test/Main.purs+1−1
@@ -101,7 +101,7 @@ main = do
101101runSnapshotTests :: TestArgs -> Aff Unit
102102runSnapshotTests { accept, filter, traceIdents } = do
103103 liftEffect $ Process.chdir $ Path.concat [ "backend-es", "test", "snapshots" ]
104− spawnFromParent "spago" [ "build -u \"-g corefn\"" ]
104+ spawnFromParent "spago" [ "build -u \"-g corefn,js\"" ]
105105 snapshotDir <- liftEffect Process.cwd
106106 snapshotPaths <- expandGlobsCwd [ "Snapshot.*.purs" ]
107107 outputRef <- liftEffect $ Ref.new Map.empty
backend-es/test/snapshots-out/Snapshot.BranchSpecialization01.js+1−2
@@ -9,8 +9,7 @@ const eqTest = {
99 if (x === "Foo") { return y === "Foo"; }
1010 if (x === "Bar") { return y === "Bar"; }
1111 if (x === "Baz") { return y === "Baz"; }
12− if (x === "Qux") { return y === "Qux"; }
13− return false;
12+ return x === "Qux" && y === "Qux";
1413 }
1514};
1615const test1 = a => a === "Baz";
backend-es/test/snapshots-out/Snapshot.CaseArray.js+1−4
@@ -1,9 +1,6 @@
11const test1 = v => {
22 if (v.length === 0) { return "0"; }
3− if (v.length === 1) {
4− if (v[0] === 1) { return "1"; }
5− return "1";
6− }
3+ if (v.length === 1) { return "1"; }
74 if (v.length === 2) {
85 if (v[1] === 2) { return "2"; }
96 return "catch";
backend-es/test/snapshots-out/Snapshot.CaseGrafting.js+1−6
@@ -4,12 +4,7 @@ const test1 = v => v1 => v2 => {
44 if (v2) { return 1; }
55 return 3;
66 }
7− if (!v) {
8− if (v1) { return 2; }
9− if (!v2) { return 3; }
10− if (v2) { return 4; }
11− $runtime.fail();
12− }
7+ if (!v && v1) { return 2; }
138 if (!v2) { return 3; }
149 if (v2) { return 4; }
1510 $runtime.fail();
backend-es/test/snapshots-out/Snapshot.CaseGuarded.js+5−38
@@ -3,24 +3,8 @@ const $Product3 = (_1, _2, _3) => ({tag: "Product3", _1, _2, _3});
33const Product3 = value0 => value1 => value2 => $Product3(value0, value1, value2);
44const NInt = x => x;
55const test5 = v => {
6− if (v.tag === "Just") {
7− if (v._1.tag === "Right") { return v._1._1; }
8− if (v.tag === "Just") {
9− if (v._1.tag === "Left") {
10− if (v._1._1 === 2) { return 4; }
11− return 5;
12− }
13− return 5;
14− }
15− return 5;
16− }
17− if (v.tag === "Just") {
18− if (v._1.tag === "Left") {
19− if (v._1._1 === 2) { return 4; }
20− return 5;
21− }
22− return 5;
23− }
6+ if (v.tag === "Just" && v._1.tag === "Right") { return v._1._1; }
7+ if (v.tag === "Just" && v._1.tag === "Left" && v._1._1 === 2) { return 4; }
248 return 5;
259};
2610const test4 = v => v1 => {
@@ -40,7 +24,6 @@ const test4 = v => v1 => {
4024 if (v.c === v1.e) { return 7; }
4125 if (v.c < v1.e) { return 8; }
4226 if (v.c > v1.e) { return 9; }
43− return (11 + v.c | 0) + v1.f | 0;
4427 }
4528 return (11 + v.c | 0) + v1.f | 0;
4629 }
@@ -50,13 +33,7 @@ const test4 = v => v1 => {
5033 if (v.c > v1.e) { return 9; }
5134 return (11 + v.c | 0) + v1.f | 0;
5235 }
53− if (v.b === 2) {
54− if (v1.d === 1) {
55− if (v1.f === 10) { return 10; }
56− return (11 + v.c | 0) + v1.f | 0;
57− }
58− return (11 + v.c | 0) + v1.f | 0;
59− }
36+ if (v.b === 2 && v1.d === 1 && v1.f === 10) { return 10; }
6037 return (11 + v.c | 0) + v1.f | 0;
6138};
6239const test3 = v => {
@@ -73,18 +50,8 @@ const test2 = v => {
7350};
7451const test1 = v => {
7552 if (v < 1) { return "n: " + Data$dShow.showIntImpl(v); }
76− if (v > 1) {
77− if (v < 100) { return "1 < x < 100: " + Data$dShow.showIntImpl(v); }
78− if (v > 1) {
79− if (v < 50) { return "1 < x < 50: " + Data$dShow.showIntImpl(v); }
80− return "catch";
81− }
82− return "catch";
83− }
84− if (v > 1) {
85− if (v < 50) { return "1 < x < 50: " + Data$dShow.showIntImpl(v); }
86− return "catch";
87− }
53+ if (v > 1 && v < 100) { return "1 < x < 100: " + Data$dShow.showIntImpl(v); }
54+ if (v > 1 && v < 50) { return "1 < x < 50: " + Data$dShow.showIntImpl(v); }
8855 return "catch";
8956};
9057export {$Product3, NInt, Product3, test1, test2, test3, test4, test5};
backend-es/test/snapshots-out/Snapshot.CaseHeuristics.js+9−48
@@ -9,18 +9,11 @@ const testPBAN = v => v1 => {
99 if (v1._1 === 1) { return 1; }
1010 return 4;
1111 }
12− if (v._1 === 2) {
13− if (v1._1 === 2) { return 2; }
14− return 4;
15− }
16− return 4;
12+ if (v._1 === 2 && v1._1 === 2) { return 2; }
1713 }
1814 return 4;
1915 }
20− if (v.tag === "Two") {
21− if (v1.tag === "Two") { return 3; }
22− return 4;
23− }
16+ if (v.tag === "Two" && v1.tag === "Two") { return 3; }
2417 return 4;
2518};
2619const testPBA = v => v1 => {
@@ -30,53 +23,24 @@ const testPBA = v => v1 => {
3023 if (v1._1 === 1) { return 1; }
3124 return 4;
3225 }
33− if (v._1 === 2) {
34− if (v1._1 === 2) { return 2; }
35− return 4;
36− }
37− return 4;
38− }
39− return 4;
40− }
41− if (v.tag === "Two") {
42− if (v._1 === 1) {
43− if (v1.tag === "Two") { return 3; }
44− return 4;
26+ if (v._1 === 2 && v1._1 === 2) { return 2; }
4527 }
4628 return 4;
4729 }
30+ if (v.tag === "Two" && v._1 === 1 && v1.tag === "Two") { return 3; }
4831 return 4;
4932};
5033const testPB = v => v1 => {
5134 if (v.tag === "One") {
52− if (v._1 === 1) {
53− if (v1.tag === "One") {
54− if (v1._1 === 1) { return 1; }
55− return 4;
56− }
57− if (v1.tag === "Zero") { return 3; }
35+ if (v._1 === 1 && v1.tag === "One") {
36+ if (v1._1 === 1) { return 1; }
5837 return 4;
5938 }
6039 if (v1.tag === "Zero") { return 3; }
6140 return 4;
6241 }
63− if (v.tag === "Two") {
64− if (v._1 === 2) {
65− if (v._2 === 3) {
66− if (v1.tag === "Two") {
67− if (v1._1 === 2) {
68− if (v1._2 === 3) { return 2; }
69− return 4;
70− }
71− return 4;
72− }
73− if (v1.tag === "Zero") { return 3; }
74− return 4;
75− }
76− if (v1.tag === "Zero") { return 3; }
77− return 4;
78− }
79− if (v1.tag === "Zero") { return 3; }
42+ if (v.tag === "Two" && v._1 === 2 && v._2 === 3 && v1.tag === "Two") {
43+ if (v1._1 === 2 && v1._2 === 3) { return 2; }
8044 return 4;
8145 }
8246 if (v1.tag === "Zero") { return 3; }
@@ -94,10 +58,7 @@ const testP = v => v1 => v2 => {
9458 if (v2 === 3) { return 3; }
9559 return 5;
9660 }
97− if (v === 1) {
98− if (v2 === 4) { return 4; }
99− return 5;
100− }
61+ if (v === 1 && v2 === 4) { return 4; }
10162 return 5;
10263};
10364export {$Column, One, Two, Zero, testP, testPB, testPBA, testPBAN};
backend-es/test/snapshots-out/Snapshot.CaseJacobs.js−1
@@ -27,7 +27,6 @@ const test1 = v => {
2727 if (v._1.tag === "Zero") { return "e2: " + showExpr.show(v._2); }
2828 if (v._2.tag === "Zero") { return "e4: " + showExpr.show(v._1); }
2929 if (v._1.tag === "Add") { return "e5: " + showExpr.show(v._1._1) + " " + showExpr.show(v._1._2) + " " + showExpr.show(v._2); }
30− return "e7: " + showExpr.show(v);
3130 }
3231 return "e7: " + showExpr.show(v);
3332};
backend-es/test/snapshots-out/Snapshot.CaseLeafTco.js+8−20
@@ -6,26 +6,19 @@ const test1 = test1$a0$copy => test1$a1$copy => {
66 const b = test1$a0, arr = test1$a1;
77 const v = Data$dArray.last(arr);
88 if (0 < arr.length) {
9− const $0 = (x, y) => {
9+ if (v.tag === "Just") {
10+ if (v._1 === 2 && arr[0] === 1) {
11+ test1$c = false;
12+ test1$r = arr;
13+ continue;
14+ }
1015 if (b) {
1116 test1$c = false;
1217 test1$r = [];
13− return;
14− }
15− test1$a0 = b;
16− test1$a1 = [y, x, 3, y, 5, 6, 7, 8, 9, 10, x, 12, 13, 14, 15, 16, 17, ...arr];
17− };
18− if (v.tag === "Just") {
19− if (v._1 === 2) {
20− if (arr[0] === 1) {
21− test1$c = false;
22− test1$r = arr;
23− continue;
24− }
25− $0(arr[0], v._1);
2618 continue;
2719 }
28− $0(arr[0], v._1);
20+ test1$a0 = b;
21+ test1$a1 = [v._1, arr[0], 3, v._1, 5, 6, 7, 8, 9, 10, arr[0], 12, 13, 14, 15, 16, 17, ...arr];
2922 continue;
3023 }
3124 if (v.tag === "Nothing") {
@@ -36,11 +29,6 @@ const test1 = test1$a0$copy => test1$a1$copy => {
3629 $runtime.fail();
3730 }
3831 if (v.tag === "Just") {
39− if (v._1 === 2) {
40− test1$c = false;
41− test1$r = [...arr, v._1];
42− continue;
43− }
4432 test1$c = false;
4533 test1$r = [...arr, v._1];
4634 continue;
backend-es/test/snapshots-out/Snapshot.CaseProduct.js+2−12
@@ -2,22 +2,12 @@ const $Product3 = (_1, _2, _3) => ({tag: "Product3", _1, _2, _3});
22const Product3 = value0 => value1 => value2 => $Product3(value0, value1, value2);
33const test1 = v => {
44 if (v._1 === 1) {
5− if (v._3 === 3) {
6− if (v._2 === 2) { return "1"; }
7− if (v._2 === 4) { return "2"; }
8− return "catch";
9− }
5+ if (v._3 === 3 && v._2 === 2) { return "1"; }
106 if (v._2 === 4) { return "2"; }
117 return "catch";
128 }
139 if (v._2 === 4) { return "2"; }
14− if (v._1 === 4) {
15− if (v._2 === 5) {
16− if (v._3 === 6) { return "3"; }
17− return "catch";
18− }
19− return "catch";
20− }
10+ if (v._1 === 4 && v._2 === 5 && v._3 === 6) { return "3"; }
2111 return "catch";
2212};
2313export {$Product3, Product3, test1};
backend-es/test/snapshots-out/Snapshot.CaseRecord.js+4−12
@@ -20,16 +20,11 @@ const test3 = v => {
2020};
2121const test2 = v => {
2222 if (v.a.c === 2) {
23− if (v.d.e === 1) {
24− if (v.d.f === 2) {
25− if (v.a.b === 1) { return 1; }
26− return 2;
27− }
28− if (v.a.b === 1) { return 3; }
29− return 4;
23+ if (v.d.e === 1 && v.d.f === 2) {
24+ if (v.a.b === 1) { return 1; }
25+ return 2;
3026 }
3127 if (v.a.b === 1) { return 3; }
32− return 4;
3328 }
3429 return 4;
3530};
@@ -37,10 +32,7 @@ const test1 = v => {
3732 if (v.a === 1) { return "0"; }
3833 if (v.b === 1) { return "1"; }
3934 if (v.c === 1) { return "2"; }
40− if (v.a === 2) {
41− if (v.b === 2) { return "3"; }
42− return "catch";
43− }
35+ if (v.a === 2 && v.b === 2) { return "3"; }
4436 return "catch";
4537};
4638export {test1, test2, test3, test4, test5, test6};
backend-es/test/snapshots-out/Snapshot.CaseRedBlackTree.js+27−115
@@ -6,137 +6,49 @@ const Black = /* #__PURE__ */ $Color("Black");
66const Leaf = /* #__PURE__ */ $RedBlackTree("Leaf");
77const Node = value0 => value1 => value2 => value3 => $RedBlackTree("Node", value0, value1, value2, value3);
88const test1 = () => v => {
9− if (v.tag === "Node") {
10− if (v._1 === "Black") {
11− if (v._2.tag === "Node") {
12− if (v._2._1 === "Red") {
13− if (v._2._2.tag === "Node") {
14− if (v._2._2._1 === "Red") { return {i: 1, a: v._2._2._2, x: v._2._2._3, b: v._2._2._4, y: v._2._3, c: v._2._4, z: v._3, d: v._4}; }
15− if (v._2._4.tag === "Node") {
16− if (v._2._4._1 === "Red") { return {i: 2, a: v._2._2, x: v._2._3, b: v._2._4._2, y: v._2._4._3, c: v._2._4._4, z: v._3, d: v._4}; }
17− if (v._4.tag === "Node") {
18− if (v._4._1 === "Red") {
19− if (v._4._2.tag === "Node") {
20− if (v._4._2._1 === "Red") { return {i: 3, a: v._2, x: v._3, b: v._4._2._2, y: v._4._2._3, c: v._4._2._4, z: v._4._3, d: v._4._4}; }
21− if (v._4._4.tag === "Node") {
22− if (v._4._4._1 === "Red") { return {i: 4, a: v._2, x: v._3, b: v._4._2, y: v._4._3, c: v._4._4._2, z: v._4._4._3, d: v._4._4._4}; }
23− $runtime.fail();
24− }
25− $runtime.fail();
26− }
27− if (v._4._4.tag === "Node") {
28− if (v._4._4._1 === "Red") { return {i: 4, a: v._2, x: v._3, b: v._4._2, y: v._4._3, c: v._4._4._2, z: v._4._4._3, d: v._4._4._4}; }
29− $runtime.fail();
30− }
31− $runtime.fail();
32− }
33− $runtime.fail();
34− }
35− $runtime.fail();
36− }
37− if (v._4.tag === "Node") {
38− if (v._4._1 === "Red") {
39− if (v._4._2.tag === "Node") {
40− if (v._4._2._1 === "Red") { return {i: 3, a: v._2, x: v._3, b: v._4._2._2, y: v._4._2._3, c: v._4._2._4, z: v._4._3, d: v._4._4}; }
41− if (v._4._4.tag === "Node") {
42− if (v._4._4._1 === "Red") { return {i: 4, a: v._2, x: v._3, b: v._4._2, y: v._4._3, c: v._4._4._2, z: v._4._4._3, d: v._4._4._4}; }
43− $runtime.fail();
44− }
45− $runtime.fail();
46− }
47− if (v._4._4.tag === "Node") {
48− if (v._4._4._1 === "Red") { return {i: 4, a: v._2, x: v._3, b: v._4._2, y: v._4._3, c: v._4._4._2, z: v._4._4._3, d: v._4._4._4}; }
49− $runtime.fail();
50− }
51− $runtime.fail();
52− }
53− $runtime.fail();
54− }
55− $runtime.fail();
56− }
9+ if (v.tag === "Node" && v._1 === "Black") {
10+ if (v._2.tag === "Node") {
11+ if (v._2._1 === "Red") {
12+ if (v._2._2.tag === "Node") {
13+ if (v._2._2._1 === "Red") { return {i: 1, a: v._2._2._2, x: v._2._2._3, b: v._2._2._4, y: v._2._3, c: v._2._4, z: v._3, d: v._4}; }
5714 if (v._2._4.tag === "Node") {
5815 if (v._2._4._1 === "Red") { return {i: 2, a: v._2._2, x: v._2._3, b: v._2._4._2, y: v._2._4._3, c: v._2._4._4, z: v._3, d: v._4}; }
59− if (v._4.tag === "Node") {
60− if (v._4._1 === "Red") {
61− if (v._4._2.tag === "Node") {
62− if (v._4._2._1 === "Red") { return {i: 3, a: v._2, x: v._3, b: v._4._2._2, y: v._4._2._3, c: v._4._2._4, z: v._4._3, d: v._4._4}; }
63− if (v._4._4.tag === "Node") {
64− if (v._4._4._1 === "Red") { return {i: 4, a: v._2, x: v._3, b: v._4._2, y: v._4._3, c: v._4._4._2, z: v._4._4._3, d: v._4._4._4}; }
65− $runtime.fail();
66− }
67− $runtime.fail();
68− }
69− if (v._4._4.tag === "Node") {
70− if (v._4._4._1 === "Red") { return {i: 4, a: v._2, x: v._3, b: v._4._2, y: v._4._3, c: v._4._4._2, z: v._4._4._3, d: v._4._4._4}; }
71− $runtime.fail();
72− }
73− $runtime.fail();
74− }
75− $runtime.fail();
16+ if (v._4.tag === "Node" && v._4._1 === "Red") {
17+ if (v._4._2.tag === "Node" && v._4._2._1 === "Red") { return {i: 3, a: v._2, x: v._3, b: v._4._2._2, y: v._4._2._3, c: v._4._2._4, z: v._4._3, d: v._4._4}; }
18+ if (v._4._4.tag === "Node" && v._4._4._1 === "Red") { return {i: 4, a: v._2, x: v._3, b: v._4._2, y: v._4._3, c: v._4._4._2, z: v._4._4._3, d: v._4._4._4}; }
7619 }
7720 $runtime.fail();
7821 }
79− if (v._4.tag === "Node") {
80− if (v._4._1 === "Red") {
81− if (v._4._2.tag === "Node") {
82− if (v._4._2._1 === "Red") { return {i: 3, a: v._2, x: v._3, b: v._4._2._2, y: v._4._2._3, c: v._4._2._4, z: v._4._3, d: v._4._4}; }
83− if (v._4._4.tag === "Node") {
84− if (v._4._4._1 === "Red") { return {i: 4, a: v._2, x: v._3, b: v._4._2, y: v._4._3, c: v._4._4._2, z: v._4._4._3, d: v._4._4._4}; }
85− $runtime.fail();
86− }
87− $runtime.fail();
88− }
89− if (v._4._4.tag === "Node") {
90− if (v._4._4._1 === "Red") { return {i: 4, a: v._2, x: v._3, b: v._4._2, y: v._4._3, c: v._4._4._2, z: v._4._4._3, d: v._4._4._4}; }
91− $runtime.fail();
92− }
93− $runtime.fail();
94− }
95− $runtime.fail();
22+ if (v._4.tag === "Node" && v._4._1 === "Red") {
23+ if (v._4._2.tag === "Node" && v._4._2._1 === "Red") { return {i: 3, a: v._2, x: v._3, b: v._4._2._2, y: v._4._2._3, c: v._4._2._4, z: v._4._3, d: v._4._4}; }
24+ if (v._4._4.tag === "Node" && v._4._4._1 === "Red") { return {i: 4, a: v._2, x: v._3, b: v._4._2, y: v._4._3, c: v._4._4._2, z: v._4._4._3, d: v._4._4._4}; }
9625 }
9726 $runtime.fail();
9827 }
99− if (v._4.tag === "Node") {
100− if (v._4._1 === "Red") {
101− if (v._4._2.tag === "Node") {
102− if (v._4._2._1 === "Red") { return {i: 3, a: v._2, x: v._3, b: v._4._2._2, y: v._4._2._3, c: v._4._2._4, z: v._4._3, d: v._4._4}; }
103− if (v._4._4.tag === "Node") {
104− if (v._4._4._1 === "Red") { return {i: 4, a: v._2, x: v._3, b: v._4._2, y: v._4._3, c: v._4._4._2, z: v._4._4._3, d: v._4._4._4}; }
105− $runtime.fail();
106− }
107− $runtime.fail();
108− }
109− if (v._4._4.tag === "Node") {
110− if (v._4._4._1 === "Red") { return {i: 4, a: v._2, x: v._3, b: v._4._2, y: v._4._3, c: v._4._4._2, z: v._4._4._3, d: v._4._4._4}; }
111− $runtime.fail();
112− }
113− $runtime.fail();
28+ if (v._2._4.tag === "Node") {
29+ if (v._2._4._1 === "Red") { return {i: 2, a: v._2._2, x: v._2._3, b: v._2._4._2, y: v._2._4._3, c: v._2._4._4, z: v._3, d: v._4}; }
30+ if (v._4.tag === "Node" && v._4._1 === "Red") {
31+ if (v._4._2.tag === "Node" && v._4._2._1 === "Red") { return {i: 3, a: v._2, x: v._3, b: v._4._2._2, y: v._4._2._3, c: v._4._2._4, z: v._4._3, d: v._4._4}; }
32+ if (v._4._4.tag === "Node" && v._4._4._1 === "Red") { return {i: 4, a: v._2, x: v._3, b: v._4._2, y: v._4._3, c: v._4._4._2, z: v._4._4._3, d: v._4._4._4}; }
11433 }
11534 $runtime.fail();
11635 }
117− $runtime.fail();
118− }
119− if (v._4.tag === "Node") {
120− if (v._4._1 === "Red") {
121− if (v._4._2.tag === "Node") {
122− if (v._4._2._1 === "Red") { return {i: 3, a: v._2, x: v._3, b: v._4._2._2, y: v._4._2._3, c: v._4._2._4, z: v._4._3, d: v._4._4}; }
123− if (v._4._4.tag === "Node") {
124− if (v._4._4._1 === "Red") { return {i: 4, a: v._2, x: v._3, b: v._4._2, y: v._4._3, c: v._4._4._2, z: v._4._4._3, d: v._4._4._4}; }
125− $runtime.fail();
126− }
127− $runtime.fail();
128− }
129− if (v._4._4.tag === "Node") {
130− if (v._4._4._1 === "Red") { return {i: 4, a: v._2, x: v._3, b: v._4._2, y: v._4._3, c: v._4._4._2, z: v._4._4._3, d: v._4._4._4}; }
131− $runtime.fail();
132− }
133− $runtime.fail();
36+ if (v._4.tag === "Node" && v._4._1 === "Red") {
37+ if (v._4._2.tag === "Node" && v._4._2._1 === "Red") { return {i: 3, a: v._2, x: v._3, b: v._4._2._2, y: v._4._2._3, c: v._4._2._4, z: v._4._3, d: v._4._4}; }
38+ if (v._4._4.tag === "Node" && v._4._4._1 === "Red") { return {i: 4, a: v._2, x: v._3, b: v._4._2, y: v._4._3, c: v._4._4._2, z: v._4._4._3, d: v._4._4._4}; }
13439 }
13540 $runtime.fail();
13641 }
42+ if (v._4.tag === "Node" && v._4._1 === "Red") {
43+ if (v._4._2.tag === "Node" && v._4._2._1 === "Red") { return {i: 3, a: v._2, x: v._3, b: v._4._2._2, y: v._4._2._3, c: v._4._2._4, z: v._4._3, d: v._4._4}; }
44+ if (v._4._4.tag === "Node" && v._4._4._1 === "Red") { return {i: 4, a: v._2, x: v._3, b: v._4._2, y: v._4._3, c: v._4._4._2, z: v._4._4._3, d: v._4._4._4}; }
45+ }
13746 $runtime.fail();
13847 }
139− $runtime.fail();
48+ if (v._4.tag === "Node" && v._4._1 === "Red") {
49+ if (v._4._2.tag === "Node" && v._4._2._1 === "Red") { return {i: 3, a: v._2, x: v._3, b: v._4._2._2, y: v._4._2._3, c: v._4._2._4, z: v._4._3, d: v._4._4}; }
50+ if (v._4._4.tag === "Node" && v._4._4._1 === "Red") { return {i: 4, a: v._2, x: v._3, b: v._4._2, y: v._4._3, c: v._4._4._2, z: v._4._4._3, d: v._4._4._4}; }
51+ }
14052 }
14153 $runtime.fail();
14254};
backend-es/test/snapshots-out/Snapshot.EsPrecedence01.js+1−1
@@ -1,5 +1,5 @@
11const test1 = f => a => b => {
2− if ((f(a) ? f(b) : false) ? f(a) : true) { return f(a); }
2+ if (f(a) && f(b) ? f(a) : true) { return f(a); }
33 return f();
44};
55export {test1};
backend-es/test/snapshots-out/Snapshot.EsSharedElse.jsnew file+6
@@ -0,0 +1,6 @@
1+const test1 = a => b => c => {
2+ if (a && b) { return 1; }
3+ if (c) { return 2; }
4+ return 3;
5+};
6+export {test1};
backend-es/test/snapshots-out/Snapshot.InlineArrayIndex.js+1−5
@@ -6,11 +6,7 @@ import * as Data$dShow from "../Data.Show/index.js";
66const assertEqual = /* #__PURE__ */ Assert.assertEqual({
77 eq: x => y => {
88 if (x.tag === "Nothing") { return y.tag === "Nothing"; }
9− if (x.tag === "Just") {
10− if (y.tag === "Just") { return x._1 === y._1; }
11− return false;
12− }
13− return false;
9+ return x.tag === "Just" && y.tag === "Just" && x._1 === y._1;
1410 }
1511})({
1612 show: v => {
backend-es/test/snapshots-out/Snapshot.KnownConstructors06.js−2
@@ -15,9 +15,7 @@ const genericTest = {
1515 if (x._1.tag === "Inr") {
1616 if (x._1._1.tag === "Inl") { return Baz; }
1717 if (x._1._1.tag === "Inr") { return Qux; }
18− $runtime.fail();
1918 }
20− $runtime.fail();
2119 }
2220 $runtime.fail();
2321 },
backend-es/test/snapshots/Snapshot.EsSharedElse.pursnew file+15
@@ -0,0 +1,15 @@
1+module Snapshot.EsSharedElse where
2+
3+test1 :: Boolean -> Boolean -> Boolean -> Int
4+test1 a b c =
5+ if a then
6+ if b then
7+ 1
8+ else if c then
9+ 2
10+ else
11+ 3
12+ else if c then
13+ 2
14+ else
15+ 3
src/PureScript/Backend/Optimizer/CoreFn.purs+3−1
@@ -142,6 +142,7 @@ derive instance functorGuard :: Functor Guard
142142
143143data Prop a = Prop String a
144144
145+derive instance Eq a => Eq (Prop a)
145146derive instance functorProp :: Functor Prop
146147
147148instance foldableProp :: Foldable Prop where
@@ -171,7 +172,8 @@ data Literal a
171172 | LitArray (Array a)
172173 | LitRecord (Array (Prop a))
173174
174−derive instance functorLiteral :: Functor Literal
175+derive instance Eq a => Eq (Literal a)
176+derive instance Functor Literal
175177
176178instance foldableLiteral :: Foldable Literal where
177179 foldl k = foldlDefault k
src/PureScript/Backend/Optimizer/Semantics.purs+99−91
@@ -67,7 +67,16 @@ data SemConditional a = SemConditional (Lazy a) (Lazy a)
6767
6868data BackendExpr
6969 = ExprSyntax BackendAnalysis (BackendSyntax BackendExpr)
70− | ExprRewrite BackendAnalysis BackendRewrite
70+ | ExprRewrite BackendAnalysis (BackendRewrite BackendExpr)
71+
72+instance Eq BackendExpr where
73+ eq = case _, _ of
74+ ExprSyntax (BackendAnalysis s1) x, ExprSyntax (BackendAnalysis s2) y ->
75+ s1.size == s2.size && x == y
76+ ExprRewrite (BackendAnalysis s1) x, ExprRewrite (BackendAnalysis s2) y ->
77+ s1.size == s2.size && x == y
78+ _, _ ->
79+ false
7180
7281type LetBindingAssoc a =
7382 { ident :: Maybe Ident
@@ -82,27 +91,33 @@ type EffectBindingAssoc a =
8291 , pure :: Boolean
8392 }
8493
85−data BackendRewrite
86− = RewriteInline (Maybe Ident) Level BackendExpr BackendExpr
87− | RewriteUncurry (Maybe Ident) Level (NonEmptyArray (Tuple (Maybe Ident) Level)) BackendExpr BackendExpr
94+data BackendRewrite a
95+ = RewriteInline (Maybe Ident) Level a a
96+ | RewriteUncurry (Maybe Ident) Level (NonEmptyArray (Tuple (Maybe Ident) Level)) a a
8897 | RewriteStop (Qualified Ident)
89− | RewriteUnpackOp (Maybe Ident) Level UnpackOp BackendExpr
90− | RewriteDistBranchesLet (Maybe Ident) Level (NonEmptyArray (Pair BackendExpr)) BackendExpr BackendExpr
91− | RewriteDistBranchesOp (NonEmptyArray (Pair BackendExpr)) BackendExpr DistOp
92−
93−data UnpackOp
94− = UnpackRecord (Array (Prop BackendExpr))
95− | UnpackUpdate BackendExpr (Array (Prop BackendExpr))
96− | UnpackArray (Array BackendExpr)
97− | UnpackData (Qualified Ident) ConstructorType ProperName Ident (Array (Tuple String BackendExpr))
98−
99−data DistOp
100− = DistApp (NonEmptyArray BackendExpr)
101− | DistUncurriedApp (Array BackendExpr)
98+ | RewriteUnpackOp (Maybe Ident) Level (UnpackOp a) a
99+ | RewriteDistBranchesLet (Maybe Ident) Level (NonEmptyArray (Pair a)) a a
100+ | RewriteDistBranchesOp (NonEmptyArray (Pair a)) a (DistOp a)
101+
102+derive instance Eq a => Eq (BackendRewrite a)
103+
104+data UnpackOp a
105+ = UnpackRecord (Array (Prop a))
106+ | UnpackUpdate a (Array (Prop a))
107+ | UnpackArray (Array a)
108+ | UnpackData (Qualified Ident) ConstructorType ProperName Ident (Array (Tuple String a))
109+
110+derive instance Eq a => Eq (UnpackOp a)
111+
112+data DistOp a
113+ = DistApp (NonEmptyArray a)
114+ | DistUncurriedApp (Array a)
102115 | DistAccessor BackendAccessor
103116 | DistPrimOp1 BackendOperator1
104− | DistPrimOp2L BackendOperator2 BackendExpr
105− | DistPrimOp2R BackendExpr BackendOperator2
117+ | DistPrimOp2L BackendOperator2 a
118+ | DistPrimOp2R a BackendOperator2
119+
120+derive instance Eq a => Eq (DistOp a)
106121
107122data ExternImpl
108123 = ExternExpr (Array (Qualified Ident)) NeutralExpr
@@ -1266,27 +1281,20 @@ build ctx = case _ of
12661281 Let ident level binding body
12671282 | shouldInlineLet level binding body ->
12681283 rewriteInline ident level binding body
1269− Let ident level binding body
1270− | Just expr' <- shouldUncurryAbs ident level binding body ->
1271− expr'
1272− Let ident level binding body
1273− | Just expr' <- shouldUnpackRecord ident level binding body ->
1274− expr'
1275− Let ident level binding body
1276− | Just expr' <- shouldUnpackUpdate ident level binding body ->
1277− expr'
1278− Let ident level binding body
1279− | Just expr' <- shouldUnpackCtor ident level binding body ->
1280− expr'
1281− Let ident level binding body
1282− | Just expr' <- shouldUnpackArray ident level binding body ->
1283− expr'
1284− Let ident level binding body
1285− | Just expr' <- shouldDistributeBranches ident level binding body ->
1286− expr'
1287− Let _ level binding body
1288− | Just expr' <- shouldEtaReduce level binding body ->
1289− expr'
1284+ | Just expr <- shouldUncurryAbs ident level binding body ->
1285+ expr
1286+ | Just expr <- shouldUnpackRecord ident level binding body ->
1287+ expr
1288+ | Just expr <- shouldUnpackUpdate ident level binding body ->
1289+ expr
1290+ | Just expr <- shouldUnpackCtor ident level binding body ->
1291+ expr
1292+ | Just expr <- shouldUnpackArray ident level binding body ->
1293+ expr
1294+ | Just expr <- shouldDistributeBranches ident level binding body ->
1295+ expr
1296+ | Just expr <- shouldEtaReduce level binding body ->
1297+ expr
12901298 App (ExprSyntax analysis (Branch bs def)) tl
12911299 | Just expr' <- shouldDistributeBranchApps analysis bs def tl ->
12921300 expr'
@@ -1315,65 +1323,65 @@ build ctx = case _ of
13151323 binding
13161324 EffectDefer expr@(ExprSyntax _ (EffectDefer _)) ->
13171325 expr
1318− Branch pairs def | Just expr <- simplifyBranches ctx pairs def ->
1319− expr
13201326 PrimOp (Op1 OpBooleanNot (ExprSyntax _ (PrimOp (Op1 OpBooleanNot expr)))) ->
13211327 expr
13221328 expr ->
13231329 buildDefault ctx expr
13241330
13251331buildBranchCond :: Ctx -> Pair BackendExpr -> BackendExpr -> BackendExpr
1326−buildBranchCond ctx (Pair a b) c = case b of
1327− ExprSyntax _ (Lit (LitBoolean true))
1328− | ExprSyntax _ (Lit (LitBoolean true)) <- c ->
1329− c
1330− | ExprSyntax _ (Lit (LitBoolean false)) <- c ->
1331− a
1332− | ExprSyntax _ x <- c, isBooleanTail x ->
1333− build ctx (PrimOp (Op2 OpBooleanOr a c))
1334− ExprSyntax _ (Lit (LitBoolean false))
1335− | ExprSyntax _ (Lit (LitBoolean false)) <- c ->
1336− c
1337− | ExprSyntax _ (PrimOp (Op1 (OpIsTag _) (ExprSyntax _ x1))) <- a
1338− , ExprSyntax _ (PrimOp (Op1 (OpIsTag _) (ExprSyntax _ x2))) <- c
1339− , isSameVariable x1 x2 ->
1340− c
1341− _ ->
1342− build ctx (Branch (NonEmptyArray.singleton (Pair a b)) c)
1343− where
1344− isSameVariable = case _, _ of
1345− Local _ l, Local _ r -> l == r
1346− Var l, Var r -> l == r
1347− _, _ -> false
1348−
1349−isBooleanTail :: forall a. BackendSyntax a -> Boolean
1350−isBooleanTail = case _ of
1351− Lit _ -> true
1352− Var _ -> true
1353− Local _ _ -> true
1354− PrimOp _ -> true
1355− _ -> false
1332+buildBranchCond ctx pair def
1333+ | Just expr <- simplifyCondIsTag ctx pair def =
1334+ expr
1335+ | Just expr <- simplifyCondBoolean ctx pair def =
1336+ expr
1337+ | Just expr <- simplifyCondLiftAnd ctx pair def =
1338+ expr
1339+ | Just expr <- simplifyCondRedundantElse ctx pair def =
1340+ expr
1341+ | ExprSyntax _ (Branch pairs def') <- def =
1342+ build ctx (Branch (NonEmptyArray.cons pair pairs) def')
1343+ | otherwise =
1344+ build ctx (Branch (NonEmptyArray.singleton pair) def)
1345+
1346+simplifyCondIsTag :: Ctx -> Pair BackendExpr -> BackendExpr -> Maybe BackendExpr
1347+simplifyCondIsTag _ = case _, _ of
1348+ Pair (ExprSyntax _ (PrimOp (Op1 (OpIsTag _) x1))) (ExprSyntax _ (Lit (LitBoolean false))), def@(ExprSyntax _ (PrimOp (Op1 (OpIsTag _) x2)))
1349+ | x1 == x2 ->
1350+ Just def
1351+ _, _ ->
1352+ Nothing
13561353
1357−simplifyBranches :: Ctx -> NonEmptyArray (Pair BackendExpr) -> BackendExpr -> Maybe BackendExpr
1358−simplifyBranches ctx pairs def = case NonEmptyArray.toArray pairs of
1359− [ a ]
1360− | Pair expr (ExprSyntax _ (Lit (LitBoolean true))) <- a
1361− , ExprSyntax _ (Lit (LitBoolean false)) <- def ->
1354+simplifyCondBoolean :: Ctx -> Pair BackendExpr -> BackendExpr -> Maybe BackendExpr
1355+simplifyCondBoolean ctx = case _, _ of
1356+ Pair expr body@(ExprSyntax _ (Lit (LitBoolean body'))), ExprSyntax _ (Lit (LitBoolean other))
1357+ | body' == other ->
1358+ Just body
1359+ | body' && not other ->
13621360 Just expr
1363− | Pair expr (ExprSyntax _ (Lit (LitBoolean false))) <- a
1364− , ExprSyntax _ (Lit (LitBoolean true)) <- def ->
1361+ | not body' && other ->
13651362 Just $ build ctx $ PrimOp (Op1 OpBooleanNot expr)
1366− [ a, b ]
1367− | Pair expr1@(ExprSyntax _ (Local _ lvl1)) body1 <- a
1368− , Pair (ExprSyntax _ (PrimOp (Op1 OpBooleanNot (ExprSyntax _ (Local _ lvl2))))) body2 <- b
1369− , ExprSyntax _ (Fail _) <- def
1370− , lvl1 == lvl2 ->
1371− Just $ build ctx $ Branch (NonEmptyArray.singleton (Pair expr1 body1)) body2
1372− _
1373− | ExprSyntax _ (Branch pairs2 def2) <- def ->
1374− Just $ build ctx (Branch (pairs <> pairs2) def2)
1375− | otherwise ->
1376− Nothing
1363+ Pair expr body, ExprSyntax _ (Lit (LitBoolean false)) ->
1364+ Just $ build ctx $ PrimOp (Op2 OpBooleanAnd expr body)
1365+ _, _ ->
1366+ Nothing
1367+
1368+simplifyCondRedundantElse :: Ctx -> Pair BackendExpr -> BackendExpr -> Maybe BackendExpr
1369+simplifyCondRedundantElse ctx = case _, _ of
1370+ Pair expr1 body1, ExprSyntax _ (Branch pairs _)
1371+ | Pair (ExprSyntax _ (PrimOp (Op1 OpBooleanNot expr2))) body2 <- NonEmptyArray.head pairs
1372+ , expr1 == expr2 ->
1373+ Just $ buildBranchCond ctx (Pair expr1 body1) body2
1374+ _, _ ->
1375+ Nothing
1376+
1377+simplifyCondLiftAnd :: Ctx -> Pair BackendExpr -> BackendExpr -> Maybe BackendExpr
1378+simplifyCondLiftAnd ctx pair def1 = case pair of
1379+ Pair x (ExprSyntax _ (Branch pairs def2))
1380+ | [ Pair y z ] <- NonEmptyArray.toArray pairs
1381+ , def1 == def2 ->
1382+ Just $ buildBranchCond ctx (Pair (build ctx (PrimOp (Op2 OpBooleanAnd x y))) z) def1
1383+ _ ->
1384+ Nothing
13771385
13781386buildStop :: Ctx -> Qualified Ident -> BackendExpr
13791387buildStop ctx stop = ExprRewrite (analyzeDefault ctx (Var stop)) (RewriteStop stop)
@@ -1642,7 +1650,7 @@ optimize traceSteps ctx env (Qualified mn (Ident id)) initN originalExpr =
16421650freeze :: BackendExpr -> Tuple BackendAnalysis NeutralExpr
16431651freeze init = Tuple (analysisOf init) $ foldBackendExpr NeutralExpr (\_ neutExpr -> neutExpr) init
16441652
1645−foldBackendExpr :: forall a. (BackendSyntax a -> a) -> (BackendRewrite -> a -> a) -> BackendExpr -> a
1653+foldBackendExpr :: forall a. (BackendSyntax a -> a) -> (BackendRewrite BackendExpr -> a -> a) -> BackendExpr -> a
16461654foldBackendExpr foldSyntax foldRewrite = go
16471655 where
16481656 go = case _ of
src/PureScript/Backend/Optimizer/Syntax.purs+7
@@ -34,6 +34,8 @@ data BackendSyntax a
3434 | PrimUndefined
3535 | Fail String
3636
37+derive instance Eq a => Eq (BackendSyntax a)
38+
3739newtype Level = Level Int
3840
3941derive newtype instance Eq Level
@@ -42,6 +44,8 @@ derive instance Newtype Level _
4244
4345data Pair a = Pair a a
4446
47+derive instance Eq a => Eq (Pair a)
48+
4549fstPair :: forall a. Pair a -> a
4650fstPair (Pair a _) = a
4751
@@ -60,6 +64,8 @@ data BackendOperator a
6064 = Op1 BackendOperator1 a
6165 | Op2 BackendOperator2 a a
6266
67+derive instance Eq a => Eq (BackendOperator a)
68+
6369data BackendOperator1
6470 = OpBooleanNot
6571 | OpIntBitNot
@@ -118,6 +124,7 @@ data BackendEffect a
118124 | EffectRefRead a
119125 | EffectRefWrite a a
120126
127+derive instance Eq a => Eq (BackendEffect a)
121128derive instance Functor BackendSyntax
122129
123130instance Foldable BackendSyntax where
src/PureScript/Backend/Optimizer/Tracer/Printer.purs+3−3
@@ -286,7 +286,7 @@ printBackendSyntax = case _ of
286286 Fail _ ->
287287 printUncurriedApp false (primOp "" "fail") []
288288
289−printBackendRewriteCase :: BackendRewrite -> Doc Void
289+printBackendRewriteCase :: forall a. BackendRewrite a -> Doc Void
290290printBackendRewriteCase = case _ of
291291 RewriteInline _ _ _ _ ->
292292 printRewrite "Inline"
@@ -301,7 +301,7 @@ printBackendRewriteCase = case _ of
301301 RewriteDistBranchesOp _ _ distOp ->
302302 printDistOpCase distOp
303303
304−printUnpackOpCase :: UnpackOp -> Doc Void
304+printUnpackOpCase :: forall a. UnpackOp a -> Doc Void
305305printUnpackOpCase = case _ of
306306 UnpackRecord _ ->
307307 printRewrite "UnpackRecord"
@@ -312,7 +312,7 @@ printUnpackOpCase = case _ of
312312 UnpackData _ _ _ _ _ ->
313313 printRewrite "UnpackData"
314314
315−printDistOpCase :: DistOp -> Doc Void
315+printDistOpCase :: forall a. DistOp a -> Doc Void
316316printDistOpCase = case _ of
317317 DistApp _ ->
318318 printRewrite "DistApp"
src/PureScript/Backend/Optimizer/Utils.purs+1−1
@@ -2,8 +2,8 @@ module PureScript.Backend.Optimizer.Utils where
22
33import Prelude
44
5−import Data.Array.NonEmpty (NonEmptyArray)
65import Data.Array.NonEmpty as NonEmptyArray
6+import Data.Array.NonEmpty.Internal (NonEmptyArray)
77import Partial.Unsafe (unsafePartial)
88
99foldl1Array :: forall a b. (b -> a -> b) -> (a -> b) -> NonEmptyArray a -> b