From 379251c65f39039ace1d2e5a80bdb5938cf3427c Mon Sep 17 00:00:00 2001 From: Sathya Gunasekaran Date: Tue, 18 Oct 2022 18:45:01 +0100 Subject: [PATCH] Add SSA-ify pass The algorithm is described in detail here: https://pp.info.uni-karlsruhe.de/uploads/publikationen/braun13cc.pdf Note that the SSA form generated is not minimal. A follow on RedundantPhiElimination pass is required to prune the graph. --- compiler/forget/src/HIR/HIR.ts | 8 + compiler/forget/src/HIR/HIRBuilder.ts | 76 +++- compiler/forget/src/HIR/PrintHIR.ts | 25 ++ compiler/forget/src/HIR/SSAify.ts | 333 ++++++++++++++++++ .../fixtures/hir/component.expect.md | 7 + .../fixtures/hir/conditional-break.expect.md | 10 + .../hir/conditional-on-mutable.expect.md | 8 + ...reeze-possibly-mutable-arguments.expect.md | 3 + .../hir/independent-across-if.expect.md | 2 + .../hir/interdependent-across-if.expect.md | 2 + .../hir/mutable-lifetime-loops.expect.md | 7 + .../mutable-lifetime-with-aliasing.expect.md | 5 + .../hir/reassignment-conditional.expect.md | 2 + .../fixtures/hir/reverse-postorder.expect.md | 8 + .../__tests__/fixtures/hir/simple.expect.md | 2 + .../hir/ssa-complex-multiple-if.expect.md | 70 ++++ .../fixtures/hir/ssa-complex-multiple-if.js | 12 + .../hir/ssa-complex-single-if.expect.md | 52 +++ .../fixtures/hir/ssa-complex-single-if.js | 9 + .../__tests__/fixtures/hir/ssa-for.expect.md | 46 +++ .../src/__tests__/fixtures/hir/ssa-for.js | 7 + .../fixtures/hir/ssa-if-else.expect.md | 55 +++ .../src/__tests__/fixtures/hir/ssa-if-else.js | 10 + .../fixtures/hir/ssa-return.expect.md | 47 +++ .../src/__tests__/fixtures/hir/ssa-return.js | 8 + .../fixtures/hir/ssa-simple-phi.expect.md | 59 ++++ .../__tests__/fixtures/hir/ssa-simple-phi.js | 11 + .../fixtures/hir/ssa-simple.expect.md | 31 ++ .../src/__tests__/fixtures/hir/ssa-simple.js | 4 + .../fixtures/hir/ssa-single-if.expect.md | 46 +++ .../__tests__/fixtures/hir/ssa-single-if.js | 8 + .../fixtures/hir/ssa-switch.expect.md | 86 +++++ .../src/__tests__/fixtures/hir/ssa-switch.js | 19 + .../fixtures/hir/ssa-throw.expect.md | 46 +++ .../src/__tests__/fixtures/hir/ssa-throw.js | 7 + .../fixtures/hir/ssa-while.expect.md | 47 +++ .../src/__tests__/fixtures/hir/ssa-while.js | 8 + .../hir/switch-non-final-default.expect.md | 3 + .../__tests__/fixtures/hir/switch.expect.md | 3 + compiler/forget/src/__tests__/hir-test.ts | 4 + 40 files changed, 1193 insertions(+), 3 deletions(-) create mode 100644 compiler/forget/src/HIR/SSAify.ts create mode 100644 compiler/forget/src/__tests__/fixtures/hir/ssa-complex-multiple-if.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/hir/ssa-complex-multiple-if.js create mode 100644 compiler/forget/src/__tests__/fixtures/hir/ssa-complex-single-if.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/hir/ssa-complex-single-if.js create mode 100644 compiler/forget/src/__tests__/fixtures/hir/ssa-for.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/hir/ssa-for.js create mode 100644 compiler/forget/src/__tests__/fixtures/hir/ssa-if-else.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/hir/ssa-if-else.js create mode 100644 compiler/forget/src/__tests__/fixtures/hir/ssa-return.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/hir/ssa-return.js create mode 100644 compiler/forget/src/__tests__/fixtures/hir/ssa-simple-phi.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/hir/ssa-simple-phi.js create mode 100644 compiler/forget/src/__tests__/fixtures/hir/ssa-simple.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/hir/ssa-simple.js create mode 100644 compiler/forget/src/__tests__/fixtures/hir/ssa-single-if.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/hir/ssa-single-if.js create mode 100644 compiler/forget/src/__tests__/fixtures/hir/ssa-switch.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/hir/ssa-switch.js create mode 100644 compiler/forget/src/__tests__/fixtures/hir/ssa-throw.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/hir/ssa-throw.js create mode 100644 compiler/forget/src/__tests__/fixtures/hir/ssa-while.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/hir/ssa-while.js diff --git a/compiler/forget/src/HIR/HIR.ts b/compiler/forget/src/HIR/HIR.ts index 4bd052e6d0..c09eef78c5 100644 --- a/compiler/forget/src/HIR/HIR.ts +++ b/compiler/forget/src/HIR/HIR.ts @@ -94,6 +94,8 @@ export type BasicBlock = { id: BlockId; instructions: Array; terminal: Terminal; + preds: Set; + phis: Set; }; /** @@ -162,6 +164,12 @@ export enum InstructionKind { */ export type InstructionValue = (InstructionData & { path: NodePath }) | Place; +export type Phi = { + kind: "Phi"; + lvalue: LValue; + operands: Map; +}; + export type InstructionData = | { kind: "Primitive"; value: number | boolean | string | null | undefined } | { kind: "JSXText"; value: string } diff --git a/compiler/forget/src/HIR/HIRBuilder.ts b/compiler/forget/src/HIR/HIRBuilder.ts index 5fbfb45c5d..5f754be897 100644 --- a/compiler/forget/src/HIR/HIRBuilder.ts +++ b/compiler/forget/src/HIR/HIRBuilder.ts @@ -114,6 +114,8 @@ export default class HIRBuilder { id: blockId, instructions, terminal: { kind: "return", value: null }, + preds: new Set(), + phis: new Set(), }); // First reduce indirections and prune unreachable blocks let reduced = shrink({ @@ -121,7 +123,9 @@ export default class HIRBuilder { entry: this.#entry, }); // then convert to reverse postorder - return reversePostorderBlocks(reduced); + const blocks = reversePostorderBlocks(reduced); + markPredecessors(blocks); + return blocks; } /** @@ -133,6 +137,8 @@ export default class HIRBuilder { id: blockId, instructions, terminal, + preds: new Set(), + phis: new Set(), }); const nextId = makeBlockId(this.#nextId++); this.#current = newBlock(nextId); @@ -148,6 +154,8 @@ export default class HIRBuilder { id: blockId, instructions, terminal, + preds: new Set(), + phis: new Set(), }); this.#current = continuation; } @@ -166,7 +174,13 @@ export default class HIRBuilder { */ complete(block: WipBlock, terminal: Terminal) { const { id: blockId, instructions } = block; - this.#completed.set(blockId, { id: blockId, instructions, terminal }); + this.#completed.set(blockId, { + id: blockId, + instructions, + terminal, + preds: new Set(), + phis: new Set(), + }); } /** @@ -181,7 +195,13 @@ export default class HIRBuilder { this.#current = newBlock(nextId); const terminal = fn(nextId); const { id: blockId, instructions } = this.#current; - this.#completed.set(blockId, { id: blockId, instructions, terminal }); + this.#completed.set(blockId, { + id: blockId, + instructions, + terminal, + preds: new Set(), + phis: new Set(), + }); this.#current = current; return nextId; } @@ -336,6 +356,8 @@ function shrink(func: HIR): HIR { id: blockId, instructions, terminal, + preds: new Set(), + phis: new Set(), }); } @@ -438,6 +460,54 @@ function reversePostorderBlocks(func: HIR): HIR { }; } +function markPredecessors(func: HIR) { + const visited: Set = new Set(); + function visit(blockId: BlockId, prevBlock?: BasicBlock) { + const block = func.blocks.get(blockId)!; + if (prevBlock) { + block.preds.add(prevBlock); + } + + if (visited.has(blockId)) { + return; + } + visited.add(blockId); + + const { terminal } = block; + + switch (terminal.kind) { + case "return": + case "throw": { + break; + } + case "goto": { + visit(terminal.block, block); + break; + } + case "if": { + const { consequent, alternate } = terminal; + visit(alternate, block); + visit(consequent, block); + break; + } + case "switch": { + const { cases } = terminal; + for (const case_ of [...cases]) { + visit(case_.block, block); + } + break; + } + default: { + assertExhaustive( + terminal, + `Unexpected terminal kind '${(terminal as any).kind}'` + ); + } + } + } + visit(func.entry); +} + /** * If the given block is a simple indirection (empty terminated with a goto), * returns the block being pointed to. Otherwise returns null diff --git a/compiler/forget/src/HIR/PrintHIR.ts b/compiler/forget/src/HIR/PrintHIR.ts index e20eb38dee..679eda65d0 100644 --- a/compiler/forget/src/HIR/PrintHIR.ts +++ b/compiler/forget/src/HIR/PrintHIR.ts @@ -12,6 +12,7 @@ import { InstructionKind, InstructionValue, LValue, + Phi, Place, Terminal, } from "./HIR"; @@ -31,6 +32,16 @@ export default function printHIR( }; for (const [blockId, block] of ir.blocks) { output.push(`bb${blockId}:`); + if (block.preds.size > 0) { + const preds = ["predecessor blocks:"]; + for (const pred of block.preds) { + preds.push(`bb${pred.id}`); + } + push(preds.join(" ")); + } + for (const phi of block.phis) { + push(printPhi(phi)); + } for (const instr of block.instructions) { push(printInstruction(instr)); } @@ -78,6 +89,20 @@ function printInstruction(instr: Instruction): string { } } +function printPhi(phi: Phi): string { + const items = []; + items.push(printLValue(phi.lvalue)); + items.push(": phi("); + const phis = []; + for (const [block, place] of phi.operands) { + phis.push(`bb${block.id}: ${printPlace(place)}`); + } + + items.push(phis.join(", ")); + items.push(")"); + return items.join(""); +} + function printTerminal(terminal: Terminal): Array | string { let value; switch (terminal.kind) { diff --git a/compiler/forget/src/HIR/SSAify.ts b/compiler/forget/src/HIR/SSAify.ts new file mode 100644 index 0000000000..422717389f --- /dev/null +++ b/compiler/forget/src/HIR/SSAify.ts @@ -0,0 +1,333 @@ +import invariant from "invariant"; +import { assertExhaustive } from "../Common/utils"; +import { + BasicBlock, + BlockId, + Effect, + HIRFunction, + IdentifierId, + Instruction, + InstructionKind, + makeIdentifierId, + Phi, + Place, +} from "./HIR"; + +type IncompletePhi = { + old: Place; + new: Place; +}; + +type State = { + defs: Map; + incompletePhis: IncompletePhi[]; +}; + +const unsealedPreds: Map = new Map(); + +class SSABuilder { + #states: Map = new Map(); + #current: BasicBlock | null = null; + visitedBlocks: Set = new Set(); + + // Hack(gsn): Start from the last stored id in HIRBuilder. + // Need to refactor makeTemporary and relevant state out of HIR Builder. + #id = 1000; + + state(): State { + invariant( + this.#current !== null, + "we need to be in a block to access state!" + ); + return this.#states.get(this.#current)!; + } + + // This tries to reuse all existing information about the oldPlace in case there's + // useful information from previous compiler passes. + makePlace(oldPlace: Place): Place { + const identifier = { + ...oldPlace.identifier, + id: makeIdentifierId(this.#id++), + }; + return { + ...oldPlace, + identifier, + }; + } + + makePlaceForPhi(oldPlace: Place): Place { + const identifier = { + ...oldPlace.identifier, + id: makeIdentifierId(this.#id++), + }; + return { + identifier, + kind: "Identifier", + memberPath: null, + effect: Effect.Mutate, + path: null as any, + }; + } + + definePlace(oldPlace: Place): Place { + const newPlace = this.makePlace(oldPlace); + this.state().defs.set(oldPlace.identifier.id, newPlace); + return newPlace; + } + + getPlace(oldPlace: Place): Place { + return this.getPlaceAt(oldPlace, this.#current!); + } + + getPlaceAt(oldPlace: Place, block: BasicBlock): Place { + // check if Place is defined locally + const state = this.#states.get(block)!; + + if (state.defs.has(oldPlace.identifier.id)) { + return state.defs.get(oldPlace.identifier.id)!; + } + + if (block.preds.size == 0) { + // We're at the entry block and haven't found our defintion yet. + throw new Error(`Unable to find \$${oldPlace.identifier.id}`); + } + + if (unsealedPreds.get(block)! > 0) { + // We haven't visited all our predecessors, let's place an incomplete phi + // for now. + const newPlace = this.makePlaceForPhi(oldPlace); + state.incompletePhis.push({ old: oldPlace, new: newPlace }); + state.defs.set(oldPlace.identifier.id, newPlace); + return newPlace; + } + + // Only one predecessor, let's check there + if (block.preds.size == 1) { + const [pred] = block.preds; + const newPlace = this.getPlaceAt(oldPlace, pred); + state.defs.set(oldPlace.identifier.id, newPlace); + return newPlace; + } + + // There are multiple predecessors, we need a phi. + const newPlace = this.makePlaceForPhi(oldPlace); + // Adding a phi may loop back to our block if there is a loop in the CFG. We + // update our defs before adding the phi to terminate the recursion rather than + // looping infinitely. + state.defs.set(oldPlace.identifier.id, newPlace); + this.addPhi(block, oldPlace, newPlace); + + // TODO(gsn): Can we just return `newPlace` rather than looking it up? + // `addPhi` _can_ mutate it, but _will_ it? + return state.defs.get(oldPlace.identifier.id)!; + } + + addPhi(block: BasicBlock, oldPlace: Place, newPlace: Place) { + const predDefs: Map = new Map(); + for (const predBlock of block.preds) { + const predPlace = this.getPlaceAt(oldPlace, predBlock); + predDefs.set(predBlock, predPlace); + } + + const phi: Phi = { + kind: "Phi", + lvalue: { place: newPlace, kind: InstructionKind.Const }, + operands: predDefs, + }; + + block.phis.add(phi); + } + + fixIncompletePhis(block: BasicBlock) { + const state = this.#states.get(block)!; + for (const phi of state.incompletePhis) { + this.addPhi(block, phi.old, phi.new); + } + } + + startBlock(block: BasicBlock) { + this.#current = block; + this.#states.set(block, { + defs: new Map(), + incompletePhis: [], + }); + } + + print() { + const text = []; + for (const [block, state] of this.#states) { + text.push(`bb${block.id}:`); + for (const [id, place] of state.defs) { + text.push(` \$${id} = \$${place.identifier.id}`); + } + + for (const incompletePhi of state.incompletePhis) { + text.push( + ` iphi \$${incompletePhi.new.identifier} = \$${incompletePhi.old.identifier}` + ); + } + } + + text.push(`current block: bb${this.#current?.id}`); + console.log(text.join("\n")); + } +} + +export default function buildSSA(func: HIRFunction) { + const builder = new SSABuilder(); + + function visit(blockId: BlockId) { + const block = func.body.blocks.get(blockId)!; + if (builder.visitedBlocks.has(block)) { + return; + } + builder.visitedBlocks.add(block); + + builder.startBlock(block); + for (const instr of block.instructions) { + const uses = collectUses(instr); + for (const { place, updateFn } of uses) { + updateFn(builder.getPlace(place)); + } + + if (instr.lvalue != null) { + const oldPlace = instr.lvalue.place; + const newPlace: Place = builder.definePlace(oldPlace); + instr.lvalue.place = newPlace; + } + } + + const { uses, outputs } = collectOutputs(block); + for (const { place, updateFn } of uses) { + updateFn(builder.getPlace(place)); + } + + const outputBlocks = outputs.map((id) => func.body.blocks.get(id)!); + for (const output of outputBlocks) { + let count; + if (unsealedPreds.has(output)) { + count = unsealedPreds.get(output)! - 1; + } else { + count = output.preds.size - 1; + } + unsealedPreds.set(output, count); + + if (count == 0 && builder.visitedBlocks.has(output)) { + builder.fixIncompletePhis(output); + } + } + + for (const output of outputs) { + visit(output); + } + } + + visit(func.body.entry); +} + +function collectOutputs(block: BasicBlock): { + uses: Array; + outputs: Array; +} { + const outputs: Array = []; + const uses: Array = []; + const { terminal } = block; + switch (terminal.kind) { + case "return": + case "throw": { + if (terminal.value) { + uses.push({ + place: terminal.value, + updateFn: (newPlace) => { + terminal.value = newPlace; + }, + }); + } + break; + } + case "goto": { + outputs.push(terminal.block); + break; + } + case "if": { + const { test, consequent, alternate } = terminal; + uses.push({ + place: test, + updateFn: (newPlace) => { + terminal.test = newPlace; + }, + }); + outputs.push(alternate); + outputs.push(consequent); + break; + } + case "switch": { + const { test, cases } = terminal; + uses.push({ + place: test, + updateFn: (newPlace) => { + terminal.test = newPlace; + }, + }); + for (const case_ of [...cases].reverse()) { + if (case_.test) { + uses.push({ + place: case_.test, + updateFn: (newPlace) => { + case_.test = newPlace; + }, + }); + } + outputs.push(case_.block); + } + break; + } + default: { + assertExhaustive( + terminal, + `Unexpected terminal kind '${(terminal as any).kind}'` + ); + } + } + + return { uses, outputs }; +} + +type Uses = { + place: Place; + updateFn: (place: Place) => void; +}; + +function collectUses(instr: Instruction): Array { + const uses: Array = []; + const instrValue = instr.value; + + // TODO(gsn): Handle more kinds of Instructions + switch (instrValue.kind) { + case "BinaryExpression": { + uses.push({ + place: instrValue.left, + updateFn: (newPlace) => { + instrValue.left = newPlace; + }, + }); + uses.push({ + place: instrValue.right, + updateFn: (newPlace) => { + instrValue.right = newPlace; + }, + }); + break; + } + case "Identifier": { + uses.push({ + place: instrValue, + updateFn: (newPlace) => { + instr.value = newPlace; + }, + }); + break; + } + } + return uses; +} diff --git a/compiler/forget/src/__tests__/fixtures/hir/component.expect.md b/compiler/forget/src/__tests__/fixtures/hir/component.expect.md index 119108f6e9..84c9732330 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/component.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/component.expect.md @@ -43,20 +43,26 @@ bb0: Const mutate max$7 = Call mutate Math$8.max(read $9, read maxItems$3) Goto bb1 bb1: + predecessor blocks: bb0 bb4 bb7 If (read items$2) then:bb3 else:bb2 bb3: + predecessor blocks: bb1 Const mutate $11 = null Const mutate $12 = Binary read item$10 == read $11 If (read $12) then:bb8 else:bb9 bb8: + predecessor blocks: bb3 Const mutate $13 = read $12 Goto bb7 bb9: + predecessor blocks: bb3 Const mutate $13 = Call mutate seen$5.has(mutate item$10) Goto bb7 bb7: + predecessor blocks: bb9 bb8 If (read $13) then:bb1 else:bb4 bb4: + predecessor blocks: bb7 Call mutate seen$5.add(mutate item$10) Const mutate $14 = "div" Const mutate $15 = JSX {read item$10} @@ -64,6 +70,7 @@ bb4: Const mutate $16 = Binary read renderedItems$4.length >= read max$7 If (read $16) then:bb2 else:bb1 bb2: + predecessor blocks: bb1 bb4 Const mutate count$17 = read renderedItems$4.length Const mutate $18 = "div" Const mutate $19 = "\n " diff --git a/compiler/forget/src/__tests__/fixtures/hir/conditional-break.expect.md b/compiler/forget/src/__tests__/fixtures/hir/conditional-break.expect.md index 2946895183..8f86585122 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/conditional-break.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/conditional-break.expect.md @@ -82,9 +82,11 @@ bb0: Call mutate a_DEBUG$2.push(read props$1.a) If (read props$1.b) then:bb2 else:bb1 bb2: + predecessor blocks: bb0 Const mutate $3 = null Return read $3 bb1: + predecessor blocks: bb0 Call mutate a_DEBUG$2.push(read props$1.d) Return freeze a_DEBUG$2 ``` @@ -111,9 +113,11 @@ bb0: Call mutate a$2.push(read props$1.a) If (read props$1.b) then:bb2 else:bb1 bb2: + predecessor blocks: bb0 Call mutate a$2.push(read props$1.c) Goto bb1 bb1: + predecessor blocks: bb0 bb2 Call mutate a$2.push(read props$1.d) Return freeze a$2 ``` @@ -141,10 +145,12 @@ bb0: Call mutate a$2.push(read props$1.a) If (read props$1.b) then:bb2 else:bb1 bb2: + predecessor blocks: bb0 Call mutate a$2.push(read props$1.c) Const mutate $3 = null Return read $3 bb1: + predecessor blocks: bb0 Call mutate a$2.push(read props$1.d) Return freeze a$2 ``` @@ -172,9 +178,11 @@ bb0: Call mutate a$2.push(read props$1.a) If (read props$1.b) then:bb2 else:bb1 bb2: + predecessor blocks: bb0 Call mutate a$2.push(read props$1.c) Return freeze a$2 bb1: + predecessor blocks: bb0 Call mutate a$2.push(read props$1.d) Return freeze a$2 ``` @@ -202,9 +210,11 @@ bb0: Call mutate a$2.push(read props$1.a) If (read props$1.b) then:bb1 else:bb2 bb2: + predecessor blocks: bb0 Call mutate a$2.push(read props$1.c) Goto bb1 bb1: + predecessor blocks: bb2 bb0 Call mutate a$2.push(read props$1.d) Return freeze a$2 ``` diff --git a/compiler/forget/src/__tests__/fixtures/hir/conditional-on-mutable.expect.md b/compiler/forget/src/__tests__/fixtures/hir/conditional-on-mutable.expect.md index 3d62513ace..d122e3f65c 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/conditional-on-mutable.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/conditional-on-mutable.expect.md @@ -71,14 +71,18 @@ bb0: Const mutate b$3 = Array [] If (read b$3) then:bb2 else:bb1 bb2: + predecessor blocks: bb0 Call mutate a$2.push(read props$1.p0) Goto bb1 bb1: + predecessor blocks: bb0 bb2 If (read props$1.p1) then:bb4 else:bb3 bb4: + predecessor blocks: bb1 Call mutate b$3.push(read props$1.p2) Goto bb3 bb3: + predecessor blocks: bb1 bb4 Const mutate $5 = JSX Return read $5 ``` @@ -110,14 +114,18 @@ bb0: Const mutate $5 = Call mutate mayMutate$4(mutate b$3) If (read $5) then:bb2 else:bb1 bb2: + predecessor blocks: bb0 Call mutate a$2.push(read props$1.p0) Goto bb1 bb1: + predecessor blocks: bb0 bb2 If (read props$1.p1) then:bb4 else:bb3 bb4: + predecessor blocks: bb1 Call mutate b$3.push(read props$1.p2) Goto bb3 bb3: + predecessor blocks: bb1 bb4 Const mutate $7 = JSX Return read $7 ``` diff --git a/compiler/forget/src/__tests__/fixtures/hir/hooks-freeze-possibly-mutable-arguments.expect.md b/compiler/forget/src/__tests__/fixtures/hir/hooks-freeze-possibly-mutable-arguments.expect.md index 9de824cfb7..e1a5600852 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/hooks-freeze-possibly-mutable-arguments.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/hooks-freeze-possibly-mutable-arguments.expect.md @@ -31,12 +31,15 @@ bb0: Let mutate a$4 = undefined If (read cond$2) then:bb2 else:bb3 bb2: + predecessor blocks: bb0 Reassign mutate a$4 = read x$3 Goto bb1 bb3: + predecessor blocks: bb0 Reassign mutate a$4 = Array [] Goto bb1 bb1: + predecessor blocks: bb3 bb2 Call read useFreeze$5(freeze a$4) Call read useFreeze$5(read a$4) Call mutate call$6(read a$4) diff --git a/compiler/forget/src/__tests__/fixtures/hir/independent-across-if.expect.md b/compiler/forget/src/__tests__/fixtures/hir/independent-across-if.expect.md index d569e3c3fc..1bbeb417d2 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/independent-across-if.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/independent-across-if.expect.md @@ -101,10 +101,12 @@ bb0: Const mutate b$4 = Call mutate compute$3(read props$1.b) If (read props$1.c) then:bb2 else:bb1 bb2: + predecessor blocks: bb0 Call mutate mutate$5(mutate a$2) Call mutate mutate$5(mutate b$4) Goto bb1 bb1: + predecessor blocks: bb0 bb2 Const mutate $7 = JSX Return read $7 ``` diff --git a/compiler/forget/src/__tests__/fixtures/hir/interdependent-across-if.expect.md b/compiler/forget/src/__tests__/fixtures/hir/interdependent-across-if.expect.md index 1ad68607fe..1bc85734df 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/interdependent-across-if.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/interdependent-across-if.expect.md @@ -80,9 +80,11 @@ bb0: Const mutate b$4 = Call mutate compute$3(read props$1.b) If (read props$1.c) then:bb2 else:bb1 bb2: + predecessor blocks: bb0 Call mutate foo$5(mutate a$2, mutate b$4) Goto bb1 bb1: + predecessor blocks: bb0 bb2 Const mutate $7 = JSX Return read $7 ``` diff --git a/compiler/forget/src/__tests__/fixtures/hir/mutable-lifetime-loops.expect.md b/compiler/forget/src/__tests__/fixtures/hir/mutable-lifetime-loops.expect.md index 201788698d..0c1d50d9ef 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/mutable-lifetime-loops.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/mutable-lifetime-loops.expect.md @@ -79,9 +79,11 @@ bb0: Let mutate d$5 = Object { } Goto bb1 bb1: + predecessor blocks: bb0 bb3 Const mutate $10 = true If (read $10) then:bb3 else:bb2 bb3: + predecessor blocks: bb1 Let mutate z$6 = read a$2 Reassign mutate a$2 = read b$3 Reassign mutate b$3 = read c$4 @@ -91,14 +93,19 @@ bb3: Const mutate $9 = Call mutate cond$8(mutate a$2) If (read $9) then:bb2 else:bb1 bb2: + predecessor blocks: bb1 bb3 If (read a$2) then:bb7 else:bb7 bb7: + predecessor blocks: bb2 If (read b$3) then:bb9 else:bb9 bb9: + predecessor blocks: bb7 If (read c$4) then:bb11 else:bb11 bb11: + predecessor blocks: bb9 If (read d$5) then:bb13 else:bb13 bb13: + predecessor blocks: bb11 Const mutate $11 = null Call mutate mutate$7(mutate d$5, read $11) Return diff --git a/compiler/forget/src/__tests__/fixtures/hir/mutable-lifetime-with-aliasing.expect.md b/compiler/forget/src/__tests__/fixtures/hir/mutable-lifetime-with-aliasing.expect.md index 628455fcf2..a832b1349e 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/mutable-lifetime-with-aliasing.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/mutable-lifetime-with-aliasing.expect.md @@ -63,14 +63,19 @@ bb0: Const mutate y$7 = Call mutate mutate$8(mutate x$6, mutate d$5) If (read a$2) then:bb1 else:bb1 bb1: + predecessor blocks: bb0 If (read b$3) then:bb3 else:bb3 bb3: + predecessor blocks: bb1 If (read c$4) then:bb5 else:bb5 bb5: + predecessor blocks: bb3 If (read d$5) then:bb7 else:bb7 bb7: + predecessor blocks: bb5 If (read y$7) then:bb9 else:bb9 bb9: + predecessor blocks: bb7 Const mutate $9 = null Call mutate mutate$8(mutate x$6, read $9) Return diff --git a/compiler/forget/src/__tests__/fixtures/hir/reassignment-conditional.expect.md b/compiler/forget/src/__tests__/fixtures/hir/reassignment-conditional.expect.md index 559a1f8f51..546b7dc730 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/reassignment-conditional.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/reassignment-conditional.expect.md @@ -28,9 +28,11 @@ bb0: Let mutate y$3 = read x$2 If (read props$1.p1) then:bb2 else:bb1 bb2: + predecessor blocks: bb0 Reassign mutate x$2 = Array [] Goto bb1 bb1: + predecessor blocks: bb0 bb2 Let mutate _$4 = JSX Call read y$3.push(read props$1.p2) Const mutate $5 = JSX diff --git a/compiler/forget/src/__tests__/fixtures/hir/reverse-postorder.expect.md b/compiler/forget/src/__tests__/fixtures/hir/reverse-postorder.expect.md index c53ffa238e..3036ab211a 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/reverse-postorder.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/reverse-postorder.expect.md @@ -39,6 +39,7 @@ bb0: Let mutate x$2 = undefined If (read props$1.cond) then:bb2 else:bb10 bb2: + predecessor blocks: bb0 Const mutate $3 = 2 Const mutate $4 = 1 Const mutate $5 = 0 @@ -48,23 +49,30 @@ bb2: Case read $3: bb4 Default: bb4 bb8: + predecessor blocks: bb2 Reassign mutate x$2 = read props$1.v0 Goto bb1 bb6: + predecessor blocks: bb2 Reassign mutate x$2 = read props$1.v1 Goto bb1 bb4: + predecessor blocks: bb2 Reassign mutate x$2 = read props$1.v2 Goto bb1 bb10: + predecessor blocks: bb0 If (read props$1.cond2) then:bb12 else:bb13 bb12: + predecessor blocks: bb10 Reassign mutate x$2 = read props$1.b Goto bb1 bb13: + predecessor blocks: bb10 Reassign mutate x$2 = read props$1.c Goto bb1 bb1: + predecessor blocks: bb13 bb12 bb8 bb6 bb4 read x$2 Return ``` diff --git a/compiler/forget/src/__tests__/fixtures/hir/simple.expect.md b/compiler/forget/src/__tests__/fixtures/hir/simple.expect.md index 6cc44460d9..c7f670aafc 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/simple.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/simple.expect.md @@ -17,10 +17,12 @@ function foo(x, y) { bb0: If (read x$1) then:bb2 else:bb1 bb2: + predecessor blocks: bb0 Const mutate $3 = false Const mutate $4 = Call read foo$0(read $3, read y$2) Return freeze $4 bb1: + predecessor blocks: bb0 Const mutate $5 = 10 Const mutate $6 = Binary read y$2 * read $5 Const mutate $7 = Array [read $6] diff --git a/compiler/forget/src/__tests__/fixtures/hir/ssa-complex-multiple-if.expect.md b/compiler/forget/src/__tests__/fixtures/hir/ssa-complex-multiple-if.expect.md new file mode 100644 index 0000000000..85c2fa2feb --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/ssa-complex-multiple-if.expect.md @@ -0,0 +1,70 @@ + +## Input + +```javascript +function foo() { + let x = 1; + let y = 2; + if (y === 2) { + x = 3; + } + + if (y === 3) { + x = 5; + } + y = x; +} + +``` + +## HIR + +``` +bb0: + Let mutate x$1000 = 1 + Let mutate y$1001 = 2 + Const mutate $1002 = 2 + Const mutate $1003 = Binary mutate y$1001 === mutate $1002 + If (mutate $1003) then:bb2 else:bb1 +bb2: + predecessor blocks: bb0 + Reassign mutate x$1011 = 3 + Goto bb1 +bb1: + predecessor blocks: bb0 bb2 + Const mutate y$1005: phi(bb0: mutate y$1001, bb2: mutate y$1001) + Const mutate x$1010: phi(bb0: mutate x$1000, bb2: mutate x$1011) + Const mutate $1004 = 3 + Const mutate $1006 = Binary mutate y$1005 === mutate $1004 + If (mutate $1006) then:bb4 else:bb3 +bb4: + predecessor blocks: bb1 + Reassign mutate x$1009 = 5 + Goto bb3 +bb3: + predecessor blocks: bb1 bb4 + Const mutate x$1007: phi(bb1: mutate x$1010, bb4: mutate x$1009) + Reassign mutate y$1008 = mutate x$1007 + Return +``` + +## Code + +```javascript +function foo$0() { + let x$1000 = 1; + let y$1001 = 2; + if (y$1001 === 2) { + x$1011 = 3; + ("<>"); + } + if (y$1005 === 3) { + x$1009 = 5; + ("<>"); + } + y$1008 = x$1007; + return; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/hir/ssa-complex-multiple-if.js b/compiler/forget/src/__tests__/fixtures/hir/ssa-complex-multiple-if.js new file mode 100644 index 0000000000..dd6b6753a3 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/ssa-complex-multiple-if.js @@ -0,0 +1,12 @@ +function foo() { + let x = 1; + let y = 2; + if (y === 2) { + x = 3; + } + + if (y === 3) { + x = 5; + } + y = x; +} diff --git a/compiler/forget/src/__tests__/fixtures/hir/ssa-complex-single-if.expect.md b/compiler/forget/src/__tests__/fixtures/hir/ssa-complex-single-if.expect.md new file mode 100644 index 0000000000..23199e29ef --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/ssa-complex-single-if.expect.md @@ -0,0 +1,52 @@ + +## Input + +```javascript +function foo() { + let x = 1; + let y = 2; + if (y === 2) { + x = 3; + } + + y = x; +} + +``` + +## HIR + +``` +bb0: + Let mutate x$1000 = 1 + Let mutate y$1001 = 2 + Const mutate $1002 = 2 + Const mutate $1003 = Binary mutate y$1001 === mutate $1002 + If (mutate $1003) then:bb2 else:bb1 +bb2: + predecessor blocks: bb0 + Reassign mutate x$1006 = 3 + Goto bb1 +bb1: + predecessor blocks: bb0 bb2 + Const mutate x$1004: phi(bb0: mutate x$1000, bb2: mutate x$1006) + Reassign mutate y$1005 = mutate x$1004 + Return +``` + +## Code + +```javascript +function foo$0() { + let x$1000 = 1; + let y$1001 = 2; + if (y$1001 === 2) { + x$1006 = 3; + ("<>"); + } + y$1005 = x$1004; + return; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/hir/ssa-complex-single-if.js b/compiler/forget/src/__tests__/fixtures/hir/ssa-complex-single-if.js new file mode 100644 index 0000000000..139a937d14 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/ssa-complex-single-if.js @@ -0,0 +1,9 @@ +function foo() { + let x = 1; + let y = 2; + if (y === 2) { + x = 3; + } + + y = x; +} diff --git a/compiler/forget/src/__tests__/fixtures/hir/ssa-for.expect.md b/compiler/forget/src/__tests__/fixtures/hir/ssa-for.expect.md new file mode 100644 index 0000000000..aa2598cd18 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/ssa-for.expect.md @@ -0,0 +1,46 @@ + +## Input + +```javascript +function foo() { + let x = 0; + for (; x < 10; ) { + x = x + 1; + } + return x; +} + +``` + +## HIR + +``` +bb0: + Let mutate x$1000 = 0 + Goto bb1 +bb1: + predecessor blocks: bb0 bb4 + Const mutate x$1002: phi(bb0: mutate x$1000, bb4: mutate x$1005) + Const mutate $1001 = 10 + Const mutate $1003 = Binary mutate x$1002 < mutate $1001 + If (mutate $1003) then:bb4 else:bb2 +bb4: + predecessor blocks: bb1 + Const mutate $1004 = 1 + Reassign mutate x$1005 = Binary mutate x$1002 + mutate $1004 + Goto bb1 +bb2: + predecessor blocks: bb1 + Return mutate x$1002 +``` + +## Code + +```javascript +function foo$0() { + let x$1000 = 0; + ("<>"); +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/hir/ssa-for.js b/compiler/forget/src/__tests__/fixtures/hir/ssa-for.js new file mode 100644 index 0000000000..0336b601a8 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/ssa-for.js @@ -0,0 +1,7 @@ +function foo() { + let x = 0; + for (; x < 10; ) { + x = x + 1; + } + return x; +} diff --git a/compiler/forget/src/__tests__/fixtures/hir/ssa-if-else.expect.md b/compiler/forget/src/__tests__/fixtures/hir/ssa-if-else.expect.md new file mode 100644 index 0000000000..d68255620b --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/ssa-if-else.expect.md @@ -0,0 +1,55 @@ + +## Input + +```javascript +function foo() { + let x = 1; + let y = 2; + + if (y) { + let z = x + y; + } else { + let z = x; + } +} + +``` + +## HIR + +``` +bb0: + Let mutate x$1000 = 1 + Let mutate y$1001 = 2 + If (mutate y$1001) then:bb2 else:bb3 +bb2: + predecessor blocks: bb0 + Let mutate z$1003 = Binary mutate x$1000 + mutate y$1001 + Goto bb1 +bb3: + predecessor blocks: bb0 + Let mutate z$1002 = mutate x$1000 + Goto bb1 +bb1: + predecessor blocks: bb3 bb2 + Return +``` + +## Code + +```javascript +function foo$0() { + let x$1000 = 1; + let y$1001 = 2; + if (y$1001) { + let z$1003 = x$1000 + y$1001; + ("<>"); + } else { + let z$1002 = x$1000; + ("<>"); + } + return; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/hir/ssa-if-else.js b/compiler/forget/src/__tests__/fixtures/hir/ssa-if-else.js new file mode 100644 index 0000000000..d5b7bb02ed --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/ssa-if-else.js @@ -0,0 +1,10 @@ +function foo() { + let x = 1; + let y = 2; + + if (y) { + let z = x + y; + } else { + let z = x; + } +} diff --git a/compiler/forget/src/__tests__/fixtures/hir/ssa-return.expect.md b/compiler/forget/src/__tests__/fixtures/hir/ssa-return.expect.md new file mode 100644 index 0000000000..45cfc3a2e4 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/ssa-return.expect.md @@ -0,0 +1,47 @@ + +## Input + +```javascript +function foo() { + let x = 1; + if (x === 1) { + x = 2; + } + + return x; +} + +``` + +## HIR + +``` +bb0: + Let mutate x$1000 = 1 + Const mutate $1001 = 1 + Const mutate $1002 = Binary mutate x$1000 === mutate $1001 + If (mutate $1002) then:bb2 else:bb1 +bb2: + predecessor blocks: bb0 + Reassign mutate x$1004 = 2 + Goto bb1 +bb1: + predecessor blocks: bb0 bb2 + Const mutate x$1003: phi(bb0: mutate x$1000, bb2: mutate x$1004) + Return mutate x$1003 +``` + +## Code + +```javascript +function foo$0() { + let x$1000 = 1; + if (x$1000 === 1) { + x$1004 = 2; + ("<>"); + } + return x$1003; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/hir/ssa-return.js b/compiler/forget/src/__tests__/fixtures/hir/ssa-return.js new file mode 100644 index 0000000000..289f0315ac --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/ssa-return.js @@ -0,0 +1,8 @@ +function foo() { + let x = 1; + if (x === 1) { + x = 2; + } + + return x; +} diff --git a/compiler/forget/src/__tests__/fixtures/hir/ssa-simple-phi.expect.md b/compiler/forget/src/__tests__/fixtures/hir/ssa-simple-phi.expect.md new file mode 100644 index 0000000000..da3a8f9d63 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/ssa-simple-phi.expect.md @@ -0,0 +1,59 @@ + +## Input + +```javascript +function foo() { + let y = 2; + + if (y > 1) { + y = 1; + } else { + y = 2; + } + + let x = y; +} + +``` + +## HIR + +``` +bb0: + Let mutate y$1000 = 2 + Const mutate $1001 = 1 + Const mutate $1002 = Binary mutate y$1000 > mutate $1001 + If (mutate $1002) then:bb2 else:bb3 +bb2: + predecessor blocks: bb0 + Reassign mutate y$1006 = 1 + Goto bb1 +bb3: + predecessor blocks: bb0 + Reassign mutate y$1003 = 2 + Goto bb1 +bb1: + predecessor blocks: bb3 bb2 + Const mutate y$1004: phi(bb3: mutate y$1003, bb2: mutate y$1006) + Let mutate x$1005 = mutate y$1004 + Return +``` + +## Code + +```javascript +function foo$0() { + let y$1000 = 2; + if (y$1000 > 1) { + y$1006 = 1; + ("<>"); + } else { + y$1003 = 2; + ("<>"); + } + let x$1005 = y$1004; + return; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/hir/ssa-simple-phi.js b/compiler/forget/src/__tests__/fixtures/hir/ssa-simple-phi.js new file mode 100644 index 0000000000..20a8956907 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/ssa-simple-phi.js @@ -0,0 +1,11 @@ +function foo() { + let y = 2; + + if (y > 1) { + y = 1; + } else { + y = 2; + } + + let x = y; +} diff --git a/compiler/forget/src/__tests__/fixtures/hir/ssa-simple.expect.md b/compiler/forget/src/__tests__/fixtures/hir/ssa-simple.expect.md new file mode 100644 index 0000000000..104b2a4f7c --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/ssa-simple.expect.md @@ -0,0 +1,31 @@ + +## Input + +```javascript +function foo() { + let x = 1; + let y = 2; +} + +``` + +## HIR + +``` +bb0: + Let mutate x$1000 = 1 + Let mutate y$1001 = 2 + Return +``` + +## Code + +```javascript +function foo$0() { + let x$1000 = 1; + let y$1001 = 2; + return; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/hir/ssa-simple.js b/compiler/forget/src/__tests__/fixtures/hir/ssa-simple.js new file mode 100644 index 0000000000..93006f8a8c --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/ssa-simple.js @@ -0,0 +1,4 @@ +function foo() { + let x = 1; + let y = 2; +} diff --git a/compiler/forget/src/__tests__/fixtures/hir/ssa-single-if.expect.md b/compiler/forget/src/__tests__/fixtures/hir/ssa-single-if.expect.md new file mode 100644 index 0000000000..501c07b2d0 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/ssa-single-if.expect.md @@ -0,0 +1,46 @@ + +## Input + +```javascript +function foo() { + let x = 1; + let y = 2; + + if (y) { + let z = x + y; + } +} + +``` + +## HIR + +``` +bb0: + Let mutate x$1000 = 1 + Let mutate y$1001 = 2 + If (mutate y$1001) then:bb2 else:bb1 +bb2: + predecessor blocks: bb0 + Let mutate z$1002 = Binary mutate x$1000 + mutate y$1001 + Goto bb1 +bb1: + predecessor blocks: bb0 bb2 + Return +``` + +## Code + +```javascript +function foo$0() { + let x$1000 = 1; + let y$1001 = 2; + if (y$1001) { + let z$1002 = x$1000 + y$1001; + ("<>"); + } + return; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/hir/ssa-single-if.js b/compiler/forget/src/__tests__/fixtures/hir/ssa-single-if.js new file mode 100644 index 0000000000..338e04b08c --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/ssa-single-if.js @@ -0,0 +1,8 @@ +function foo() { + let x = 1; + let y = 2; + + if (y) { + let z = x + y; + } +} diff --git a/compiler/forget/src/__tests__/fixtures/hir/ssa-switch.expect.md b/compiler/forget/src/__tests__/fixtures/hir/ssa-switch.expect.md new file mode 100644 index 0000000000..5845ad9436 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/ssa-switch.expect.md @@ -0,0 +1,86 @@ + +## Input + +```javascript +function foo() { + let x = 1; + + switch (x) { + case x === 1: { + x = x + 1; + break; + } + case x === 2: { + x = x + 2; + break; + } + default: { + x = x + 3; + } + } + + let y = x; +} + +``` + +## HIR + +``` +bb0: + Let mutate x$1000 = 1 + Const mutate $1001 = 2 + Const mutate $1002 = Binary mutate x$1000 === mutate $1001 + Const mutate $1003 = 1 + Const mutate $1004 = Binary mutate x$1000 === mutate $1003 + Switch (mutate x$1000) + Case mutate $1004: bb5 + Case mutate $1002: bb3 + Default: bb2 +bb5: + predecessor blocks: bb0 + Const mutate $1011 = 1 + Reassign mutate x$1012 = Binary mutate x$1000 + mutate $1011 + Goto bb1 +bb3: + predecessor blocks: bb0 + Const mutate $1009 = 2 + Reassign mutate x$1010 = Binary mutate x$1000 + mutate $1009 + Goto bb1 +bb2: + predecessor blocks: bb0 + Const mutate $1005 = 3 + Reassign mutate x$1006 = Binary mutate x$1000 + mutate $1005 + Goto bb1 +bb1: + predecessor blocks: bb5 bb3 bb2 + Const mutate x$1007: phi(bb5: mutate x$1012, bb3: mutate x$1010, bb2: mutate x$1006) + Let mutate y$1008 = mutate x$1007 + Return +``` + +## Code + +```javascript +function foo$0() { + let x$1000 = 1; + switch (x$1000) { + case x$1000 === 1: { + x$1012 = x$1000 + 1; + ("<>"); + } + case x$1000 === 2: { + x$1010 = x$1000 + 2; + ("<>"); + } + default: { + x$1006 = x$1000 + 3; + ("<>"); + } + } + let y$1008 = x$1007; + return; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/hir/ssa-switch.js b/compiler/forget/src/__tests__/fixtures/hir/ssa-switch.js new file mode 100644 index 0000000000..69876afeed --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/ssa-switch.js @@ -0,0 +1,19 @@ +function foo() { + let x = 1; + + switch (x) { + case x === 1: { + x = x + 1; + break; + } + case x === 2: { + x = x + 2; + break; + } + default: { + x = x + 3; + } + } + + let y = x; +} diff --git a/compiler/forget/src/__tests__/fixtures/hir/ssa-throw.expect.md b/compiler/forget/src/__tests__/fixtures/hir/ssa-throw.expect.md new file mode 100644 index 0000000000..39f079b678 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/ssa-throw.expect.md @@ -0,0 +1,46 @@ + +## Input + +```javascript +function foo() { + let x = 1; + if (x === 1) { + x = 2; + } + throw x; +} + +``` + +## HIR + +``` +bb0: + Let mutate x$1000 = 1 + Const mutate $1001 = 1 + Const mutate $1002 = Binary mutate x$1000 === mutate $1001 + If (mutate $1002) then:bb2 else:bb1 +bb2: + predecessor blocks: bb0 + Reassign mutate x$1004 = 2 + Goto bb1 +bb1: + predecessor blocks: bb0 bb2 + Const mutate x$1003: phi(bb0: mutate x$1000, bb2: mutate x$1004) + Throw mutate x$1003 +``` + +## Code + +```javascript +function foo$0() { + let x$1000 = 1; + if (x$1000 === 1) { + x$1004 = 2; + ("<>"); + } + throw x$1003; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/hir/ssa-throw.js b/compiler/forget/src/__tests__/fixtures/hir/ssa-throw.js new file mode 100644 index 0000000000..c2a382f990 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/ssa-throw.js @@ -0,0 +1,7 @@ +function foo() { + let x = 1; + if (x === 1) { + x = 2; + } + throw x; +} diff --git a/compiler/forget/src/__tests__/fixtures/hir/ssa-while.expect.md b/compiler/forget/src/__tests__/fixtures/hir/ssa-while.expect.md new file mode 100644 index 0000000000..fe737e26b6 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/ssa-while.expect.md @@ -0,0 +1,47 @@ + +## Input + +```javascript +function foo() { + let x = 1; + while (x < 10) { + x = x + 1; + } + + return x; +} + +``` + +## HIR + +``` +bb0: + Let mutate x$1000 = 1 + Goto bb1 +bb1: + predecessor blocks: bb0 bb3 + Const mutate x$1002: phi(bb0: mutate x$1000, bb3: mutate x$1005) + Const mutate $1001 = 10 + Const mutate $1003 = Binary mutate x$1002 < mutate $1001 + If (mutate $1003) then:bb3 else:bb2 +bb3: + predecessor blocks: bb1 + Const mutate $1004 = 1 + Reassign mutate x$1005 = Binary mutate x$1002 + mutate $1004 + Goto bb1 +bb2: + predecessor blocks: bb1 + Return mutate x$1002 +``` + +## Code + +```javascript +function foo$0() { + let x$1000 = 1; + ("<>"); +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/hir/ssa-while.js b/compiler/forget/src/__tests__/fixtures/hir/ssa-while.js new file mode 100644 index 0000000000..47397ae648 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/ssa-while.js @@ -0,0 +1,8 @@ +function foo() { + let x = 1; + while (x < 10) { + x = x + 1; + } + + return x; +} diff --git a/compiler/forget/src/__tests__/fixtures/hir/switch-non-final-default.expect.md b/compiler/forget/src/__tests__/fixtures/hir/switch-non-final-default.expect.md index 09fc5f4a7d..130b4a8db5 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/switch-non-final-default.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/switch-non-final-default.expect.md @@ -43,13 +43,16 @@ bb0: Default: bb1 Case read $4: bb2 bb6: + predecessor blocks: bb0 Call mutate x$2.push(read props$1.p2) Reassign mutate y$3 = Array [] Goto bb1 bb2: + predecessor blocks: bb0 Reassign mutate y$3 = read x$2 Goto bb1 bb1: + predecessor blocks: bb0 bb6 bb2 Const mutate child$7 = JSX Call read y$3.push(read props$1.p4) Const mutate $8 = JSX {read child$7} diff --git a/compiler/forget/src/__tests__/fixtures/hir/switch.expect.md b/compiler/forget/src/__tests__/fixtures/hir/switch.expect.md index b23a1978ab..716cbb0dce 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/switch.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/switch.expect.md @@ -37,14 +37,17 @@ bb0: Case read $4: bb2 Default: bb1 bb4: + predecessor blocks: bb0 Call mutate x$2.push(read props$1.p2) Call mutate x$2.push(read props$1.p3) Reassign mutate y$3 = Array [] Goto bb2 bb2: + predecessor blocks: bb4 bb0 Reassign mutate y$3 = read x$2 Goto bb1 bb1: + predecessor blocks: bb2 bb0 Const mutate child$6 = JSX Call read y$3.push(read props$1.p4) Const mutate $7 = JSX {read child$6} diff --git a/compiler/forget/src/__tests__/hir-test.ts b/compiler/forget/src/__tests__/hir-test.ts index 5fd1f1794a..7d7229bca7 100644 --- a/compiler/forget/src/__tests__/hir-test.ts +++ b/compiler/forget/src/__tests__/hir-test.ts @@ -19,6 +19,7 @@ import codegen from "../HIR/Codegen"; import { HIRFunction } from "../HIR/HIR"; import inferReferenceEffects from "../HIR/InferReferenceEffects"; import printHIR from "../HIR/PrintHIR"; +import buildSSA from "../HIR/SSAify"; import generateTestsFromFixtures from "./test-utils/generateTestsFromFixtures"; function wrapWithTripleBackticks(s: string, ext?: string) { @@ -45,6 +46,9 @@ describe("React Forget (HIR version)", () => { enter(nodePath) { const ir: HIRFunction = lower(nodePath); inferReferenceEffects(ir); + if (file.startsWith("ssa")) { + buildSSA(ir); + } // const lifetimeGraph = buildDefUseGraph(ir); const textHIR = printHIR(ir.body); // const textLifetimeGraph = printGraph(lifetimeGraph);