[ssa] Use Identifier for updating SSA

Instead of using Place, use Identifier as the unit of comparison in SSA. 

Place is too high level and can not be substituted for other Places (even those 
with the same Identifier) as Place contain higher level metadata such as 
memberPath.
This commit is contained in:
Sathya Gunasekaran
2022-10-27 14:11:19 +01:00
parent 588a098149
commit c8aaa7f172
22 changed files with 209 additions and 150 deletions
+2 -2
View File
@@ -166,8 +166,8 @@ export type InstructionValue = (InstructionData & { path: NodePath }) | Place;
export type Phi = {
kind: "Phi";
lvalue: LValue;
operands: Map<BasicBlock, Place>;
id: Identifier;
operands: Map<BasicBlock, Identifier>;
};
export type InstructionData =
+9 -10
View File
@@ -8,6 +8,7 @@
import { assertExhaustive } from "../Common/utils";
import {
HIR,
Identifier,
Instruction,
InstructionKind,
InstructionValue,
@@ -91,11 +92,11 @@ function printInstruction(instr: Instruction): string {
function printPhi(phi: Phi): string {
const items = [];
items.push(printLValue(phi.lvalue));
items.push(printIdentifier(phi.id));
items.push(": phi(");
const phis = [];
for (const [block, place] of phi.operands) {
phis.push(`bb${block.id}: ${printPlace(place)}`);
for (const [block, id] of phi.operands) {
phis.push(`bb${block.id}: ${printIdentifier(id)}`);
}
items.push(phis.join(", "));
@@ -256,13 +257,7 @@ export function printLValue(lval: LValue): string {
}
export function printPlace(place: Place): string {
const items = [
place.effect,
" ",
place.identifier.name,
"$",
place.identifier.id,
];
const items = [place.effect, " ", printIdentifier(place.identifier)];
if (place.memberPath != null) {
for (const path of place.memberPath) {
items.push(".");
@@ -271,3 +266,7 @@ export function printPlace(place: Place): string {
}
return items.filter((x) => x != null).join("");
}
export function printIdentifier(id: Identifier): string {
return `${id.name ?? ""}\$${id.id}`;
}
+40 -63
View File
@@ -3,24 +3,22 @@ import { assertExhaustive } from "../Common/utils";
import {
BasicBlock,
BlockId,
Effect,
HIRFunction,
IdentifierId,
Identifier,
Instruction,
InstructionKind,
Phi,
Place,
} from "./HIR";
import { Environment } from "./HIRBuilder";
import { printPlace } from "./PrintHIR";
import { printIdentifier } from "./PrintHIR";
type IncompletePhi = {
old: Place;
new: Place;
oldId: Identifier;
newId: Identifier;
};
type State = {
defs: Map<IdentifierId, Place>;
defs: Map<Identifier, Identifier>;
incompletePhis: IncompletePhi[];
};
@@ -48,99 +46,80 @@ class SSABuilder {
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: this.nextIdentifierId,
};
definePlace(oldPlace: Place): Place {
const oldId = oldPlace.identifier;
const newId = { ...oldId, id: this.nextIdentifierId };
this.state().defs.set(oldId, newId);
return {
...oldPlace,
identifier,
identifier: newId,
};
}
makePlaceForPhi(oldPlace: Place): Place {
const identifier = {
...oldPlace.identifier,
id: this.nextIdentifierId,
};
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!);
const newId = this.getIdAt(oldPlace.identifier, this.#current!);
return {
...oldPlace,
identifier: newId,
};
}
getPlaceAt(oldPlace: Place, block: BasicBlock): Place {
getIdAt(oldId: Identifier, block: BasicBlock): Identifier {
// 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 (state.defs.has(oldId)) {
return state.defs.get(oldId)!;
}
if (block.preds.size == 0) {
// We're at the entry block and haven't found our defintion yet.
console.log(
`Unable to find "${printPlace(oldPlace)}", assuming it's a global`
`Unable to find "${printIdentifier(oldId)}", assuming it's a global`
);
//return oldPlace;
return oldId;
}
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;
const newId = { ...oldId, id: this.nextIdentifierId };
state.incompletePhis.push({ oldId, newId });
state.defs.set(oldId, newId);
return newId;
}
// 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;
const newId = this.getIdAt(oldId, pred);
state.defs.set(oldId, newId);
return newId;
}
// There are multiple predecessors, we need a phi.
const newPlace = this.makePlaceForPhi(oldPlace);
const newId = { ...oldId, id: this.nextIdentifierId };
// 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);
state.defs.set(oldId, newId);
this.addPhi(block, oldId, newId);
// 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)!;
return state.defs.get(oldId)!;
}
addPhi(block: BasicBlock, oldPlace: Place, newPlace: Place) {
const predDefs: Map<BasicBlock, Place> = new Map();
addPhi(block: BasicBlock, oldId: Identifier, newId: Identifier) {
const predDefs: Map<BasicBlock, Identifier> = new Map();
for (const predBlock of block.preds) {
const predPlace = this.getPlaceAt(oldPlace, predBlock);
predDefs.set(predBlock, predPlace);
const predId = this.getIdAt(oldId, predBlock);
predDefs.set(predBlock, predId);
}
const phi: Phi = {
kind: "Phi",
lvalue: { place: newPlace, kind: InstructionKind.Const },
id: newId,
operands: predDefs,
};
@@ -150,7 +129,7 @@ class SSABuilder {
fixIncompletePhis(block: BasicBlock) {
const state = this.#states.get(block)!;
for (const phi of state.incompletePhis) {
this.addPhi(block, phi.old, phi.new);
this.addPhi(block, phi.oldId, phi.newId);
}
}
@@ -166,14 +145,12 @@ class SSABuilder {
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 [oldId, newId] of state.defs) {
text.push(` \$${oldId}: \$${newId}`);
}
for (const incompletePhi of state.incompletePhis) {
text.push(
` iphi \$${incompletePhi.new.identifier} = \$${incompletePhi.old.identifier}`
);
text.push(` iphi \$${incompletePhi.newId} = \$${incompletePhi.oldId}`);
}
}
@@ -17,8 +17,8 @@ function Component(props) {
bb0:
Const mutate a$5 = 1
Const mutate b$6 = 2
Const mutate x$7 = Array [mutate a$5, mutate b$6]
Return mutate x$7
Const mutate x$7 = Array [read a$5, read b$6]
Return freeze x$7
```
## Code
@@ -34,16 +34,15 @@ function foo$0() {
```
bb0:
Const mutate foo$11: phi()
Const mutate a$9 = Array []
Const mutate b$10 = Object { }
Call mutate foo$11(mutate a$9, mutate b$10)
Const mutate $12 = "div"
Let mutate _$13 = JSX <mutate $12 a={mutate a$9} ></mutate $12>
Call mutate foo$11(mutate b$10)
Const mutate $14 = "div"
Const mutate $15 = JSX <mutate $14 a={mutate a$9} b={mutate b$10} ></mutate $14>
Return mutate $15
Call mutate foo$4(mutate a$9, mutate b$10)
Const mutate $11 = "div"
Let mutate _$12 = JSX <read $11 a={freeze a$9} ></read $11>
Call mutate foo$4(mutate b$10)
Const mutate $13 = "div"
Const mutate $14 = JSX <read $13 a={read a$9} b={freeze b$10} ></read $13>
Return read $14
```
## Code
@@ -52,10 +51,10 @@ bb0:
function Component$0(props$1) {
const a$9 = [];
const b$10 = {};
foo$11(a$9, b$10);
let _$13 = <div a={a$9}></div>;
foo$4(a$9, b$10);
let _$12 = <div a={a$9}></div>;
foo$11(b$10);
foo$4(b$10);
return <div a={a$9} b={b$10}></div>;
}
@@ -24,27 +24,27 @@ bb0:
Let mutate x$7 = 1
Let mutate y$8 = 2
Const mutate $9 = 2
Const mutate $10 = Binary mutate y$8 === mutate $9
If (mutate $10) then:bb2 else:bb1
Const mutate $10 = Binary read y$8 === read $9
If (read $10) then:bb2 else:bb1
bb2:
predecessor blocks: bb0
Reassign mutate x$18 = 3
Goto bb1
bb1:
predecessor blocks: bb0 bb2
Const mutate y$12: phi(bb0: mutate y$8, bb2: mutate y$8)
Const mutate x$17: phi(bb0: mutate x$7, bb2: mutate x$18)
y$12: phi(bb0: y$8, bb2: y$8)
x$17: phi(bb0: x$7, bb2: x$18)
Const mutate $11 = 3
Const mutate $13 = Binary mutate y$12 === mutate $11
If (mutate $13) then:bb4 else:bb3
Const mutate $13 = Binary read y$12 === read $11
If (read $13) then:bb4 else:bb3
bb4:
predecessor blocks: bb1
Reassign mutate x$16 = 5
Goto bb3
bb3:
predecessor blocks: bb1 bb4
Const mutate x$14: phi(bb1: mutate x$17, bb4: mutate x$16)
Reassign mutate y$15 = mutate x$14
x$14: phi(bb1: x$17, bb4: x$16)
Reassign mutate y$15 = read x$14
Return
```
@@ -21,16 +21,16 @@ bb0:
Let mutate x$5 = 1
Let mutate y$6 = 2
Const mutate $7 = 2
Const mutate $8 = Binary mutate y$6 === mutate $7
If (mutate $8) then:bb2 else:bb1
Const mutate $8 = Binary read y$6 === read $7
If (read $8) then:bb2 else:bb1
bb2:
predecessor blocks: bb0
Reassign mutate x$11 = 3
Goto bb1
bb1:
predecessor blocks: bb0 bb2
Const mutate x$9: phi(bb0: mutate x$5, bb2: mutate x$11)
Reassign mutate y$10 = mutate x$9
x$9: phi(bb0: x$5, bb2: x$11)
Reassign mutate y$10 = read x$9
Return
```
@@ -20,18 +20,18 @@ bb0:
Goto bb1
bb1:
predecessor blocks: bb0 bb4
Const mutate x$7: phi(bb0: mutate x$5, bb4: mutate x$10)
x$7: phi(bb0: x$5, bb4: x$10)
Const mutate $6 = 10
Const mutate $8 = Binary mutate x$7 < mutate $6
If (mutate $8) then:bb4 else:bb2
Const mutate $8 = Binary read x$7 < read $6
If (read $8) then:bb4 else:bb2
bb4:
predecessor blocks: bb1
Const mutate $9 = 1
Reassign mutate x$10 = Binary mutate x$7 + mutate $9
Reassign mutate x$10 = Binary read x$7 + read $9
Goto bb1
bb2:
predecessor blocks: bb1
Return mutate x$7
Return read x$7
```
## Code
@@ -21,14 +21,14 @@ function foo() {
bb0:
Let mutate x$5 = 1
Let mutate y$6 = 2
If (mutate y$6) then:bb2 else:bb3
If (read y$6) then:bb2 else:bb3
bb2:
predecessor blocks: bb0
Let mutate z$8 = Binary mutate x$5 + mutate y$6
Let mutate z$8 = Binary read x$5 + read y$6
Goto bb1
bb3:
predecessor blocks: bb0
Let mutate z$7 = mutate x$5
Let mutate z$7 = read x$5
Goto bb1
bb1:
predecessor blocks: bb3 bb2
@@ -32,11 +32,10 @@ function Foo$0() {
```
bb0:
Const mutate Foo$8: phi()
Const mutate a$6 = Array []
Const mutate b$7 = Object { }
Let mutate c$9 = New mutate Foo$8(mutate a$6, mutate b$7)
Return mutate c$9
Let mutate c$8 = New mutate Foo$5(mutate a$6, mutate b$7)
Return freeze c$8
```
## Code
@@ -45,8 +44,8 @@ bb0:
function Component$0(props$1) {
const a$6 = [];
const b$7 = {};
let c$9 = new Foo$8(a$6, b$7);
return c$9;
let c$8 = new Foo$5(a$6, b$7);
return c$8;
}
```
@@ -25,8 +25,8 @@ bb0:
Let mutate x$6 = 1
Let mutate y$7 = 2
Const mutate $8 = 1
Const mutate $9 = Binary mutate x$6 > mutate $8
If (mutate $9) then:bb2 else:bb3
Const mutate $9 = Binary read x$6 > read $8
If (read $9) then:bb2 else:bb3
bb2:
predecessor blocks: bb0
Reassign mutate x$14 = 2
@@ -37,10 +37,10 @@ bb3:
Goto bb1
bb1:
predecessor blocks: bb3 bb2
Const mutate x$11: phi(bb3: mutate x$6, bb2: mutate x$14)
Const mutate y$12: phi(bb3: mutate y$10, bb2: mutate y$7)
Let mutate t$13 = Object { x: mutate x$11, y: mutate y$12 }
Return mutate t$13
x$11: phi(bb3: x$6, bb2: x$14)
y$12: phi(bb3: y$10, bb2: y$7)
Let mutate t$13 = Object { x: read x$11, y: read y$12 }
Return freeze t$13
```
## Code
@@ -17,8 +17,8 @@ function Component(props) {
bb0:
Const mutate a$5 = 1
Const mutate b$6 = 2
Const mutate x$7 = Object { a: mutate a$5, b: mutate b$6 }
Return mutate x$7
Const mutate x$7 = Object { a: read a$5, b: read b$6 }
Return freeze x$7
```
## Code
@@ -0,0 +1,38 @@
## Input
```javascript
function foo() {
const x = [];
const y = { x: x };
y.x.push([]);
return y;
}
```
## HIR
```
bb0:
Const mutate x$4 = Array []
Const mutate y$5 = Object { x: read x$4 }
Const mutate $6 = Array []
Call mutate y$5.x.push(mutate $6)
Return freeze y$5
```
## Code
```javascript
function foo$0() {
const x$4 = [];
const y$5 = {
x: x$4,
};
y$5.x.push([]);
return y$5;
}
```
@@ -0,0 +1,6 @@
function foo() {
const x = [];
const y = { x: x };
y.x.push([]);
return y;
}
@@ -0,0 +1,35 @@
## Input
```javascript
function foo() {
const x = [];
const y = {};
y.x = x;
return y;
}
```
## HIR
```
bb0:
Const mutate x$3 = Array []
Const mutate y$4 = Object { }
Reassign mutate y$5.x = read x$3
Return freeze y$5
```
## Code
```javascript
function foo$0() {
const x$3 = [];
const y$4 = {};
y$5 = x$3;
return y$5;
}
```
@@ -0,0 +1,6 @@
function foo() {
const x = [];
const y = {};
y.x = x;
return y;
}
@@ -19,16 +19,16 @@ function foo() {
bb0:
Let mutate x$4 = 1
Const mutate $5 = 1
Const mutate $6 = Binary mutate x$4 === mutate $5
If (mutate $6) then:bb2 else:bb1
Const mutate $6 = Binary read x$4 === read $5
If (read $6) then:bb2 else:bb1
bb2:
predecessor blocks: bb0
Reassign mutate x$8 = 2
Goto bb1
bb1:
predecessor blocks: bb0 bb2
Const mutate x$7: phi(bb0: mutate x$4, bb2: mutate x$8)
Return mutate x$7
x$7: phi(bb0: x$4, bb2: x$8)
Return read x$7
```
## Code
@@ -22,8 +22,8 @@ function foo() {
bb0:
Let mutate y$5 = 2
Const mutate $6 = 1
Const mutate $7 = Binary mutate y$5 > mutate $6
If (mutate $7) then:bb2 else:bb3
Const mutate $7 = Binary read y$5 > read $6
If (read $7) then:bb2 else:bb3
bb2:
predecessor blocks: bb0
Reassign mutate y$11 = 1
@@ -34,8 +34,8 @@ bb3:
Goto bb1
bb1:
predecessor blocks: bb3 bb2
Const mutate y$9: phi(bb3: mutate y$8, bb2: mutate y$11)
Let mutate x$10 = mutate y$9
y$9: phi(bb3: y$8, bb2: y$11)
Let mutate x$10 = read y$9
Return
```
@@ -19,10 +19,10 @@ function foo() {
bb0:
Let mutate x$4 = 1
Let mutate y$5 = 2
If (mutate y$5) then:bb2 else:bb1
If (read y$5) then:bb2 else:bb1
bb2:
predecessor blocks: bb0
Let mutate z$6 = Binary mutate x$4 + mutate y$5
Let mutate z$6 = Binary read x$4 + read y$5
Goto bb1
bb1:
predecessor blocks: bb0 bb2
@@ -30,32 +30,32 @@ function foo() {
bb0:
Let mutate x$10 = 1
Const mutate $11 = 2
Const mutate $12 = Binary mutate x$10 === mutate $11
Const mutate $12 = Binary read x$10 === read $11
Const mutate $13 = 1
Const mutate $14 = Binary mutate x$10 === mutate $13
Switch (mutate x$10)
Case mutate $14: bb5
Case mutate $12: bb3
Const mutate $14 = Binary read x$10 === read $13
Switch (<unknown> x$10)
Case read $14: bb5
Case read $12: bb3
Default: bb2
bb5:
predecessor blocks: bb0
Const mutate $21 = 1
Reassign mutate x$22 = Binary mutate x$10 + mutate $21
Reassign mutate x$22 = Binary read x$10 + read $21
Goto bb1
bb3:
predecessor blocks: bb0
Const mutate $19 = 2
Reassign mutate x$20 = Binary mutate x$10 + mutate $19
Reassign mutate x$20 = Binary read x$10 + read $19
Goto bb1
bb2:
predecessor blocks: bb0
Const mutate $15 = 3
Reassign mutate x$16 = Binary mutate x$10 + mutate $15
Reassign mutate x$16 = Binary read x$10 + read $15
Goto bb1
bb1:
predecessor blocks: bb5 bb3 bb2
Const mutate x$17: phi(bb5: mutate x$22, bb3: mutate x$20, bb2: mutate x$16)
Let mutate y$18 = mutate x$17
x$17: phi(bb5: x$22, bb3: x$20, bb2: x$16)
Let mutate y$18 = read x$17
Return
```
@@ -18,16 +18,16 @@ function foo() {
bb0:
Let mutate x$4 = 1
Const mutate $5 = 1
Const mutate $6 = Binary mutate x$4 === mutate $5
If (mutate $6) then:bb2 else:bb1
Const mutate $6 = Binary read x$4 === read $5
If (read $6) then:bb2 else:bb1
bb2:
predecessor blocks: bb0
Reassign mutate x$8 = 2
Goto bb1
bb1:
predecessor blocks: bb0 bb2
Const mutate x$7: phi(bb0: mutate x$4, bb2: mutate x$8)
Throw mutate x$7
x$7: phi(bb0: x$4, bb2: x$8)
Throw read x$7
```
## Code
@@ -21,18 +21,18 @@ bb0:
Goto bb1
bb1:
predecessor blocks: bb0 bb3
Const mutate x$7: phi(bb0: mutate x$5, bb3: mutate x$10)
x$7: phi(bb0: x$5, bb3: x$10)
Const mutate $6 = 10
Const mutate $8 = Binary mutate x$7 < mutate $6
If (mutate $8) then:bb3 else:bb2
Const mutate $8 = Binary read x$7 < read $6
If (read $8) then:bb3 else:bb2
bb3:
predecessor blocks: bb1
Const mutate $9 = 1
Reassign mutate x$10 = Binary mutate x$7 + mutate $9
Reassign mutate x$10 = Binary read x$7 + read $9
Goto bb1
bb2:
predecessor blocks: bb1
Return mutate x$7
Return read x$7
```
## Code