@samitouri / QOS-React-2 / commits / daaf95854c

[hir] Don't alias fields unless a mutation occurs after aliasing

A wise Joe once said, "We only care about observed aliasing". Instead of performing aliasing for fields and non fields together, split the analysis to happen over separate passes. Similarly split inferring mutable lifetimes pass for fields and non fields. Now, we can identify the aliases of fields that are *not* mutated and only alias the ones that do mutate. The algorithm is roughly as follows: 1. Build the set of aliases for non fields 2. Infer mutable ranges for all instructions except aliasing fields 3. Infer mutable ranges for all aliased instructions based on the alias set calculated in step 1 (this doesn't include aliasing fields) 4. Extend the set of aliases (calculated in step 1) to include fields only if the field or the receiver is mutated after the aliasing, ie, if _mutability is observed_. 5. Run infer mutable ranges again for all instructions including the fields that were aliased in the previous step. 6. Run infer mutable ranges for all aliased instructions including the fields that were aliased.

Sathya Gunasekaran committed Dec 8, 2022 at 23:29 UTC daaf95854c48f1b829af9bd81ff9f020a5dd28e3
28 files changed +914 -90
compiler/forget/src/HIR/InferAlias.ts renamed
+29 -26
@@ -1,6 +1,6 @@
1 import invariant from "invariant";
2 import DisjointSet from "./DisjointSet";
3 -import { HIRFunction, Identifier, Instruction, Place, LValue } from "./HIR";
3 +import { HIRFunction, Identifier, Instruction, LValue, Place } from "./HIR";
4 import { printInstructionValue } from "./PrintHIR";
5
6 type AbstractValue = AbstractObject | AbstractPrimitive;
@@ -14,6 +14,31 @@ type AbstractPrimitive = {
14 };
15
16 export type AliasSet = Set<Identifier>;
17 +
18 +export function buildAliasSets(
19 + aliases: DisjointSet<Identifier>
20 +): Array<AliasSet> {
21 + const aliasIds: Map<Identifier, number> = new Map();
22 + const aliasSets: Map<number, Set<Identifier>> = new Map();
23 +
24 + aliases.forEach((identifier, groupIdentifier) => {
25 + let aliasId = aliasIds.get(groupIdentifier);
26 + if (aliasId == null) {
27 + aliasId = aliasIds.size;
28 + aliasIds.set(groupIdentifier, aliasId);
29 + }
30 +
31 + let aliasSet = aliasSets.get(aliasId);
32 + if (aliasSet === undefined) {
33 + aliasSet = new Set();
34 + aliasSets.set(aliasId, aliasSet);
35 + }
36 + aliasSet.add(identifier);
37 + });
38 +
39 + return [...aliasSets.values()];
40 +}
41 +
42 class AbstractState {
43 aliases = new DisjointSet<Identifier>();
44 // NOTE(gsn): Should this be a part of AbstractObject? No, because this has
@@ -132,7 +157,6 @@ class AbstractState {
157 }
158
159 memberAlias.add(alias.identifier);
135 - this.aliases.union([lvalue.place.identifier, alias.identifier]);
160 return;
161 }
162
@@ -153,38 +177,17 @@ class AbstractState {
177 // lvalue = alias.memberPath;
178 this.#values.set(lvalue.place.identifier, value);
179 }
156 -
157 - buildAliasSets(): Array<Set<Identifier>> {
158 - const aliasIds: Map<Identifier, number> = new Map();
159 - const aliasSets: Map<number, Set<Identifier>> = new Map();
160 -
161 - this.aliases.forEach((identifier, groupIdentifier) => {
162 - let aliasId = aliasIds.get(groupIdentifier);
163 - if (aliasId == null) {
164 - aliasId = aliasIds.size;
165 - aliasIds.set(groupIdentifier, aliasId);
166 - }
167 -
168 - let aliasSet = aliasSets.get(aliasId);
169 - if (aliasSet === undefined) {
170 - aliasSet = new Set();
171 - aliasSets.set(aliasId, aliasSet);
172 - }
173 - aliasSet.add(identifier);
174 - });
175 -
176 - return [...aliasSets.values()];
177 - }
180 }
181
180 -export function buildAliasSets(func: HIRFunction): Array<AliasSet> {
182 +export function inferAliases(func: HIRFunction): DisjointSet<Identifier> {
183 const state = new AbstractState();
184 for (const [_, block] of func.body.blocks) {
185 for (const instr of block.instructions) {
186 inferInstr(instr, state);
187 }
188 }
187 - return state.buildAliasSets();
189 +
190 + return state.aliases;
191 }
192
193 function inferInstr(instr: Instruction, state: AbstractState) {
compiler/forget/src/HIR/InferAliasForFields.ts new
+42
@@ -0,0 +1,42 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + */
7 +import DisjointSet from "./DisjointSet";
8 +import { HIRFunction, Identifier } from "./HIR";
9 +
10 +export function inferAliasForFields(
11 + func: HIRFunction,
12 + aliases: DisjointSet<Identifier>
13 +) {
14 + for (const [_, block] of func.body.blocks) {
15 + for (const instr of block.instructions) {
16 + let { lvalue, value } = instr;
17 +
18 + // Not an aliasing instruction.
19 + if (value.kind !== "Identifier") {
20 + continue;
21 + }
22 +
23 + // No lvalue, no aliasing.
24 + if (lvalue === null) {
25 + continue;
26 + }
27 +
28 + // No field aliasing.
29 + if (lvalue.place.memberPath === null) {
30 + continue;
31 + }
32 +
33 + // lvalue or value get mutated later, so this aliasing is observable.
34 + if (
35 + lvalue.place.identifier.mutableRange.end > instr.id ||
36 + value.identifier.mutableRange.end > instr.id
37 + ) {
38 + aliases.union([lvalue.place.identifier, value.identifier]);
39 + }
40 + }
41 + }
42 +}
compiler/forget/src/HIR/InferMutableLifetimes.ts
+5 -26
@@ -7,14 +7,10 @@
7
8 import invariant from "invariant";
9 import { assertExhaustive } from "../Common/utils";
10 -import { buildAliasSets } from "./BuildAliasSets";
11 -import DisjointSet from "./DisjointSet";
10 import {
11 Effect,
12 HIRFunction,
15 - Identifier,
13 Instruction,
17 - InstructionId,
14 makeInstructionId,
15 Place,
16 } from "./HIR";
@@ -85,7 +81,10 @@ function inferPlace(place: Place, instr: Instruction) {
81 }
82 }
83
88 -export function inferMutableRanges(func: HIRFunction) {
84 +export function inferMutableLifetimes(
85 + func: HIRFunction,
86 + inferMutableRangeForFields: boolean
87 +) {
88 for (const [_, block] of func.body.blocks) {
89 for (const phi of block.phis) {
90 let start = Number.MAX_SAFE_INTEGER;
@@ -120,30 +119,10 @@ export function inferMutableRanges(func: HIRFunction) {
119 // Let's be optimistic and assume this lvalue is not mutable by
120 // default.
121 lvalueId.mutableRange.end = makeInstructionId(instr.id + 1);
123 - } else {
122 + } else if (inferMutableRangeForFields) {
123 inferPlace(instr.lvalue.place, instr);
124 }
125 }
126 }
127 }
129 -
130 - const aliasSets = buildAliasSets(func);
131 - for (const aliasSet of aliasSets) {
132 - // Update mutableRange.end only if the identifiers have actually been
133 - // mutated.
134 - const haveIdentifiersBeenMutated = [...aliasSet].some(
135 - (id) => id.mutableRange.end - id.mutableRange.start > 1
136 - );
137 -
138 - if (haveIdentifiersBeenMutated) {
139 - // Find final instruction which mutates this alias set.
140 - const mutableRangeEnds = [...aliasSet].map((id) => id.mutableRange.end);
141 - const maxMutableRangeEnd = Math.max(...mutableRangeEnds) as InstructionId;
142 -
143 - // Update mutableRange.end for all aliases in this set.
144 - for (const alias of aliasSet) {
145 - alias.mutableRange.end = maxMutableRangeEnd;
146 - }
147 - }
148 - }
128 }
compiler/forget/src/HIR/InferMutableRanges.ts new
+25
@@ -0,0 +1,25 @@
1 +import { HIRFunction } from "./HIR";
2 +import { inferAliases } from "./InferAlias";
3 +import { inferAliasForFields } from "./InferAliasForFields";
4 +import { inferMutableLifetimes } from "./InferMutableLifetimes";
5 +import { inferMutableRangesForAlias } from "./InferMutableRangesForAlias";
6 +
7 +export function inferMutableRanges(ir: HIRFunction) {
8 + // Calculate aliases
9 + const aliases = inferAliases(ir);
10 +
11 + // Infer mutable ranges for non fields
12 + inferMutableLifetimes(ir, false);
13 +
14 + // Infer mutable ranges for aliases that are not fields
15 + inferMutableRangesForAlias(aliases);
16 +
17 + // Update aliasing information of fields
18 + inferAliasForFields(ir, aliases);
19 +
20 + // Re-infer mutable ranges for all values
21 + inferMutableLifetimes(ir, true);
22 +
23 + // Re-infer mutable ranges for aliases
24 + inferMutableRangesForAlias(aliases);
25 +}
compiler/forget/src/HIR/InferMutableRangesForAlias.ts new
+25
@@ -0,0 +1,25 @@
1 +import DisjointSet from "./DisjointSet";
2 +import { Identifier, InstructionId } from "./HIR";
3 +import { buildAliasSets } from "./InferAlias";
4 +
5 +export function inferMutableRangesForAlias(aliases: DisjointSet<Identifier>) {
6 + const aliasSets = buildAliasSets(aliases);
7 + for (const aliasSet of aliasSets) {
8 + // Update mutableRange.end only if the identifiers have actually been
9 + // mutated.
10 + const haveIdentifiersBeenMutated = [...aliasSet].some(
11 + (id) => id.mutableRange.end - id.mutableRange.start > 1
12 + );
13 +
14 + if (haveIdentifiersBeenMutated) {
15 + // Find final instruction which mutates this alias set.
16 + const mutableRangeEnds = [...aliasSet].map((id) => id.mutableRange.end);
17 + const maxMutableRangeEnd = Math.max(...mutableRangeEnds) as InstructionId;
18 +
19 + // Update mutableRange.end for all aliases in this set.
20 + for (const alias of aliasSet) {
21 + alias.mutableRange.end = maxMutableRangeEnd;
22 + }
23 + }
24 + }
25 +}
compiler/forget/src/HIR/Pipeline.ts
+1 -1
@@ -10,11 +10,11 @@ import { lower } from "../HIR/BuildHIR";
10 import { eliminateRedundantPhi } from "../HIR/EliminateRedundantPhi";
11 import enterSSA from "../HIR/EnterSSA";
12 import { Environment } from "../HIR/HIRBuilder";
13 -import { inferMutableRanges } from "../HIR/InferMutableLifetimes";
13 import inferReferenceEffects from "../HIR/InferReferenceEffects";
14 import { leaveSSA } from "../HIR/LeaveSSA";
15 import codegen from "./Codegen";
16 import { HIRFunction } from "./HIR";
17 +import { inferMutableRanges } from "./InferMutableRanges";
18 import { inferReactiveScopeDependencies } from "./InferReactiveScopeDependencies";
19 import { inferReactiveScopes } from "./InferReactiveScopes";
20 import { inferReactiveScopeVariables } from "./InferReactiveScopeVariables";
compiler/forget/src/HIR/PrintHIR.ts
+13
@@ -7,6 +7,7 @@
7
8 import generate from "@babel/generator";
9 import { assertExhaustive } from "../Common/utils";
10 +import DisjointSet from "./DisjointSet";
11 import {
12 GotoVariant,
13 HIR,
@@ -22,6 +23,7 @@ import {
23 SourceLocation,
24 Terminal,
25 } from "./HIR";
26 +import { buildAliasSets } from "./InferAlias";
27
28 export type Options = {
29 indent: number;
@@ -332,3 +334,14 @@ export function printSourceLocation(loc: SourceLocation): string {
334 return `${loc.start.line}:${loc.start.column}:${loc.end.line}:${loc.end.column}`;
335 }
336 }
337 +
338 +export function printAliases(aliases: DisjointSet<Identifier>): string {
339 + const aliasSets = buildAliasSets(aliases);
340 +
341 + const items = [];
342 + for (const aliasSet of aliasSets) {
343 + items.push([...aliasSet].map((id) => printIdentifier(id)).join(","));
344 + }
345 +
346 + return items.join("\n");
347 +}
compiler/forget/src/__tests__/fixtures/hir/reactive-scope-grouping.expect.md
+10 -9
@@ -19,12 +19,13 @@ function foo() {
19 ```
20 bb0:
21 [1] Let mutate x$4_@0[1:6] = Object { }
22 - [2] Let mutate y$5_@0[1:6] = Array []
23 - [3] Let mutate z$6_@0[1:6] = Object { }
24 - [4] Call mutate y$5_@0.push(mutate z$6_@0)
25 - [5] Reassign mutate x$4_@0.y[1:6] = read y$5_@0
22 + [2] Let mutate y$5_@1[2:5] = Array []
23 + [3] Let mutate z$6_@1[2:5] = Object { }
24 + [4] Call mutate y$5_@1.push(mutate z$6_@1)
25 + [5] Reassign mutate x$4_@0.y[1:6] = read y$5_@1
26 [6] Return freeze x$4_@0
27 -
27 +scope1 [2:5]:
28 + - mutate x$4_@0.y
29 ```
30
31 ### CFG
@@ -35,10 +36,10 @@ flowchart TB
36 subgraph bb0
37 bb0_instrs["
38 [1] Let mutate x$4_@0[1:6] = Object { }
38 - [2] Let mutate y$5_@0[1:6] = Array []
39 - [3] Let mutate z$6_@0[1:6] = Object { }
40 - [4] Call mutate y$5_@0.push(mutate z$6_@0)
41 - [5] Reassign mutate x$4_@0.y[1:6] = read y$5_@0
39 + [2] Let mutate y$5_@1[2:5] = Array []
40 + [3] Let mutate z$6_@1[2:5] = Object { }
41 + [4] Call mutate y$5_@1.push(mutate z$6_@1)
42 + [5] Reassign mutate x$4_@0.y[1:6] = read y$5_@1
43 "]
44 bb0_instrs --> bb0_terminal(["Return freeze x$4_@0"])
45 end
compiler/forget/src/__tests__/fixtures/hir/ssa-for.expect.md
+5 -5
@@ -16,7 +16,7 @@ function foo() {
16
17 ```
18 bb0:
19 - [1] Let mutate x$7_@0[0:13] = 1
19 + [1] Let mutate x$7_@0[1:13] = 1
20 [2] For init=bb3 test=bb1 loop=bb5 update=bb4 fallthrough=bb2
21 bb3:
22 predecessor blocks: bb0
@@ -24,14 +24,14 @@ bb3:
24 [4] Goto bb1
25 bb1:
26 predecessor blocks: bb3 bb4
27 - x$13_@0[0:13]: phi(bb3: x$7_@0, bb4: x$14_@0)
27 + x$13_@0[1:13]: phi(bb3: x$7_@0, bb4: x$14_@0)
28 [5] Const mutate $9_@2 = 10
29 [6] Const mutate $11_@3[6:8] = Binary read i$8_@1 < read $9_@2
30 [7] If (read $11_@3) then:bb5 else:bb2 fallthrough=bb2
31 bb5:
32 predecessor blocks: bb1
33 [8] Const mutate $12_@4 = 1
34 - [9] Reassign mutate x$14_@0[0:13] = Binary read x$13_@0 + read $12_@4
34 + [9] Reassign mutate x$14_@0[1:13] = Binary read x$13_@0 + read $12_@4
35 [10] Goto(Continue) bb4
36 bb4:
37 predecessor blocks: bb5
@@ -51,7 +51,7 @@ flowchart TB
51 %% Basic Blocks
52 subgraph bb0
53 bb0_instrs["
54 - [1] Let mutate x$7_@0[0:13] = 1
54 + [1] Let mutate x$7_@0[1:13] = 1
55 "]
56 bb0_instrs --> bb0_terminal(["For"])
57 end
@@ -71,7 +71,7 @@ flowchart TB
71 subgraph bb5
72 bb5_instrs["
73 [8] Const mutate $12_@4 = 1
74 - [9] Reassign mutate x$14_@0[0:13] = Binary read x$13_@0 + read $12_@4
74 + [9] Reassign mutate x$14_@0[1:13] = Binary read x$13_@0 + read $12_@4
75 "]
76 bb5_instrs --> bb5_terminal(["Goto"])
77 end
compiler/forget/src/__tests__/fixtures/hir/ssa-property-alias-alias-mutate-if.expect.md new
+106
@@ -0,0 +1,106 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function foo(a) {
6 + const b = {};
7 + const x = b;
8 + if (a) {
9 + let y = {};
10 + x.y = y;
11 + } else {
12 + let z = {};
13 + x.z = z;
14 + }
15 + mutate(b); // aliases x, y & z
16 + return x;
17 +}
18 +
19 +```
20 +
21 +## HIR
22 +
23 +```
24 +bb0:
25 + [1] Const mutate b$8_@0[0:11] = Object { }
26 + [2] Const mutate x$9_@0[0:11] = read b$8_@0
27 + [3] If (read a$7) then:bb2 else:bb3 fallthrough=bb1
28 +bb2:
29 + predecessor blocks: bb0
30 + [4] Let mutate y$10_@0[0:11] = Object { }
31 + [5] Reassign mutate x$9_@0.y[0:11] = read y$10_@0
32 + [6] Goto bb1
33 +bb3:
34 + predecessor blocks: bb0
35 + [7] Let mutate z$11_@0[0:11] = Object { }
36 + [8] Reassign mutate x$9_@0.z[0:11] = read z$11_@0
37 + [9] Goto bb1
38 +bb1:
39 + predecessor blocks: bb2 bb3
40 + [10] Call mutate mutate$6_@0(mutate b$8_@0)
41 + [11] Return freeze x$9_@0
42 +
43 +```
44 +
45 +### CFG
46 +
47 +```mermaid
48 +flowchart TB
49 + %% Basic Blocks
50 + subgraph bb0
51 + bb0_instrs["
52 + [1] Const mutate b$8_@0[0:11] = Object { }
53 + [2] Const mutate x$9_@0[0:11] = read b$8_@0
54 + "]
55 + bb0_instrs --> bb0_terminal(["If (read a$7)"])
56 + end
57 + subgraph bb2
58 + bb2_instrs["
59 + [4] Let mutate y$10_@0[0:11] = Object { }
60 + [5] Reassign mutate x$9_@0.y[0:11] = read y$10_@0
61 + "]
62 + bb2_instrs --> bb2_terminal(["Goto"])
63 + end
64 + subgraph bb3
65 + bb3_instrs["
66 + [7] Let mutate z$11_@0[0:11] = Object { }
67 + [8] Reassign mutate x$9_@0.z[0:11] = read z$11_@0
68 + "]
69 + bb3_instrs --> bb3_terminal(["Goto"])
70 + end
71 + subgraph bb1
72 + bb1_instrs["
73 + [10] Call mutate mutate$6_@0(mutate b$8_@0)
74 + "]
75 + bb1_instrs --> bb1_terminal(["Return freeze x$9_@0"])
76 + end
77 +
78 + %% Jumps
79 + bb0_terminal -- "then" --> bb2
80 + bb0_terminal -- "else" --> bb3
81 + bb0_terminal -- "fallthrough" --> bb1
82 + bb2_terminal --> bb1
83 + bb3_terminal --> bb1
84 +
85 +```
86 +
87 +## Code
88 +
89 +```javascript
90 +function foo$0(a$7) {
91 + const b$8 = {};
92 + const x$9 = b$8;
93 + bb1: if (a$7) {
94 + const y$10 = {};
95 + x$9.y = y$10;
96 + } else {
97 + const z$11 = {};
98 + x$9.z = z$11;
99 + }
100 +
101 + mutate$6(b$8);
102 + return x$9;
103 +}
104 +
105 +```
106 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/ssa-property-alias-alias-mutate-if.js new
+13
@@ -0,0 +1,13 @@
1 +function foo(a) {
2 + const b = {};
3 + const x = b;
4 + if (a) {
5 + let y = {};
6 + x.y = y;
7 + } else {
8 + let z = {};
9 + x.z = z;
10 + }
11 + mutate(b); // aliases x, y & z
12 + return x;
13 +}
compiler/forget/src/__tests__/fixtures/hir/ssa-property-alias-if.expect.md new
+101
@@ -0,0 +1,101 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function foo(a) {
6 + const x = {};
7 + if (a) {
8 + let y = {};
9 + x.y = y;
10 + } else {
11 + let z = {};
12 + x.z = z;
13 + }
14 + return x;
15 +}
16 +
17 +```
18 +
19 +## HIR
20 +
21 +```
22 +bb0:
23 + [1] Const mutate x$6_@0[1:9] = Object { }
24 + [2] If (read a$5) then:bb2 else:bb3 fallthrough=bb1
25 +bb2:
26 + predecessor blocks: bb0
27 + [3] Let mutate y$7_@1 = Object { }
28 + [4] Reassign mutate x$6_@0.y[1:9] = read y$7_@1
29 + [5] Goto bb1
30 +bb3:
31 + predecessor blocks: bb0
32 + [6] Let mutate z$8_@2 = Object { }
33 + [7] Reassign mutate x$6_@0.z[1:9] = read z$8_@2
34 + [8] Goto bb1
35 +bb1:
36 + predecessor blocks: bb2 bb3
37 + [9] Return freeze x$6_@0
38 +scope0 [1:9]:
39 + - read a$5
40 +scope1 [3:4]:
41 + - mutate x$6_@0.y
42 +scope2 [6:7]:
43 + - mutate x$6_@0.z
44 +```
45 +
46 +### CFG
47 +
48 +```mermaid
49 +flowchart TB
50 + %% Basic Blocks
51 + subgraph bb0
52 + bb0_instrs["
53 + [1] Const mutate x$6_@0[1:9] = Object { }
54 + "]
55 + bb0_instrs --> bb0_terminal(["If (read a$5)"])
56 + end
57 + subgraph bb2
58 + bb2_instrs["
59 + [3] Let mutate y$7_@1 = Object { }
60 + [4] Reassign mutate x$6_@0.y[1:9] = read y$7_@1
61 + "]
62 + bb2_instrs --> bb2_terminal(["Goto"])
63 + end
64 + subgraph bb3
65 + bb3_instrs["
66 + [6] Let mutate z$8_@2 = Object { }
67 + [7] Reassign mutate x$6_@0.z[1:9] = read z$8_@2
68 + "]
69 + bb3_instrs --> bb3_terminal(["Goto"])
70 + end
71 + subgraph bb1
72 + bb1_terminal(["Return freeze x$6_@0"])
73 + end
74 +
75 + %% Jumps
76 + bb0_terminal -- "then" --> bb2
77 + bb0_terminal -- "else" --> bb3
78 + bb0_terminal -- "fallthrough" --> bb1
79 + bb2_terminal --> bb1
80 + bb3_terminal --> bb1
81 +
82 +```
83 +
84 +## Code
85 +
86 +```javascript
87 +function foo$0(a$5) {
88 + const x$6 = {};
89 + bb1: if (a$5) {
90 + const y$7 = {};
91 + x$6.y = y$7;
92 + } else {
93 + const z$8 = {};
94 + x$6.z = z$8;
95 + }
96 +
97 + return x$6;
98 +}
99 +
100 +```
101 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/ssa-property-alias-if.js new
+11
@@ -0,0 +1,11 @@
1 +function foo(a) {
2 + const x = {};
3 + if (a) {
4 + let y = {};
5 + x.y = y;
6 + } else {
7 + let z = {};
8 + x.z = z;
9 + }
10 + return x;
11 +}
compiler/forget/src/__tests__/fixtures/hir/ssa-property-alias-mutate-if.expect.md new
+102
@@ -0,0 +1,102 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function foo(a) {
6 + const x = {};
7 + if (a) {
8 + let y = {};
9 + x.y = y;
10 + } else {
11 + let z = {};
12 + x.z = z;
13 + }
14 + mutate(x);
15 + return x;
16 +}
17 +
18 +```
19 +
20 +## HIR
21 +
22 +```
23 +bb0:
24 + [1] Const mutate x$7_@0[0:10] = Object { }
25 + [2] If (read a$6) then:bb2 else:bb3 fallthrough=bb1
26 +bb2:
27 + predecessor blocks: bb0
28 + [3] Let mutate y$8_@0[0:10] = Object { }
29 + [4] Reassign mutate x$7_@0.y[0:10] = read y$8_@0
30 + [5] Goto bb1
31 +bb3:
32 + predecessor blocks: bb0
33 + [6] Let mutate z$9_@0[0:10] = Object { }
34 + [7] Reassign mutate x$7_@0.z[0:10] = read z$9_@0
35 + [8] Goto bb1
36 +bb1:
37 + predecessor blocks: bb2 bb3
38 + [9] Call mutate mutate$5_@0(mutate x$7_@0)
39 + [10] Return freeze x$7_@0
40 +
41 +```
42 +
43 +### CFG
44 +
45 +```mermaid
46 +flowchart TB
47 + %% Basic Blocks
48 + subgraph bb0
49 + bb0_instrs["
50 + [1] Const mutate x$7_@0[0:10] = Object { }
51 + "]
52 + bb0_instrs --> bb0_terminal(["If (read a$6)"])
53 + end
54 + subgraph bb2
55 + bb2_instrs["
56 + [3] Let mutate y$8_@0[0:10] = Object { }
57 + [4] Reassign mutate x$7_@0.y[0:10] = read y$8_@0
58 + "]
59 + bb2_instrs --> bb2_terminal(["Goto"])
60 + end
61 + subgraph bb3
62 + bb3_instrs["
63 + [6] Let mutate z$9_@0[0:10] = Object { }
64 + [7] Reassign mutate x$7_@0.z[0:10] = read z$9_@0
65 + "]
66 + bb3_instrs --> bb3_terminal(["Goto"])
67 + end
68 + subgraph bb1
69 + bb1_instrs["
70 + [9] Call mutate mutate$5_@0(mutate x$7_@0)
71 + "]
72 + bb1_instrs --> bb1_terminal(["Return freeze x$7_@0"])
73 + end
74 +
75 + %% Jumps
76 + bb0_terminal -- "then" --> bb2
77 + bb0_terminal -- "else" --> bb3
78 + bb0_terminal -- "fallthrough" --> bb1
79 + bb2_terminal --> bb1
80 + bb3_terminal --> bb1
81 +
82 +```
83 +
84 +## Code
85 +
86 +```javascript
87 +function foo$0(a$6) {
88 + const x$7 = {};
89 + bb1: if (a$6) {
90 + const y$8 = {};
91 + x$7.y = y$8;
92 + } else {
93 + const z$9 = {};
94 + x$7.z = z$9;
95 + }
96 +
97 + mutate$5(x$7);
98 + return x$7;
99 +}
100 +
101 +```
102 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/ssa-property-alias-mutate-if.js new
+12
@@ -0,0 +1,12 @@
1 +function foo(a) {
2 + const x = {};
3 + if (a) {
4 + let y = {};
5 + x.y = y;
6 + } else {
7 + let z = {};
8 + x.z = z;
9 + }
10 + mutate(x);
11 + return x;
12 +}
compiler/forget/src/__tests__/fixtures/hir/ssa-property-alias-mutate-inside-if.expect.md new
+101
@@ -0,0 +1,101 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function foo(a) {
6 + const x = {};
7 + if (a) {
8 + let y = {};
9 + x.y = y;
10 + mutate(y); // aliases x & y, but not z
11 + } else {
12 + let z = {};
13 + x.z = z;
14 + }
15 + return x;
16 +}
17 +
18 +```
19 +
20 +## HIR
21 +
22 +```
23 +bb0:
24 + [1] Const mutate x$7_@0[0:10] = Object { }
25 + [2] If (read a$6) then:bb2 else:bb3 fallthrough=bb1
26 +bb2:
27 + predecessor blocks: bb0
28 + [3] Let mutate y$8_@0[0:10] = Object { }
29 + [4] Reassign mutate x$7_@0.y[0:10] = read y$8_@0
30 + [5] Call mutate mutate$4_@0(mutate y$8_@0)
31 + [6] Goto bb1
32 +bb3:
33 + predecessor blocks: bb0
34 + [7] Let mutate z$9_@1 = Object { }
35 + [8] Reassign mutate x$7_@0.z[0:10] = read z$9_@1
36 + [9] Goto bb1
37 +bb1:
38 + predecessor blocks: bb2 bb3
39 + [10] Return freeze x$7_@0
40 +scope1 [7:8]:
41 + - mutate x$7_@0.z
42 +```
43 +
44 +### CFG
45 +
46 +```mermaid
47 +flowchart TB
48 + %% Basic Blocks
49 + subgraph bb0
50 + bb0_instrs["
51 + [1] Const mutate x$7_@0[0:10] = Object { }
52 + "]
53 + bb0_instrs --> bb0_terminal(["If (read a$6)"])
54 + end
55 + subgraph bb2
56 + bb2_instrs["
57 + [3] Let mutate y$8_@0[0:10] = Object { }
58 + [4] Reassign mutate x$7_@0.y[0:10] = read y$8_@0
59 + [5] Call mutate mutate$4_@0(mutate y$8_@0)
60 + "]
61 + bb2_instrs --> bb2_terminal(["Goto"])
62 + end
63 + subgraph bb3
64 + bb3_instrs["
65 + [7] Let mutate z$9_@1 = Object { }
66 + [8] Reassign mutate x$7_@0.z[0:10] = read z$9_@1
67 + "]
68 + bb3_instrs --> bb3_terminal(["Goto"])
69 + end
70 + subgraph bb1
71 + bb1_terminal(["Return freeze x$7_@0"])
72 + end
73 +
74 + %% Jumps
75 + bb0_terminal -- "then" --> bb2
76 + bb0_terminal -- "else" --> bb3
77 + bb0_terminal -- "fallthrough" --> bb1
78 + bb2_terminal --> bb1
79 + bb3_terminal --> bb1
80 +
81 +```
82 +
83 +## Code
84 +
85 +```javascript
86 +function foo$0(a$6) {
87 + const x$7 = {};
88 + bb1: if (a$6) {
89 + const y$8 = {};
90 + x$7.y = y$8;
91 + mutate$4(y$8);
92 + } else {
93 + const z$9 = {};
94 + x$7.z = z$9;
95 + }
96 +
97 + return x$7;
98 +}
99 +
100 +```
101 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/ssa-property-alias-mutate-inside-if.js new
+12
@@ -0,0 +1,12 @@
1 +function foo(a) {
2 + const x = {};
3 + if (a) {
4 + let y = {};
5 + x.y = y;
6 + mutate(y); // aliases x & y, but not z
7 + } else {
8 + let z = {};
9 + x.z = z;
10 + }
11 + return x;
12 +}
compiler/forget/src/__tests__/fixtures/hir/ssa-property-alias-mutate.expect.md new
+64
@@ -0,0 +1,64 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function foo() {
6 + const a = {};
7 + const x = a;
8 +
9 + const y = {};
10 + y.x = x;
11 +
12 + mutate(a); // y & x are aliased to a
13 + return y;
14 +}
15 +
16 +```
17 +
18 +## HIR
19 +
20 +```
21 +bb0:
22 + [1] Const mutate a$5_@0[0:6] = Object { }
23 + [2] Const mutate x$6_@0[0:6] = read a$5_@0
24 + [3] Const mutate y$7_@0[0:6] = Object { }
25 + [4] Reassign mutate y$7_@0.x[0:6] = read x$6_@0
26 + [5] Call mutate mutate$4_@0(mutate a$5_@0)
27 + [6] Return freeze y$7_@0
28 +
29 +```
30 +
31 +### CFG
32 +
33 +```mermaid
34 +flowchart TB
35 + %% Basic Blocks
36 + subgraph bb0
37 + bb0_instrs["
38 + [1] Const mutate a$5_@0[0:6] = Object { }
39 + [2] Const mutate x$6_@0[0:6] = read a$5_@0
40 + [3] Const mutate y$7_@0[0:6] = Object { }
41 + [4] Reassign mutate y$7_@0.x[0:6] = read x$6_@0
42 + [5] Call mutate mutate$4_@0(mutate a$5_@0)
43 + "]
44 + bb0_instrs --> bb0_terminal(["Return freeze y$7_@0"])
45 + end
46 +
47 + %% Jumps
48 + %% empty
49 +```
50 +
51 +## Code
52 +
53 +```javascript
54 +function foo$0() {
55 + const a$5 = {};
56 + const x$6 = a$5;
57 + const y$7 = {};
58 + y$7.x = x$6;
59 + mutate$4(a$5);
60 + return y$7;
61 +}
62 +
63 +```
64 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/ssa-property-alias-mutate.js new
+10
@@ -0,0 +1,10 @@
1 +function foo() {
2 + const a = {};
3 + const x = a;
4 +
5 + const y = {};
6 + y.x = x;
7 +
8 + mutate(a); // y & x are aliased to a
9 + return y;
10 +}
compiler/forget/src/__tests__/fixtures/hir/ssa-property-mutate-2.expect.md new
+58
@@ -0,0 +1,58 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function foo() {
6 + const x = [];
7 + const y = {};
8 + y.x = x;
9 + mutate(x);
10 + return y;
11 +}
12 +
13 +```
14 +
15 +## HIR
16 +
17 +```
18 +bb0:
19 + [1] Const mutate x$4_@0[0:5] = Array []
20 + [2] Const mutate y$5_@0[0:5] = Object { }
21 + [3] Reassign mutate y$5_@0.x[0:5] = read x$4_@0
22 + [4] Call mutate mutate$3_@0(mutate x$4_@0)
23 + [5] Return freeze y$5_@0
24 +
25 +```
26 +
27 +### CFG
28 +
29 +```mermaid
30 +flowchart TB
31 + %% Basic Blocks
32 + subgraph bb0
33 + bb0_instrs["
34 + [1] Const mutate x$4_@0[0:5] = Array []
35 + [2] Const mutate y$5_@0[0:5] = Object { }
36 + [3] Reassign mutate y$5_@0.x[0:5] = read x$4_@0
37 + [4] Call mutate mutate$3_@0(mutate x$4_@0)
38 + "]
39 + bb0_instrs --> bb0_terminal(["Return freeze y$5_@0"])
40 + end
41 +
42 + %% Jumps
43 + %% empty
44 +```
45 +
46 +## Code
47 +
48 +```javascript
49 +function foo$0() {
50 + const x$4 = [];
51 + const y$5 = {};
52 + y$5.x = x$4;
53 + mutate$3(x$4);
54 + return y$5;
55 +}
56 +
57 +```
58 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/ssa-property-mutate-2.js new
+7
@@ -0,0 +1,7 @@
1 +function foo() {
2 + const x = [];
3 + const y = {};
4 + y.x = x;
5 + mutate(x);
6 + return y;
7 +}
compiler/forget/src/__tests__/fixtures/hir/ssa-property-mutate-alias.expect.md new
+64
@@ -0,0 +1,64 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function foo() {
6 + const a = {};
7 + const y = a;
8 + const x = [];
9 +
10 + y.x = x;
11 +
12 + mutate(a); // y & x are aliased to a
13 + return y;
14 +}
15 +
16 +```
17 +
18 +## HIR
19 +
20 +```
21 +bb0:
22 + [1] Const mutate a$5_@0[0:6] = Object { }
23 + [2] Const mutate y$6_@0[0:6] = read a$5_@0
24 + [3] Const mutate x$7_@0[0:6] = Array []
25 + [4] Reassign mutate y$6_@0.x[0:6] = read x$7_@0
26 + [5] Call mutate mutate$4_@0(mutate a$5_@0)
27 + [6] Return freeze y$6_@0
28 +
29 +```
30 +
31 +### CFG
32 +
33 +```mermaid
34 +flowchart TB
35 + %% Basic Blocks
36 + subgraph bb0
37 + bb0_instrs["
38 + [1] Const mutate a$5_@0[0:6] = Object { }
39 + [2] Const mutate y$6_@0[0:6] = read a$5_@0
40 + [3] Const mutate x$7_@0[0:6] = Array []
41 + [4] Reassign mutate y$6_@0.x[0:6] = read x$7_@0
42 + [5] Call mutate mutate$4_@0(mutate a$5_@0)
43 + "]
44 + bb0_instrs --> bb0_terminal(["Return freeze y$6_@0"])
45 + end
46 +
47 + %% Jumps
48 + %% empty
49 +```
50 +
51 +## Code
52 +
53 +```javascript
54 +function foo$0() {
55 + const a$5 = {};
56 + const y$6 = a$5;
57 + const x$7 = [];
58 + y$6.x = x$7;
59 + mutate$4(a$5);
60 + return y$6;
61 +}
62 +
63 +```
64 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/ssa-property-mutate-alias.js new
+10
@@ -0,0 +1,10 @@
1 +function foo() {
2 + const a = {};
3 + const y = a;
4 + const x = [];
5 +
6 + y.x = x;
7 +
8 + mutate(a); // y & x are aliased to a
9 + return y;
10 +}
compiler/forget/src/__tests__/fixtures/hir/ssa-property-mutate.expect.md new
+58
@@ -0,0 +1,58 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function foo() {
6 + const x = [];
7 + const y = {};
8 + y.x = x;
9 + mutate(y);
10 + return y;
11 +}
12 +
13 +```
14 +
15 +## HIR
16 +
17 +```
18 +bb0:
19 + [1] Const mutate x$4_@0[0:5] = Array []
20 + [2] Const mutate y$5_@0[0:5] = Object { }
21 + [3] Reassign mutate y$5_@0.x[0:5] = read x$4_@0
22 + [4] Call mutate mutate$3_@0(mutate y$5_@0)
23 + [5] Return freeze y$5_@0
24 +
25 +```
26 +
27 +### CFG
28 +
29 +```mermaid
30 +flowchart TB
31 + %% Basic Blocks
32 + subgraph bb0
33 + bb0_instrs["
34 + [1] Const mutate x$4_@0[0:5] = Array []
35 + [2] Const mutate y$5_@0[0:5] = Object { }
36 + [3] Reassign mutate y$5_@0.x[0:5] = read x$4_@0
37 + [4] Call mutate mutate$3_@0(mutate y$5_@0)
38 + "]
39 + bb0_instrs --> bb0_terminal(["Return freeze y$5_@0"])
40 + end
41 +
42 + %% Jumps
43 + %% empty
44 +```
45 +
46 +## Code
47 +
48 +```javascript
49 +function foo$0() {
50 + const x$4 = [];
51 + const y$5 = {};
52 + y$5.x = x$4;
53 + mutate$3(y$5);
54 + return y$5;
55 +}
56 +
57 +```
58 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/ssa-property-mutate.js new
+7
@@ -0,0 +1,7 @@
1 +function foo() {
2 + const x = [];
3 + const y = {};
4 + y.x = x;
5 + mutate(y);
6 + return y;
7 +}
compiler/forget/src/__tests__/fixtures/hir/ssa-property.expect.md
+10 -9
@@ -15,11 +15,12 @@ function foo() {
15
16 ```
17 bb0:
18 - [1] Const mutate x$3_@0[1:4] = Array []
19 - [2] Const mutate y$4_@0[1:4] = Object { }
20 - [3] Reassign mutate y$4_@0.x[1:4] = read x$3_@0
21 - [4] Return freeze y$4_@0
22 -
18 + [1] Const mutate x$3_@0 = Array []
19 + [2] Const mutate y$4_@1[2:4] = Object { }
20 + [3] Reassign mutate y$4_@1.x[2:4] = read x$3_@0
21 + [4] Return freeze y$4_@1
22 +scope1 [2:4]:
23 + - read x$3_@0
24 ```
25
26 ### CFG
@@ -29,11 +30,11 @@ flowchart TB
30 %% Basic Blocks
31 subgraph bb0
32 bb0_instrs["
32 - [1] Const mutate x$3_@0[1:4] = Array []
33 - [2] Const mutate y$4_@0[1:4] = Object { }
34 - [3] Reassign mutate y$4_@0.x[1:4] = read x$3_@0
33 + [1] Const mutate x$3_@0 = Array []
34 + [2] Const mutate y$4_@1[2:4] = Object { }
35 + [3] Reassign mutate y$4_@1.x[2:4] = read x$3_@0
36 "]
36 - bb0_instrs --> bb0_terminal(["Return freeze y$4_@0"])
37 + bb0_instrs --> bb0_terminal(["Return freeze y$4_@1"])
38 end
39
40 %% Jumps
compiler/forget/src/__tests__/fixtures/hir/ssa-while.expect.md
+12 -13
@@ -17,24 +17,23 @@ function foo() {
17
18 ```
19 bb0:
20 - [1] Let mutate x$5_@0[0:9] = 1
20 + [1] Let mutate x$5_@0[1:9] = 1
21 [2] While test=bb1 loop=bb3 fallthrough=bb2
22 bb1:
23 predecessor blocks: bb0 bb3
24 - x$7_@0[0:9]: phi(bb0: x$5_@0, bb3: x$10_@0)
24 + x$7_@0[1:9]: phi(bb0: x$5_@0, bb3: x$10_@0)
25 [3] Const mutate $6_@1 = 10
26 - [4] Const mutate $8_@2[4:6] = Binary read x$7_@0 < read $6_@1
27 - [5] If (read $8_@2) then:bb3 else:bb2 fallthrough=bb2
26 + [4] Const mutate $8_@0[1:9] = Binary read x$7_@0 < read $6_@1
27 + [5] If (read $8_@0) then:bb3 else:bb2 fallthrough=bb2
28 bb3:
29 predecessor blocks: bb1
30 - [6] Const mutate $9_@3 = 1
31 - [7] Reassign mutate x$10_@0[0:9] = Binary read x$7_@0 + read $9_@3
30 + [6] Const mutate $9_@2 = 1
31 + [7] Reassign mutate x$10_@0[1:9] = Binary read x$7_@0 + read $9_@2
32 [8] Goto(Continue) bb1
33 bb2:
34 predecessor blocks: bb1
35 [9] Return read x$7_@0
36 -scope2 [4:6]:
37 - - read $6_@1
36 +
37 ```
38
39 ### CFG
@@ -44,21 +43,21 @@ flowchart TB
43 %% Basic Blocks
44 subgraph bb0
45 bb0_instrs["
47 - [1] Let mutate x$5_@0[0:9] = 1
46 + [1] Let mutate x$5_@0[1:9] = 1
47 "]
48 bb0_instrs --> bb0_terminal(["While"])
49 end
50 subgraph bb1
51 bb1_instrs["
52 [3] Const mutate $6_@1 = 10
54 - [4] Const mutate $8_@2[4:6] = Binary read x$7_@0 < read $6_@1
53 + [4] Const mutate $8_@0[1:9] = Binary read x$7_@0 < read $6_@1
54 "]
56 - bb1_instrs --> bb1_terminal(["If (read $8_@2)"])
55 + bb1_instrs --> bb1_terminal(["If (read $8_@0)"])
56 end
57 subgraph bb3
58 bb3_instrs["
60 - [6] Const mutate $9_@3 = 1
61 - [7] Reassign mutate x$10_@0[0:9] = Binary read x$7_@0 + read $9_@3
59 + [6] Const mutate $9_@2 = 1
60 + [7] Reassign mutate x$10_@0[1:9] = Binary read x$7_@0 + read $9_@2
61 "]
62 bb3_instrs --> bb3_terminal(["Goto"])
63 end
compiler/forget/src/index.ts
+1 -1
@@ -29,7 +29,7 @@ import codegen from "./HIR/Codegen";
29 import { eliminateRedundantPhi } from "./HIR/EliminateRedundantPhi";
30 import enterSSA from "./HIR/EnterSSA";
31 import { Environment } from "./HIR/HIRBuilder";
32 -import { inferMutableRanges } from "./HIR/InferMutableLifetimes";
32 +import { inferMutableRanges } from "./HIR/InferMutableRanges";
33 import { inferReactiveScopeDependencies } from "./HIR/InferReactiveScopeDependencies";
34 import { inferReactiveScopes } from "./HIR/InferReactiveScopes";
35 import { inferReactiveScopeVariables } from "./HIR/InferReactiveScopeVariables";