diff --git a/compiler/forget/src/HIR/HIRTreeVisitor.ts b/compiler/forget/src/HIR/HIRTreeVisitor.ts index ad5453bc10..ef26a430c2 100644 --- a/compiler/forget/src/HIR/HIRTreeVisitor.ts +++ b/compiler/forget/src/HIR/HIRTreeVisitor.ts @@ -66,7 +66,9 @@ class Driver { switch (terminal.kind) { case "return": { const value = - terminal.value != null ? this.visitPlace(terminal.value) : null; + terminal.value != null + ? this.visitPlace(terminal.value, terminal.id) + : null; this.visitor.visitTerminalId(terminal.id); this.visitor.appendBlock( blockValue, @@ -78,7 +80,7 @@ class Driver { break; } case "throw": { - const value = this.visitPlace(terminal.value); + const value = this.visitPlace(terminal.value, terminal.id); this.visitor.visitTerminalId(terminal.id); this.visitor.appendBlock( blockValue, @@ -90,7 +92,7 @@ class Driver { break; } case "if": { - const test = this.visitPlace(terminal.test); + const test = this.visitPlace(terminal.test, terminal.id); const fallthroughId = terminal.fallthrough !== null && !this.cx.isScheduled(terminal.fallthrough) @@ -152,7 +154,7 @@ class Driver { break; } case "switch": { - const test = this.visitPlace(terminal.test); + const test = this.visitPlace(terminal.test, terminal.id); const fallthroughId = terminal.fallthrough !== null && !this.cx.isScheduled(terminal.fallthrough) @@ -166,7 +168,10 @@ class Driver { this.visitor.visitTerminalId(terminal.id); const cases: Array = []; [...terminal.cases].reverse().forEach((case_, index) => { - const test = case_.test !== null ? this.visitPlace(case_.test) : null; + const test = + case_.test !== null + ? this.visitPlace(case_.test, terminal.id) + : null; let consequent; if (this.cx.isScheduled(case_.block)) { @@ -241,7 +246,7 @@ class Driver { // body.length === bodyLength, // "Expected test to produce only temporaries" // ); - const testValue = this.visitPlace(testTerminal.test); + const testValue = this.visitPlace(testTerminal.test, terminal.id); const fallthroughId = terminal.fallthrough !== null && !this.cx.isScheduled(terminal.fallthrough) @@ -391,13 +396,13 @@ class Driver { } visitInstr(instr: Instruction, blockValue: TBlock): void { - const value = this.visitor.visitValue(instr.value); + const value = this.visitor.visitValue(instr.value, instr.id); const item = this.visitor.visitInstruction(instr, value); this.visitor.appendBlock(blockValue, item); } - visitPlace(place: Place): TValue { - return this.visitor.visitValue(place); + visitPlace(place: Place, id: InstructionId): TValue { + return this.visitor.visitValue(place, id); } } @@ -642,7 +647,7 @@ export interface Visitor { * Convert an InstructionValue into the visitor's own representation * of a value. */ - visitValue(value: InstructionValue): TValue; + visitValue(value: InstructionValue, id: InstructionId): TValue; /** * Convert an Instruction into the visitor's own representation of diff --git a/compiler/forget/src/HIR/InferReactiveScopes.ts b/compiler/forget/src/HIR/InferReactiveScopes.ts index 94e188f486..3a74f83d12 100644 --- a/compiler/forget/src/HIR/InferReactiveScopes.ts +++ b/compiler/forget/src/HIR/InferReactiveScopes.ts @@ -17,7 +17,10 @@ import { ScopeId, } from "./HIR"; import { BlockTerminal, Visitor, visitTree } from "./HIRTreeVisitor"; -import { eachInstructionOperand } from "./visitors"; +import { + eachInstructionOperand, + eachInstructionValueOperand, +} from "./visitors"; /** * This is a second (final) stage of constructing reactive scopes. Prior to this pass, @@ -249,13 +252,28 @@ class MergeOverlappingReactiveScopesVisitor enterBlock(): void { this.scopes.push(new BlockScope()); } - visitValue(value: InstructionValue): void {} - visitInstruction(instruction: Instruction, value: void): void { - const scope = getInstructionScope(instruction); - if (scope !== null && instruction.id < scope.range.end) { - this.visitScope(scope); + visitValue(value: InstructionValue, id: InstructionId): void { + this.visitId(id); + for (const operand of eachInstructionValueOperand(value)) { + if ( + operand.identifier.scope !== null && + id >= operand.identifier.scope.range.start && + id < operand.identifier.scope.range.end + ) { + this.visitScope(operand.identifier.scope); + } } + } + visitInstruction(instruction: Instruction, value: void): void { this.visitId(instruction.id); + if ( + instruction.lvalue !== null && + instruction.lvalue.place.identifier.scope !== null && + instruction.id >= instruction.lvalue.place.identifier.scope.range.start && + instruction.id < instruction.lvalue.place.identifier.scope.range.end + ) { + this.visitScope(instruction.lvalue.place.identifier.scope); + } } visitTerminalId(id: InstructionId): void { this.visitId(id); diff --git a/compiler/forget/src/HIR/visitors.ts b/compiler/forget/src/HIR/visitors.ts index d9652c4096..441a27b8ca 100644 --- a/compiler/forget/src/HIR/visitors.ts +++ b/compiler/forget/src/HIR/visitors.ts @@ -10,13 +10,18 @@ import { BasicBlock, BlockId, Instruction, + InstructionValue, makeInstructionId, Place, Terminal, } from "./HIR"; export function* eachInstructionOperand(instr: Instruction): Iterable { - const instrValue = instr.value; + yield* eachInstructionValueOperand(instr.value); +} +export function* eachInstructionValueOperand( + instrValue: InstructionValue +): Iterable { switch (instrValue.kind) { case "NewExpression": case "CallExpression": { diff --git a/compiler/forget/src/__tests__/fixtures/hir/extend-scopes-if.expect.md b/compiler/forget/src/__tests__/fixtures/hir/extend-scopes-if.expect.md index a9d8c0750d..1ee3fa4578 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/extend-scopes-if.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/extend-scopes-if.expect.md @@ -33,8 +33,8 @@ bb4: [4] If (read c$9) then:bb6 else:bb1 bb6: predecessor blocks: bb4 - [5] Const mutate $11_@0[1:8] = 0 - [6] Call mutate x$10_@0.push(read $11_@0) + [5] Const mutate $11_@1 = 0 + [6] Call mutate x$10_@0.push(read $11_@1) [7] Goto bb1 bb1: predecessor blocks: bb6 bb4 bb2 bb0 @@ -67,8 +67,8 @@ flowchart TB end subgraph bb6 bb6_instrs[" - [5] Const mutate $11_@0[1:8] = 0 - [6] Call mutate x$10_@0.push(read $11_@0) + [5] Const mutate $11_@1 = 0 + [6] Call mutate x$10_@0.push(read $11_@1) "] bb6_instrs --> bb6_terminal(["Goto"]) end diff --git a/compiler/forget/src/__tests__/fixtures/hir/overlapping-scopes-interleaved-by-terminal.expect.md b/compiler/forget/src/__tests__/fixtures/hir/overlapping-scopes-interleaved-by-terminal.expect.md new file mode 100644 index 0000000000..784d287dcc --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/overlapping-scopes-interleaved-by-terminal.expect.md @@ -0,0 +1,72 @@ + +## Input + +```javascript +function foo(a, b, c) { + const x = []; + const y = []; + + if (x) { + } + + y.push(a); + x.push(b); +} + +``` + +## HIR + +``` +bb0: + [1] Const mutate x$9_@0[1:6] = Array [] + [2] Const mutate y$10_@0[1:6] = Array [] + [3] If (read x$9_@0) then:bb1 else:bb1 +bb1: + predecessor blocks: bb0 + [4] Call mutate y$10_@0.push(read a$6) + [5] Call mutate x$9_@0.push(read b$7) + [6] Return +``` + +### CFG + +```mermaid +flowchart TB + %% Basic Blocks + subgraph bb0 + bb0_instrs[" + [1] Const mutate x$9_@0[1:6] = Array [] + [2] Const mutate y$10_@0[1:6] = Array [] + "] + bb0_instrs --> bb0_terminal(["If (read x$9_@0)"]) + end + subgraph bb1 + bb1_instrs[" + [4] Call mutate y$10_@0.push(read a$6) + [5] Call mutate x$9_@0.push(read b$7) + "] + bb1_instrs --> bb1_terminal(["Return"]) + end + + %% Jumps + bb0_terminal -- "then" --> bb1 + bb0_terminal -- "else" --> bb1 + +``` + +## Code + +```javascript +function foo$0(a$1, b$2, c$3) { + const x$4 = []; + const y$5 = []; + bb1: if (x$4) { + } + + y$5.push(a$1); + x$4.push(b$2); +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/hir/overlapping-scopes-interleaved-by-terminal.js b/compiler/forget/src/__tests__/fixtures/hir/overlapping-scopes-interleaved-by-terminal.js new file mode 100644 index 0000000000..66d7e1ce04 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/overlapping-scopes-interleaved-by-terminal.js @@ -0,0 +1,10 @@ +function foo(a, b, c) { + const x = []; + const y = []; + + if (x) { + } + + y.push(a); + x.push(b); +} 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 0f73e4d141..c34951a726 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 @@ -34,8 +34,8 @@ bb4: bb3: predecessor blocks: bb4 bb2 [7] Const mutate $13_@2 = "div" - [8] Const mutate $15_@0[1:11] = JSX {freeze y$12_@1} - [9] Call mutate x$11_@0.push(read $15_@0) + [8] Const mutate $15_@3 = JSX {freeze y$12_@1} + [9] Call mutate x$11_@0.push(read $15_@3) [10] Goto bb1 bb1: predecessor blocks: bb3 bb0 @@ -68,8 +68,8 @@ flowchart TB subgraph bb3 bb3_instrs[" [7] Const mutate $13_@2 = 'div' - [8] Const mutate $15_@0[1:11] = JSX {freeze y$12_@1} - [9] Call mutate x$11_@0.push(read $15_@0) + [8] Const mutate $15_@3 = JSX {freeze y$12_@1} + [9] Call mutate x$11_@0.push(read $15_@3) "] bb3_instrs --> bb3_terminal(["Goto"]) end diff --git a/compiler/forget/src/__tests__/fixtures/hir/ssa-shadowing.expect.md b/compiler/forget/src/__tests__/fixtures/hir/ssa-shadowing.expect.md index ab45dd1b9c..e9f4967c3e 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/ssa-shadowing.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/ssa-shadowing.expect.md @@ -47,7 +47,7 @@ function log$0() {} ``` bb0: - [1] Let mutate str$6_@2[0:9] = "" + [1] Let mutate str$6_@0[1:8] = "" [2] If (read cond$5) then:bb2 else:bb3 bb2: predecessor blocks: bb0 @@ -56,12 +56,12 @@ bb2: [5] Goto bb1 bb3: predecessor blocks: bb0 - [6] Reassign mutate str$8_@2[0:9] = "fallthrough test" + [6] Reassign mutate str$8_@0[1:8] = "fallthrough test" [7] Goto bb1 bb1: predecessor blocks: bb2 bb3 - str$10_@2[0:9]: phi(bb2: str$6_@2, bb3: str$8_@2) - [8] Call mutate log$4_@2(read str$10_@2) + str$10_@0[1:8]: phi(bb2: str$6_@0, bb3: str$8_@0) + [8] Call mutate log$4_@2(read str$10_@0) [9] Return ``` @@ -72,7 +72,7 @@ flowchart TB %% Basic Blocks subgraph bb0 bb0_instrs[" - [1] Let mutate str$6_@2[0:9] = '' + [1] Let mutate str$6_@0[1:8] = '' "] bb0_instrs --> bb0_terminal(["If (read cond$5)"]) end @@ -85,13 +85,13 @@ flowchart TB end subgraph bb3 bb3_instrs[" - [6] Reassign mutate str$8_@2[0:9] = 'fallthrough test' + [6] Reassign mutate str$8_@0[1:8] = 'fallthrough test' "] bb3_instrs --> bb3_terminal(["Goto"]) end subgraph bb1 bb1_instrs[" - [8] Call mutate log$4_@2(read str$10_@2) + [8] Call mutate log$4_@2(read str$10_@0) "] bb1_instrs --> bb1_terminal(["Return"]) end diff --git a/compiler/forget/src/__tests__/fixtures/hir/ssa-switch.expect.md b/compiler/forget/src/__tests__/fixtures/hir/ssa-switch.expect.md index 12e2979b3e..8e89919f48 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/ssa-switch.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/ssa-switch.expect.md @@ -39,23 +39,23 @@ bb0: Default: bb2 bb5: predecessor blocks: bb0 - [7] Const mutate $15_@6[7:17] = 1 - [8] Reassign mutate x$16_@6[7:17] = Binary read x$10_@0 + read $15_@6 + [7] Const mutate $15_@5 = 1 + [8] Reassign mutate x$16_@6[8:17] = Binary read x$10_@0 + read $15_@5 [9] Goto bb1 bb3: predecessor blocks: bb0 - [10] Const mutate $17_@6[7:17] = 2 - [11] Reassign mutate x$18_@6[7:17] = Binary read x$10_@0 + read $17_@6 + [10] Const mutate $17_@7 = 2 + [11] Reassign mutate x$18_@6[8:17] = Binary read x$10_@0 + read $17_@7 [12] Goto bb1 bb2: predecessor blocks: bb0 [13] Const mutate $19_@8 = 3 - [14] Reassign mutate x$20_@6[7:17] = Binary read x$10_@0 + read $19_@8 + [14] Reassign mutate x$20_@6[8:17] = Binary read x$10_@0 + read $19_@8 [15] Goto bb1 bb1: predecessor blocks: bb5 bb3 bb2 - x$21_@6[7:17]: phi(bb5: x$16_@6, bb3: x$18_@6, bb2: x$20_@6) - [16] Let mutate y$22_@6[7:17] = read x$21_@6 + x$21_@6[8:17]: phi(bb5: x$16_@6, bb3: x$18_@6, bb2: x$20_@6) + [16] Let mutate y$22_@6[8:17] = read x$21_@6 [17] Return ``` @@ -76,28 +76,28 @@ flowchart TB end subgraph bb5 bb5_instrs[" - [7] Const mutate $15_@6[7:17] = 1 - [8] Reassign mutate x$16_@6[7:17] = Binary read x$10_@0 + read $15_@6 + [7] Const mutate $15_@5 = 1 + [8] Reassign mutate x$16_@6[8:17] = Binary read x$10_@0 + read $15_@5 "] bb5_instrs --> bb5_terminal(["Goto"]) end subgraph bb3 bb3_instrs[" - [10] Const mutate $17_@6[7:17] = 2 - [11] Reassign mutate x$18_@6[7:17] = Binary read x$10_@0 + read $17_@6 + [10] Const mutate $17_@7 = 2 + [11] Reassign mutate x$18_@6[8:17] = Binary read x$10_@0 + read $17_@7 "] bb3_instrs --> bb3_terminal(["Goto"]) end subgraph bb2 bb2_instrs[" [13] Const mutate $19_@8 = 3 - [14] Reassign mutate x$20_@6[7:17] = Binary read x$10_@0 + read $19_@8 + [14] Reassign mutate x$20_@6[8:17] = Binary read x$10_@0 + read $19_@8 "] bb2_instrs --> bb2_terminal(["Goto"]) end subgraph bb1 bb1_instrs[" - [16] Let mutate y$22_@6[7:17] = read x$21_@6 + [16] Let mutate y$22_@6[8:17] = read x$21_@6 "] bb1_instrs --> bb1_terminal(["Return"]) end diff --git a/compiler/forget/src/__tests__/fixtures/hir/ssa-while.expect.md b/compiler/forget/src/__tests__/fixtures/hir/ssa-while.expect.md index ac19040d62..3516e2b666 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/ssa-while.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/ssa-while.expect.md @@ -27,8 +27,8 @@ bb1: [5] If (read $8_@2) then:bb3 else:bb2 bb3: predecessor blocks: bb1 - [6] Const mutate $9_@0[0:9] = 1 - [7] Reassign mutate x$10_@0[0:9] = Binary read x$7_@0 + read $9_@0 + [6] Const mutate $9_@3 = 1 + [7] Reassign mutate x$10_@0[0:9] = Binary read x$7_@0 + read $9_@3 [8] Goto(Continue) bb1 bb2: predecessor blocks: bb1 @@ -55,8 +55,8 @@ flowchart TB end subgraph bb3 bb3_instrs[" - [6] Const mutate $9_@0[0:9] = 1 - [7] Reassign mutate x$10_@0[0:9] = Binary read x$7_@0 + read $9_@0 + [6] Const mutate $9_@3 = 1 + [7] Reassign mutate x$10_@0[0:9] = Binary read x$7_@0 + read $9_@3 "] bb3_instrs --> bb3_terminal(["Goto"]) end