Propagate scope dependencies upwards
Initial, partial implementation of dependency propagation. This lays the groundwork, #876 completes it after some other fixes this uncovered.
Joe Savona committed
Dec 14, 2022 at 08:42 UTC
972caf5bf15faf8f69e72f12dfd06d9ee3043451
16 files changed
+127
-14
compiler/forget/src/HIR/PropagateScopeDependencies.ts
new
+78
@@ -0,0 +1,78 @@
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
+
8
+import { assertExhaustive } from "../Common/utils";
9
+import { Place, ReactiveBasicBlock, ReactiveFunction } from "./HIR";
10
+
11
+/**
12
+ * Propagates the dependencies of each scope to its parent scope(s).
13
+ */
14
+export function propagateScopeDependencies(fn: ReactiveFunction): void {
15
+ const dependencies: Set<Place> = new Set();
16
+ visit(fn.body, dependencies);
17
+}
18
+
19
+function visit(block: ReactiveBasicBlock, dependencies: Set<Place>): void {
20
+ for (const item of block) {
21
+ switch (item.kind) {
22
+ case "block": {
23
+ visit(item.instructions, item.dependencies);
24
+ for (const dep of item.dependencies) {
25
+ dependencies.add(dep);
26
+ }
27
+ break;
28
+ }
29
+ case "instruction": {
30
+ break;
31
+ }
32
+ case "terminal": {
33
+ const terminal = item.terminal;
34
+ switch (terminal.kind) {
35
+ case "break":
36
+ case "continue":
37
+ case "return":
38
+ case "throw": {
39
+ break;
40
+ }
41
+ case "for": {
42
+ visit(terminal.loop, dependencies);
43
+ break;
44
+ }
45
+ case "while": {
46
+ visit(terminal.loop, dependencies);
47
+ break;
48
+ }
49
+ case "if": {
50
+ visit(terminal.consequent, dependencies);
51
+ if (terminal.alternate !== null) {
52
+ visit(terminal.alternate, dependencies);
53
+ }
54
+ break;
55
+ }
56
+ case "switch": {
57
+ for (const case_ of terminal.cases) {
58
+ if (case_.block !== undefined) {
59
+ visit(case_.block, dependencies);
60
+ }
61
+ }
62
+ break;
63
+ }
64
+ default: {
65
+ assertExhaustive(
66
+ terminal,
67
+ `Unexpected terminal kind '${(terminal as any).kind}'`
68
+ );
69
+ }
70
+ }
71
+ break;
72
+ }
73
+ default: {
74
+ assertExhaustive(item, `Unexpected item`);
75
+ }
76
+ }
77
+ }
78
+}
compiler/forget/src/__tests__/fixtures/hir/call.expect.md
+3
-1
@@ -52,6 +52,8 @@ bb0:
52
[7] Const mutate $14:TPrimitive = "div"
53
[8] Const mutate $15_@2 = JSX <read $14:TPrimitive a={read a$10_@0:TObject} b={freeze b$11_@0:TObject} ></read $14:TPrimitive>
54
[9] Return read $15_@2
55
+scope0 [1:7]:
56
+ - dependency: read $12:TPrimitive
57
scope1 [5:6]:
58
- dependency: read $12:TPrimitive
59
scope2 [8:9]:
@@ -66,7 +68,7 @@ scope2 [8:9]:
68
function Component(
69
props,
70
) {
69
- scope @0 [1:7] deps=[] {
71
+ scope @0 [1:7] deps=[read $12:TPrimitive] {
72
[1] Const mutate a$10_@0:TObject[1:7] = Array []
73
[2] Const mutate b$11_@0:TObject[1:7] = Object { }
74
[3] Call mutate foo$4:TFunction(mutate a$10_@0:TObject, mutate b$11_@0:TObject)
compiler/forget/src/__tests__/fixtures/hir/component.expect.md
+4
-1
@@ -100,6 +100,9 @@ bb2:
100
[40] Const mutate $73 = "\n "
101
[41] Const mutate $74_@5 = JSX <read $67:TPrimitive>{read $68}{read $71_@4}{read $72}{freeze renderedItems$32_@0:TFunction}{read $73}</read $67:TPrimitive>
102
[42] Return read $74_@5
103
+scope0 [3:33]:
104
+ - dependency: read $34:TPrimitive
105
+ - dependency: read maxItems$31:TProp
106
scope2 [6:7]:
107
- dependency: read $34:TPrimitive
108
- dependency: read maxItems$31:TProp
@@ -126,7 +129,7 @@ function Component(
129
) {
130
[1] Const mutate items$30:TProp = read props$29.items
131
[2] Const mutate maxItems$31:TProp = read props$29.maxItems
129
- scope @0 [3:33] deps=[] {
132
+ scope @0 [3:33] deps=[read $34:TPrimitive, read maxItems$31:TProp] {
133
[3] Const mutate renderedItems$32_@0:TFunction[3:33] = Array []
134
[4] Const mutate seen$33_@0:TFunction[3:33] = New mutate Set$6()
135
[5] Const mutate $34:TPrimitive = 0
compiler/forget/src/__tests__/fixtures/hir/constructor.expect.md
+3
-1
@@ -52,6 +52,8 @@ bb0:
52
[7] Const mutate $14:TPrimitive = "div"
53
[8] Const mutate $15_@2 = JSX <read $14:TPrimitive a={read a$10_@0} b={freeze b$11_@0:TObject} ></read $14:TPrimitive>
54
[9] Return read $15_@2
55
+scope0 [1:7]:
56
+ - dependency: read $12:TPrimitive
57
scope1 [5:6]:
58
- dependency: read $12:TPrimitive
59
scope2 [8:9]:
@@ -66,7 +68,7 @@ scope2 [8:9]:
68
function Component(
69
props,
70
) {
69
- scope @0 [1:7] deps=[] {
71
+ scope @0 [1:7] deps=[read $12:TPrimitive] {
72
[1] Const mutate a$10_@0[1:7] = Array []
73
[2] Const mutate b$11_@0:TObject[1:7] = Object { }
74
[3] New mutate Foo$4(mutate a$10_@0, mutate b$11_@0:TObject)
compiler/forget/src/__tests__/fixtures/hir/dependencies.expect.md
+3
-1
@@ -47,6 +47,8 @@ scope0 [1:10]:
47
- dependency: read x$6
48
- dependency: read x$6
49
- dependency: read y$7
50
+ - dependency: read y$7
51
+ - dependency: read x$6
52
scope1 [3:7]:
53
- dependency: read y$7
54
- dependency: read x$6
@@ -60,7 +62,7 @@ function foo(
62
y,
63
z,
64
) {
63
- scope @0 [1:10] deps=[read z$8, read x$6, read x$6, read y$7] {
65
+ scope @0 [1:10] deps=[read z$8, read x$6, read x$6, read y$7, read y$7, read x$6] {
66
[1] Const mutate items$9_@0:TFunction[1:10] = Array [read z$8]
67
[2] Call mutate items$9_@0.push(read x$6)
68
scope @1 [3:7] deps=[read y$7, read x$6] {
compiler/forget/src/__tests__/fixtures/hir/overlapping-scopes-shadowed.expect.md
+4
-1
@@ -20,6 +20,9 @@ bb0:
20
[3] Call mutate y$8_@1.push(read b$6)
21
[4] Call mutate x$7_@0.push(read a$5)
22
[5] Return
23
+scope0 [1:5]:
24
+ - dependency: read b$6
25
+ - dependency: read a$5
26
scope1 [2:4]:
27
- dependency: read b$6
28
- dependency: read a$5
@@ -32,7 +35,7 @@ function foo(
35
a,
36
b,
37
) {
35
- scope @0 [1:5] deps=[] {
38
+ scope @0 [1:5] deps=[read b$6, read a$5] {
39
[1] Const mutate x$7_@0:TFunction[1:5] = Array []
40
scope @1 [2:4] deps=[read b$6, read a$5] {
41
[2] Const mutate y$8_@1:TFunction[2:4] = Array []
compiler/forget/src/__tests__/fixtures/hir/overlapping-scopes-shadowing-within-block.expect.md
+5
-1
@@ -42,6 +42,10 @@ bb1:
42
[11] Return freeze x$11_@0:TFunction
43
scope0 [1:11]:
44
- dependency: read a$8
45
+ - dependency: read c$10
46
+ - dependency: read b$9
47
+ - dependency: read $13:TPrimitive
48
+ - dependency: freeze y$12_@1:TFunction
49
scope1 [3:7]:
50
- dependency: read c$10
51
- dependency: read b$9
@@ -58,7 +62,7 @@ function foo(
62
b,
63
c,
64
) {
61
- scope @0 [1:11] deps=[read a$8] {
65
+ scope @0 [1:11] deps=[read a$8, read c$10, read b$9, read $13:TPrimitive, freeze y$12_@1:TFunction] {
66
[1] Const mutate x$11_@0:TFunction[1:11] = Array []
67
if (read a$8) {
68
scope @1 [3:7] deps=[read c$10, read b$9] {
compiler/forget/src/__tests__/fixtures/hir/reactive-scope-grouping.expect.md
+3
-1
@@ -24,6 +24,8 @@ bb0:
24
[4] Call mutate y$5_@1.push(mutate z$6_@1:TObject)
25
[5] Reassign mutate x$4_@0.y[1:6] = read y$5_@1:TFunction
26
[6] Return freeze x$4_@0:TObject
27
+scope0 [1:6]:
28
+ - dependency: mutate x$4_@0.y
29
scope1 [2:5]:
30
- dependency: mutate x$4_@0.y
31
```
@@ -33,7 +35,7 @@ scope1 [2:5]:
35
```
36
function foo(
37
) {
36
- scope @0 [1:6] deps=[] {
38
+ scope @0 [1:6] deps=[mutate x$4_@0.y] {
39
[1] Const mutate x$4_@0:TObject[1:6] = Object { }
40
scope @1 [2:5] deps=[mutate x$4_@0.y] {
41
[2] Const mutate y$5_@1:TFunction[2:5] = Array []
compiler/forget/src/__tests__/fixtures/hir/reactive-scopes-if.expect.md
+4
-1
@@ -40,6 +40,9 @@ bb1:
40
scope0 [1:11]:
41
- dependency: read c$10
42
- dependency: read a$8
43
+ - dependency: read b$9
44
+ - dependency: read $13:TPrimitive
45
+ - dependency: freeze y$12_@1:TFunction
46
scope1 [3:5]:
47
- dependency: read b$9
48
scope2 [6:7]:
@@ -55,7 +58,7 @@ function foo(
58
b,
59
c,
60
) {
58
- scope @0 [1:11] deps=[read c$10, read a$8] {
61
+ scope @0 [1:11] deps=[read c$10, read a$8, read b$9, read $13:TPrimitive, freeze y$12_@1:TFunction] {
62
[1] Const mutate x$11_@0:TFunction[1:11] = Array []
63
if (read a$8) {
64
scope @1 [3:5] deps=[read b$9] {
compiler/forget/src/__tests__/fixtures/hir/reassignment.expect.md
+4
-1
@@ -31,6 +31,9 @@ bb0:
31
[8] Return read $11_@3
32
scope0 [1:7]:
33
- dependency: read props$6.p0
34
+ - dependency: read Component$0
35
+ - dependency: freeze x$9_@1
36
+ - dependency: read props$6.p1
37
scope2 [5:6]:
38
- dependency: read Component$0
39
- dependency: freeze x$9_@1
@@ -47,7 +50,7 @@ scope3 [7:8]:
50
function Component(
51
props,
52
) {
50
- scope @0 [1:7] deps=[read props$6.p0] {
53
+ scope @0 [1:7] deps=[read props$6.p0, read Component$0, freeze x$9_@1, read props$6.p1] {
54
[1] Const mutate x$7_@0:TFunction[1:7] = Array []
55
[2] Call mutate x$7_@0.push(read props$6.p0)
56
[3] Const mutate y$8_@0:TFunction[1:7] = read x$7_@0:TFunction
compiler/forget/src/__tests__/fixtures/hir/ssa-call-jsx-2.expect.md
+3
-1
@@ -62,6 +62,8 @@ bb1:
62
[10] Const mutate $19:TPrimitive = "div"
63
[11] Const mutate $20_@3 = JSX <read $19:TPrimitive a={freeze a$11_@0} b={freeze b$12_@0:TObject} ></read $19:TPrimitive>
64
[12] Return read $20_@3
65
+scope0 [1:10]:
66
+ - dependency: read $14:TPrimitive
67
scope2 [7:8]:
68
- dependency: read $14:TPrimitive
69
scope3 [11:12]:
@@ -76,7 +78,7 @@ scope3 [11:12]:
78
function Component(
79
props,
80
) {
79
- scope @0 [1:10] deps=[] {
81
+ scope @0 [1:10] deps=[read $14:TPrimitive] {
82
[1] Const mutate a$11_@0[1:10] = Array []
83
[2] Const mutate b$12_@0:TObject[1:10] = Object { }
84
[3] Call mutate foo$4:TFunction(mutate a$11_@0, mutate b$12_@0:TObject)
compiler/forget/src/__tests__/fixtures/hir/ssa-call-jsx.expect.md
+3
-1
@@ -52,6 +52,8 @@ bb0:
52
[7] Const mutate $14:TPrimitive = "div"
53
[8] Const mutate $15_@2 = JSX <read $14:TPrimitive a={read a$10_@0} b={freeze b$11_@0:TObject} ></read $14:TPrimitive>
54
[9] Return read $15_@2
55
+scope0 [1:7]:
56
+ - dependency: read $12:TPrimitive
57
scope1 [5:6]:
58
- dependency: read $12:TPrimitive
59
scope2 [8:9]:
@@ -66,7 +68,7 @@ scope2 [8:9]:
68
function Component(
69
props,
70
) {
69
- scope @0 [1:7] deps=[] {
71
+ scope @0 [1:7] deps=[read $12:TPrimitive] {
72
[1] Const mutate a$10_@0[1:7] = Array []
73
[2] Const mutate b$11_@0:TObject[1:7] = Object { }
74
[3] Call mutate foo$4:TFunction(mutate a$10_@0, mutate b$11_@0:TObject)
compiler/forget/src/__tests__/fixtures/hir/ssa-property-alias-if.expect.md
+3
-1
@@ -37,6 +37,8 @@ bb1:
37
[9] Return freeze x$6_@0:TObject
38
scope0 [1:9]:
39
- dependency: read a$5
40
+ - dependency: mutate x$6_@0.y
41
+ - dependency: mutate x$6_@0.z
42
scope1 [3:4]:
43
- dependency: mutate x$6_@0.y
44
scope2 [6:7]:
@@ -49,7 +51,7 @@ scope2 [6:7]:
51
function foo(
52
a,
53
) {
52
- scope @0 [1:9] deps=[read a$5] {
54
+ scope @0 [1:9] deps=[read a$5, mutate x$6_@0.y, mutate x$6_@0.z] {
55
[1] Const mutate x$6_@0:TObject[1:9] = Object { }
56
if (read a$5) {
57
scope @1 [3:4] deps=[mutate x$6_@0.y] {
compiler/forget/src/__tests__/fixtures/hir/ssa-property-alias-mutate-inside-if.expect.md
+2
-1
@@ -39,6 +39,7 @@ bb1:
39
[10] Return freeze x$7_@0:TObject
40
scope0 [1:10]:
41
- dependency: read a$6
42
+ - dependency: mutate x$7_@0.z
43
scope1 [7:8]:
44
- dependency: mutate x$7_@0.z
45
```
@@ -49,7 +50,7 @@ scope1 [7:8]:
50
function foo(
51
a,
52
) {
52
- scope @0 [1:10] deps=[read a$6] {
53
+ scope @0 [1:10] deps=[read a$6, mutate x$7_@0.z] {
54
[1] Const mutate x$7_@0:TObject[1:10] = Object { }
55
if (read a$6) {
56
[3] Const mutate y$8_@0:TObject[1:10] = Object { }
compiler/forget/src/__tests__/fixtures/hir/type-test-field-store.expect.md
+3
-1
@@ -20,6 +20,8 @@ bb0:
20
[3] Reassign mutate x$4_@0.t[1:4] = read q$5_@1:TObject
21
[4] Const mutate z$6:TObject = read x$4_@0.t
22
[5] Return
23
+scope0 [1:4]:
24
+ - dependency: mutate x$4_@0.t
25
scope1 [2:3]:
26
- dependency: mutate x$4_@0.t
27
```
@@ -29,7 +31,7 @@ scope1 [2:3]:
31
```
32
function component(
33
) {
32
- scope @0 [1:4] deps=[] {
34
+ scope @0 [1:4] deps=[mutate x$4_@0.t] {
35
[1] Const mutate x$4_@0:TObject[1:4] = Object { }
36
scope @1 [2:3] deps=[mutate x$4_@0.t] {
37
[2] Const mutate q$5_@1:TObject = Object { }
compiler/forget/src/__tests__/hir-test.ts
+2
@@ -20,6 +20,7 @@ import { toggleLogging } from "../HIR/logger";
20
import run from "../HIR/Pipeline";
21
import { printFunction } from "../HIR/PrintHIR";
22
import { printReactiveFunction } from "../HIR/PrintReactiveFunction";
23
+import { propagateScopeDependencies } from "../HIR/PropagateScopeDependencies";
24
import generateTestsFromFixtures from "./test-utils/generateTestsFromFixtures";
25
26
function wrapWithTripleBackticks(s: string, ext?: string) {
@@ -150,6 +151,7 @@ function transform(text: string, file: string): Array<TestOutput> {
151
152
const reactiveFunction = buildReactiveFunction(ir);
153
flattenReactiveLoops(reactiveFunction);
154
+ propagateScopeDependencies(reactiveFunction);
155
const scopes = printReactiveFunction(reactiveFunction);
156
157
const textHIR = printFunction(ir);