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.
This commit is contained in:
Sathya Gunasekaran
2022-10-18 18:45:01 +01:00
parent 9524c58486
commit 379251c65f
40 changed files with 1193 additions and 3 deletions
+8
View File
@@ -94,6 +94,8 @@ export type BasicBlock = {
id: BlockId;
instructions: Array<Instruction>;
terminal: Terminal;
preds: Set<BasicBlock>;
phis: Set<Phi>;
};
/**
@@ -162,6 +164,12 @@ export enum InstructionKind {
*/
export type InstructionValue = (InstructionData & { path: NodePath }) | Place;
export type Phi = {
kind: "Phi";
lvalue: LValue;
operands: Map<BasicBlock, Place>;
};
export type InstructionData =
| { kind: "Primitive"; value: number | boolean | string | null | undefined }
| { kind: "JSXText"; value: string }
+73 -3
View File
@@ -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<BlockId> = 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
+25
View File
@@ -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> | string {
let value;
switch (terminal.kind) {
+333
View File
@@ -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<IdentifierId, Place>;
incompletePhis: IncompletePhi[];
};
const unsealedPreds: Map<BasicBlock, number> = new Map();
class SSABuilder {
#states: Map<BasicBlock, State> = new Map();
#current: BasicBlock | null = null;
visitedBlocks: Set<BasicBlock> = 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<BasicBlock, Place> = 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<Uses>;
outputs: Array<BlockId>;
} {
const outputs: Array<BlockId> = [];
const uses: Array<Uses> = [];
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<Uses> {
const uses: Array<Uses> = [];
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;
}
@@ -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 $14>{read item$10}</read $14>
@@ -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 "
@@ -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
```
@@ -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 <read Foo$4 a={freeze a$2} b={freeze b$3} ></read Foo$4>
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 <read Foo$6 a={freeze a$2} b={freeze b$3} ></read Foo$6>
Return read $7
```
@@ -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)
@@ -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 <read Foo$6 a={freeze a$2} b={freeze b$4} ></read Foo$6>
Return read $7
```
@@ -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 <read Foo$6 a={freeze a$2} b={freeze b$4} ></read Foo$6>
Return read $7
```
@@ -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
@@ -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
@@ -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 <read Component$0 x={freeze x$2} ></read Component$0>
Call read y$3.push(read props$1.p2)
Const mutate $5 = JSX <read Component$0 x={read x$2} y={read y$3} ></read Component$0>
@@ -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
```
@@ -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]
@@ -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;
("<<TODO: handle complex control flow in codegen>>");
}
if (y$1005 === 3) {
x$1009 = 5;
("<<TODO: handle complex control flow in codegen>>");
}
y$1008 = x$1007;
return;
}
```
@@ -0,0 +1,12 @@
function foo() {
let x = 1;
let y = 2;
if (y === 2) {
x = 3;
}
if (y === 3) {
x = 5;
}
y = x;
}
@@ -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;
("<<TODO: handle complex control flow in codegen>>");
}
y$1005 = x$1004;
return;
}
```
@@ -0,0 +1,9 @@
function foo() {
let x = 1;
let y = 2;
if (y === 2) {
x = 3;
}
y = x;
}
@@ -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;
("<<TODO: handle complex control flow in codegen>>");
}
```
@@ -0,0 +1,7 @@
function foo() {
let x = 0;
for (; x < 10; ) {
x = x + 1;
}
return x;
}
@@ -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;
("<<TODO: handle complex control flow in codegen>>");
} else {
let z$1002 = x$1000;
("<<TODO: handle complex control flow in codegen>>");
}
return;
}
```
@@ -0,0 +1,10 @@
function foo() {
let x = 1;
let y = 2;
if (y) {
let z = x + y;
} else {
let z = x;
}
}
@@ -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;
("<<TODO: handle complex control flow in codegen>>");
}
return x$1003;
}
```
@@ -0,0 +1,8 @@
function foo() {
let x = 1;
if (x === 1) {
x = 2;
}
return x;
}
@@ -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;
("<<TODO: handle complex control flow in codegen>>");
} else {
y$1003 = 2;
("<<TODO: handle complex control flow in codegen>>");
}
let x$1005 = y$1004;
return;
}
```
@@ -0,0 +1,11 @@
function foo() {
let y = 2;
if (y > 1) {
y = 1;
} else {
y = 2;
}
let x = y;
}
@@ -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;
}
```
@@ -0,0 +1,4 @@
function foo() {
let x = 1;
let y = 2;
}
@@ -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;
("<<TODO: handle complex control flow in codegen>>");
}
return;
}
```
@@ -0,0 +1,8 @@
function foo() {
let x = 1;
let y = 2;
if (y) {
let z = x + y;
}
}
@@ -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;
("<<TODO: handle complex control flow in codegen>>");
}
case x$1000 === 2: {
x$1010 = x$1000 + 2;
("<<TODO: handle complex control flow in codegen>>");
}
default: {
x$1006 = x$1000 + 3;
("<<TODO: handle complex control flow in codegen>>");
}
}
let y$1008 = x$1007;
return;
}
```
@@ -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;
}
@@ -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;
("<<TODO: handle complex control flow in codegen>>");
}
throw x$1003;
}
```
@@ -0,0 +1,7 @@
function foo() {
let x = 1;
if (x === 1) {
x = 2;
}
throw x;
}
@@ -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;
("<<TODO: handle complex control flow in codegen>>");
}
```
@@ -0,0 +1,8 @@
function foo() {
let x = 1;
while (x < 10) {
x = x + 1;
}
return x;
}
@@ -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 <read Component$0 data={freeze x$2} ></read Component$0>
Call read y$3.push(read props$1.p4)
Const mutate $8 = JSX <read Component$0 data={freeze y$3} >{read child$7}</read Component$0>
@@ -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 <read Component$0 data={freeze x$2} ></read Component$0>
Call read y$3.push(read props$1.p4)
Const mutate $7 = JSX <read Component$0 data={read y$3} >{read child$6}</read Component$0>
@@ -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);