diff --git a/compiler/forget/src/HIR/PropagateScopeDependencies.ts b/compiler/forget/src/HIR/PropagateScopeDependencies.ts new file mode 100644 index 0000000000..c51028cfb9 --- /dev/null +++ b/compiler/forget/src/HIR/PropagateScopeDependencies.ts @@ -0,0 +1,78 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { assertExhaustive } from "../Common/utils"; +import { Place, ReactiveBasicBlock, ReactiveFunction } from "./HIR"; + +/** + * Propagates the dependencies of each scope to its parent scope(s). + */ +export function propagateScopeDependencies(fn: ReactiveFunction): void { + const dependencies: Set = new Set(); + visit(fn.body, dependencies); +} + +function visit(block: ReactiveBasicBlock, dependencies: Set): void { + for (const item of block) { + switch (item.kind) { + case "block": { + visit(item.instructions, item.dependencies); + for (const dep of item.dependencies) { + dependencies.add(dep); + } + break; + } + case "instruction": { + break; + } + case "terminal": { + const terminal = item.terminal; + switch (terminal.kind) { + case "break": + case "continue": + case "return": + case "throw": { + break; + } + case "for": { + visit(terminal.loop, dependencies); + break; + } + case "while": { + visit(terminal.loop, dependencies); + break; + } + case "if": { + visit(terminal.consequent, dependencies); + if (terminal.alternate !== null) { + visit(terminal.alternate, dependencies); + } + break; + } + case "switch": { + for (const case_ of terminal.cases) { + if (case_.block !== undefined) { + visit(case_.block, dependencies); + } + } + break; + } + default: { + assertExhaustive( + terminal, + `Unexpected terminal kind '${(terminal as any).kind}'` + ); + } + } + break; + } + default: { + assertExhaustive(item, `Unexpected item`); + } + } + } +} diff --git a/compiler/forget/src/__tests__/fixtures/hir/call.expect.md b/compiler/forget/src/__tests__/fixtures/hir/call.expect.md index 7825e874c8..493f22d46a 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/call.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/call.expect.md @@ -52,6 +52,8 @@ bb0: [7] Const mutate $14:TPrimitive = "div" [8] Const mutate $15_@2 = JSX [9] Return read $15_@2 +scope0 [1:7]: + - dependency: read $12:TPrimitive scope1 [5:6]: - dependency: read $12:TPrimitive scope2 [8:9]: @@ -66,7 +68,7 @@ scope2 [8:9]: function Component( props, ) { - scope @0 [1:7] deps=[] { + scope @0 [1:7] deps=[read $12:TPrimitive] { [1] Const mutate a$10_@0:TObject[1:7] = Array [] [2] Const mutate b$11_@0:TObject[1:7] = Object { } [3] Call mutate foo$4:TFunction(mutate a$10_@0:TObject, mutate b$11_@0:TObject) diff --git a/compiler/forget/src/__tests__/fixtures/hir/component.expect.md b/compiler/forget/src/__tests__/fixtures/hir/component.expect.md index 51445ee584..8315adb173 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/component.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/component.expect.md @@ -100,6 +100,9 @@ bb2: [40] Const mutate $73 = "\n " [41] Const mutate $74_@5 = JSX {read $68}{read $71_@4}{read $72}{freeze renderedItems$32_@0:TFunction}{read $73} [42] Return read $74_@5 +scope0 [3:33]: + - dependency: read $34:TPrimitive + - dependency: read maxItems$31:TProp scope2 [6:7]: - dependency: read $34:TPrimitive - dependency: read maxItems$31:TProp @@ -126,7 +129,7 @@ function Component( ) { [1] Const mutate items$30:TProp = read props$29.items [2] Const mutate maxItems$31:TProp = read props$29.maxItems - scope @0 [3:33] deps=[] { + scope @0 [3:33] deps=[read $34:TPrimitive, read maxItems$31:TProp] { [3] Const mutate renderedItems$32_@0:TFunction[3:33] = Array [] [4] Const mutate seen$33_@0:TFunction[3:33] = New mutate Set$6() [5] Const mutate $34:TPrimitive = 0 diff --git a/compiler/forget/src/__tests__/fixtures/hir/constructor.expect.md b/compiler/forget/src/__tests__/fixtures/hir/constructor.expect.md index 45be34d3e9..856ce3e2fe 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/constructor.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/constructor.expect.md @@ -52,6 +52,8 @@ bb0: [7] Const mutate $14:TPrimitive = "div" [8] Const mutate $15_@2 = JSX [9] Return read $15_@2 +scope0 [1:7]: + - dependency: read $12:TPrimitive scope1 [5:6]: - dependency: read $12:TPrimitive scope2 [8:9]: @@ -66,7 +68,7 @@ scope2 [8:9]: function Component( props, ) { - scope @0 [1:7] deps=[] { + scope @0 [1:7] deps=[read $12:TPrimitive] { [1] Const mutate a$10_@0[1:7] = Array [] [2] Const mutate b$11_@0:TObject[1:7] = Object { } [3] New mutate Foo$4(mutate a$10_@0, mutate b$11_@0:TObject) diff --git a/compiler/forget/src/__tests__/fixtures/hir/dependencies.expect.md b/compiler/forget/src/__tests__/fixtures/hir/dependencies.expect.md index f67e635a16..f94b06f7cb 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/dependencies.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/dependencies.expect.md @@ -47,6 +47,8 @@ scope0 [1:10]: - dependency: read x$6 - dependency: read x$6 - dependency: read y$7 + - dependency: read y$7 + - dependency: read x$6 scope1 [3:7]: - dependency: read y$7 - dependency: read x$6 @@ -60,7 +62,7 @@ function foo( y, z, ) { - scope @0 [1:10] deps=[read z$8, read x$6, read x$6, read y$7] { + scope @0 [1:10] deps=[read z$8, read x$6, read x$6, read y$7, read y$7, read x$6] { [1] Const mutate items$9_@0:TFunction[1:10] = Array [read z$8] [2] Call mutate items$9_@0.push(read x$6) scope @1 [3:7] deps=[read y$7, read x$6] { diff --git a/compiler/forget/src/__tests__/fixtures/hir/overlapping-scopes-shadowed.expect.md b/compiler/forget/src/__tests__/fixtures/hir/overlapping-scopes-shadowed.expect.md index a6a3ea2588..81f93b377b 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/overlapping-scopes-shadowed.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/overlapping-scopes-shadowed.expect.md @@ -20,6 +20,9 @@ bb0: [3] Call mutate y$8_@1.push(read b$6) [4] Call mutate x$7_@0.push(read a$5) [5] Return +scope0 [1:5]: + - dependency: read b$6 + - dependency: read a$5 scope1 [2:4]: - dependency: read b$6 - dependency: read a$5 @@ -32,7 +35,7 @@ function foo( a, b, ) { - scope @0 [1:5] deps=[] { + scope @0 [1:5] deps=[read b$6, read a$5] { [1] Const mutate x$7_@0:TFunction[1:5] = Array [] scope @1 [2:4] deps=[read b$6, read a$5] { [2] Const mutate y$8_@1:TFunction[2:4] = Array [] diff --git a/compiler/forget/src/__tests__/fixtures/hir/overlapping-scopes-shadowing-within-block.expect.md b/compiler/forget/src/__tests__/fixtures/hir/overlapping-scopes-shadowing-within-block.expect.md index 495405d899..e627aad4dc 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/overlapping-scopes-shadowing-within-block.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/overlapping-scopes-shadowing-within-block.expect.md @@ -42,6 +42,10 @@ bb1: [11] Return freeze x$11_@0:TFunction scope0 [1:11]: - dependency: read a$8 + - dependency: read c$10 + - dependency: read b$9 + - dependency: read $13:TPrimitive + - dependency: freeze y$12_@1:TFunction scope1 [3:7]: - dependency: read c$10 - dependency: read b$9 @@ -58,7 +62,7 @@ function foo( b, c, ) { - scope @0 [1:11] deps=[read a$8] { + scope @0 [1:11] deps=[read a$8, read c$10, read b$9, read $13:TPrimitive, freeze y$12_@1:TFunction] { [1] Const mutate x$11_@0:TFunction[1:11] = Array [] if (read a$8) { scope @1 [3:7] deps=[read c$10, read b$9] { diff --git a/compiler/forget/src/__tests__/fixtures/hir/reactive-scope-grouping.expect.md b/compiler/forget/src/__tests__/fixtures/hir/reactive-scope-grouping.expect.md index 60467e613a..ceffb04a01 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/reactive-scope-grouping.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/reactive-scope-grouping.expect.md @@ -24,6 +24,8 @@ bb0: [4] Call mutate y$5_@1.push(mutate z$6_@1:TObject) [5] Reassign mutate x$4_@0.y[1:6] = read y$5_@1:TFunction [6] Return freeze x$4_@0:TObject +scope0 [1:6]: + - dependency: mutate x$4_@0.y scope1 [2:5]: - dependency: mutate x$4_@0.y ``` @@ -33,7 +35,7 @@ scope1 [2:5]: ``` function foo( ) { - scope @0 [1:6] deps=[] { + scope @0 [1:6] deps=[mutate x$4_@0.y] { [1] Const mutate x$4_@0:TObject[1:6] = Object { } scope @1 [2:5] deps=[mutate x$4_@0.y] { [2] Const mutate y$5_@1:TFunction[2:5] = Array [] diff --git a/compiler/forget/src/__tests__/fixtures/hir/reactive-scopes-if.expect.md b/compiler/forget/src/__tests__/fixtures/hir/reactive-scopes-if.expect.md index 5da3098471..f22fd0098f 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/reactive-scopes-if.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/reactive-scopes-if.expect.md @@ -40,6 +40,9 @@ bb1: scope0 [1:11]: - dependency: read c$10 - dependency: read a$8 + - dependency: read b$9 + - dependency: read $13:TPrimitive + - dependency: freeze y$12_@1:TFunction scope1 [3:5]: - dependency: read b$9 scope2 [6:7]: @@ -55,7 +58,7 @@ function foo( b, c, ) { - scope @0 [1:11] deps=[read c$10, read a$8] { + scope @0 [1:11] deps=[read c$10, read a$8, read b$9, read $13:TPrimitive, freeze y$12_@1:TFunction] { [1] Const mutate x$11_@0:TFunction[1:11] = Array [] if (read a$8) { scope @1 [3:5] deps=[read b$9] { diff --git a/compiler/forget/src/__tests__/fixtures/hir/reassignment.expect.md b/compiler/forget/src/__tests__/fixtures/hir/reassignment.expect.md index 7ad7efeb6e..44c6af6f4c 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/reassignment.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/reassignment.expect.md @@ -31,6 +31,9 @@ bb0: [8] Return read $11_@3 scope0 [1:7]: - dependency: read props$6.p0 + - dependency: read Component$0 + - dependency: freeze x$9_@1 + - dependency: read props$6.p1 scope2 [5:6]: - dependency: read Component$0 - dependency: freeze x$9_@1 @@ -47,7 +50,7 @@ scope3 [7:8]: function Component( props, ) { - scope @0 [1:7] deps=[read props$6.p0] { + scope @0 [1:7] deps=[read props$6.p0, read Component$0, freeze x$9_@1, read props$6.p1] { [1] Const mutate x$7_@0:TFunction[1:7] = Array [] [2] Call mutate x$7_@0.push(read props$6.p0) [3] Const mutate y$8_@0:TFunction[1:7] = read x$7_@0:TFunction diff --git a/compiler/forget/src/__tests__/fixtures/hir/ssa-call-jsx-2.expect.md b/compiler/forget/src/__tests__/fixtures/hir/ssa-call-jsx-2.expect.md index c7fad4c78c..5f21a03946 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/ssa-call-jsx-2.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/ssa-call-jsx-2.expect.md @@ -62,6 +62,8 @@ bb1: [10] Const mutate $19:TPrimitive = "div" [11] Const mutate $20_@3 = JSX [12] Return read $20_@3 +scope0 [1:10]: + - dependency: read $14:TPrimitive scope2 [7:8]: - dependency: read $14:TPrimitive scope3 [11:12]: @@ -76,7 +78,7 @@ scope3 [11:12]: function Component( props, ) { - scope @0 [1:10] deps=[] { + scope @0 [1:10] deps=[read $14:TPrimitive] { [1] Const mutate a$11_@0[1:10] = Array [] [2] Const mutate b$12_@0:TObject[1:10] = Object { } [3] Call mutate foo$4:TFunction(mutate a$11_@0, mutate b$12_@0:TObject) diff --git a/compiler/forget/src/__tests__/fixtures/hir/ssa-call-jsx.expect.md b/compiler/forget/src/__tests__/fixtures/hir/ssa-call-jsx.expect.md index b74dad9824..54d7127f27 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/ssa-call-jsx.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/ssa-call-jsx.expect.md @@ -52,6 +52,8 @@ bb0: [7] Const mutate $14:TPrimitive = "div" [8] Const mutate $15_@2 = JSX [9] Return read $15_@2 +scope0 [1:7]: + - dependency: read $12:TPrimitive scope1 [5:6]: - dependency: read $12:TPrimitive scope2 [8:9]: @@ -66,7 +68,7 @@ scope2 [8:9]: function Component( props, ) { - scope @0 [1:7] deps=[] { + scope @0 [1:7] deps=[read $12:TPrimitive] { [1] Const mutate a$10_@0[1:7] = Array [] [2] Const mutate b$11_@0:TObject[1:7] = Object { } [3] Call mutate foo$4:TFunction(mutate a$10_@0, mutate b$11_@0:TObject) diff --git a/compiler/forget/src/__tests__/fixtures/hir/ssa-property-alias-if.expect.md b/compiler/forget/src/__tests__/fixtures/hir/ssa-property-alias-if.expect.md index 7afc4a4c4e..0cfd14f33c 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/ssa-property-alias-if.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/ssa-property-alias-if.expect.md @@ -37,6 +37,8 @@ bb1: [9] Return freeze x$6_@0:TObject scope0 [1:9]: - dependency: read a$5 + - dependency: mutate x$6_@0.y + - dependency: mutate x$6_@0.z scope1 [3:4]: - dependency: mutate x$6_@0.y scope2 [6:7]: @@ -49,7 +51,7 @@ scope2 [6:7]: function foo( a, ) { - scope @0 [1:9] deps=[read a$5] { + scope @0 [1:9] deps=[read a$5, mutate x$6_@0.y, mutate x$6_@0.z] { [1] Const mutate x$6_@0:TObject[1:9] = Object { } if (read a$5) { scope @1 [3:4] deps=[mutate x$6_@0.y] { diff --git a/compiler/forget/src/__tests__/fixtures/hir/ssa-property-alias-mutate-inside-if.expect.md b/compiler/forget/src/__tests__/fixtures/hir/ssa-property-alias-mutate-inside-if.expect.md index 9b1c42ccd6..29ecf9a2a5 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/ssa-property-alias-mutate-inside-if.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/ssa-property-alias-mutate-inside-if.expect.md @@ -39,6 +39,7 @@ bb1: [10] Return freeze x$7_@0:TObject scope0 [1:10]: - dependency: read a$6 + - dependency: mutate x$7_@0.z scope1 [7:8]: - dependency: mutate x$7_@0.z ``` @@ -49,7 +50,7 @@ scope1 [7:8]: function foo( a, ) { - scope @0 [1:10] deps=[read a$6] { + scope @0 [1:10] deps=[read a$6, mutate x$7_@0.z] { [1] Const mutate x$7_@0:TObject[1:10] = Object { } if (read a$6) { [3] Const mutate y$8_@0:TObject[1:10] = Object { } diff --git a/compiler/forget/src/__tests__/fixtures/hir/type-test-field-store.expect.md b/compiler/forget/src/__tests__/fixtures/hir/type-test-field-store.expect.md index 618a26998c..faa84d881a 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/type-test-field-store.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/type-test-field-store.expect.md @@ -20,6 +20,8 @@ bb0: [3] Reassign mutate x$4_@0.t[1:4] = read q$5_@1:TObject [4] Const mutate z$6:TObject = read x$4_@0.t [5] Return +scope0 [1:4]: + - dependency: mutate x$4_@0.t scope1 [2:3]: - dependency: mutate x$4_@0.t ``` @@ -29,7 +31,7 @@ scope1 [2:3]: ``` function component( ) { - scope @0 [1:4] deps=[] { + scope @0 [1:4] deps=[mutate x$4_@0.t] { [1] Const mutate x$4_@0:TObject[1:4] = Object { } scope @1 [2:3] deps=[mutate x$4_@0.t] { [2] Const mutate q$5_@1:TObject = Object { } diff --git a/compiler/forget/src/__tests__/hir-test.ts b/compiler/forget/src/__tests__/hir-test.ts index 86f2802c75..376a9afcea 100644 --- a/compiler/forget/src/__tests__/hir-test.ts +++ b/compiler/forget/src/__tests__/hir-test.ts @@ -20,6 +20,7 @@ import { toggleLogging } from "../HIR/logger"; import run from "../HIR/Pipeline"; import { printFunction } from "../HIR/PrintHIR"; import { printReactiveFunction } from "../HIR/PrintReactiveFunction"; +import { propagateScopeDependencies } from "../HIR/PropagateScopeDependencies"; import generateTestsFromFixtures from "./test-utils/generateTestsFromFixtures"; function wrapWithTripleBackticks(s: string, ext?: string) { @@ -150,6 +151,7 @@ function transform(text: string, file: string): Array { const reactiveFunction = buildReactiveFunction(ir); flattenReactiveLoops(reactiveFunction); + propagateScopeDependencies(reactiveFunction); const scopes = printReactiveFunction(reactiveFunction); const textHIR = printFunction(ir);