@samitouri / QOS-React-2 / commits / 972caf5bf1

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);