Improve DCE to handle unused loop phis

Improves DCE, using fixpoint iteration to detect values that are updated across 
loops but otherwise never read. There is still some further optimization we can 
do (the dce-loop case could optimize out `y`), but this seems like plenty for 
now. 

Actually, we probably have to add some return statements to our fixtures before 
landing this, because otherwise most of the code goes away.
This commit is contained in:
Joe Savona
2023-02-03 15:46:00 -08:00
parent 19e21b73a7
commit b009626431
32 changed files with 157 additions and 346 deletions
@@ -5,18 +5,12 @@
* LICENSE file in the root directory of this source tree.
*/
import {
BlockId,
HIRFunction,
Identifier,
Instruction,
InstructionValue,
} from "../HIR";
import { HIRFunction, Identifier, InstructionValue } from "../HIR";
import {
eachInstructionValueOperand,
eachTerminalOperand,
} from "../HIR/visitors";
import { assertExhaustive } from "../Utils/utils";
import { assertExhaustive, retainWhere } from "../Utils/utils";
/**
* Implements dead-code elimination, eliminating instructions whose values are unused.
@@ -26,71 +20,54 @@ import { assertExhaustive } from "../Utils/utils";
export function deadCodeElimination(fn: HIRFunction): void {
const used = new Set<Identifier>();
// Find any phi operands involved in a loop, which might otherwise appear as dead
// code when using a reverse iteration.
//
// A more advanced algorithm could still prune some of these operands
// let's keep it simple for now
const seen = new Set<BlockId>();
for (const [, block] of fn.body.blocks) {
for (const phi of block.phis) {
for (const [pred, operand] of phi.operands) {
if (!seen.has(pred)) {
used.add(operand);
}
}
}
seen.add(block.id);
}
// TODO: ensure a single iteration of the CFG if there are no back-edges,
// see EliminateRedundantPhi.ts for an example
let lastSize = -1;
while (used.size > lastSize) {
lastSize = used.size;
// Iterate blocks in postorder (successors before predecessors, excepting loops)
// to find usages before declarations
const reversedBlocks = [...fn.body.blocks.values()].reverse();
for (const block of reversedBlocks) {
for (const operand of eachTerminalOperand(block.terminal)) {
used.add(operand.identifier);
}
let nextInstructions: Array<Instruction> | null = null;
for (let i = block.instructions.length - 1; i >= 0; i--) {
const instr = block.instructions[i]!;
if (
!used.has(instr.lvalue.place.identifier) &&
pruneableValue(instr.value) &&
// Can't prune the last value of a value block, that's its value!
!(block.kind !== "block" && i === block.instructions.length - 1)
) {
continue;
}
used.add(instr.lvalue.place.identifier);
nextInstructions ??= [];
nextInstructions.push(instr);
for (const operand of eachInstructionValueOperand(instr.value)) {
// Iterate blocks in postorder (successors before predecessors, excepting loops)
// to find usages before declarations
const reversedBlocks = [...fn.body.blocks.values()].reverse();
for (const block of reversedBlocks) {
for (const operand of eachTerminalOperand(block.terminal)) {
used.add(operand.identifier);
}
}
if (nextInstructions !== null) {
nextInstructions.reverse();
block.instructions = nextInstructions;
}
for (const phi of block.phis) {
if (used.has(phi.id)) {
for (const [, operand] of phi.operands) {
used.add(operand);
for (let i = block.instructions.length - 1; i >= 0; i--) {
const instr = block.instructions[i]!;
if (
!used.has(instr.lvalue.place.identifier) &&
pruneableValue(instr.value) &&
// Can't prune the last value of a value block, that's its value!
!(block.kind !== "block" && i === block.instructions.length - 1)
) {
continue;
}
} else {
for (const [, operand] of phi.operands) {
if (used.has(operand)) {
used.add(phi.id);
for (const [, operand] of phi.operands) {
used.add(operand);
}
break;
used.add(instr.lvalue.place.identifier);
for (const operand of eachInstructionValueOperand(instr.value)) {
used.add(operand.identifier);
}
}
for (const phi of block.phis) {
if (used.has(phi.id)) {
for (const [pred, operand] of phi.operands) {
used.add(operand);
}
}
}
}
}
for (const [, block] of fn.body.blocks) {
for (const phi of block.phis) {
if (!used.has(phi.id)) {
block.phis.delete(phi);
}
}
retainWhere(block.instructions, (instr) =>
used.has(instr.lvalue.place.identifier)
);
}
}
/**
@@ -30,12 +30,10 @@ function foo(cond) {
const $ = React.useMemoCache();
const c_0 = $[0] !== cond;
let a;
let b;
let c;
if (c_0) {
a = {};
b = {};
c = {};
let b = {};
let c = {};
while (cond) {
const z = a;
a = b;
@@ -45,16 +43,9 @@ function foo(cond) {
}
$[0] = cond;
$[1] = a;
$[2] = b;
$[3] = c;
} else {
a = $[1];
b = $[2];
c = $[3];
}
a;
b;
c;
return a;
}
@@ -14,14 +14,7 @@ function f() {
## Code
```javascript
function f() {
const x = 1;
1;
const x$0 = 2;
1;
const x$1 = 3;
const x$2 = x$1 >>> 1;
}
function f() {}
```
@@ -22,23 +22,9 @@ function foo(a, b, c) {
```javascript
function foo(a, b, c) {
const x = undefined;
let x$0 = undefined;
if (a) {
2;
1;
const x$1 = 1;
x$0 = x$1;
} else {
0;
1;
const x$2 = 1;
x$0 = x$2;
}
1;
true;
return b;
}
@@ -0,0 +1,27 @@
## Input
```javascript
function foo(props) {
let x = 0;
let y = 0;
while (y < props.max) {
x++;
y++;
}
}
```
## Code
```javascript
function foo(props) {
let y = 0;
while (y < props.max) {
y = y + 1;
}
}
```
@@ -0,0 +1,8 @@
function foo(props) {
let x = 0;
let y = 0;
while (y < props.max) {
x++;
y++;
}
}
@@ -25,17 +25,7 @@ function foo(a, b, c) {
## Code
```javascript
function foo(a, b, c) {
const d = undefined;
const g = undefined;
const n = undefined;
const o = undefined;
const d$0 = a[0];
const g$1 = a[1][0].e.f;
const n$2 = b.l.m[0][0];
const o$3 = b.o;
}
function foo(a, b, c) {}
```
@@ -24,13 +24,7 @@ function foo(a, b, c) {
## Code
```javascript
function foo(a, b, c) {
const d = a[0];
const f = a[1][0].e.f;
const n = b.l.m[0][0];
const o = b.o;
}
function foo(a, b, c) {}
```
@@ -21,25 +21,24 @@ function foo(a, b) {
```javascript
function foo(a, b) {
const $ = React.useMemoCache();
const x = undefined;
const c_0 = $[0] !== a;
let x$0;
let x;
if (c_0) {
x$0 = undefined;
x = undefined;
if (a) {
const x$1 = 1;
x$0 = x$1;
const x$0 = 1;
x = x$0;
} else {
const x$2 = 2;
x$0 = x$2;
const x$1 = 2;
x = x$1;
}
$[0] = a;
$[1] = x$0;
$[1] = x;
} else {
x$0 = $[1];
x = $[1];
}
const y = x$0;
const y = x;
return y;
}
@@ -20,42 +20,41 @@ function foo(a, b, c, d) {
```javascript
function foo(a, b, c, d) {
const $ = React.useMemoCache();
const x = {};
const c_0 = $[0] !== b;
const c_1 = $[1] !== c;
let x$0;
let x;
if (c_0 || c_1) {
x$0 = undefined;
x = undefined;
if (someVal) {
const c_3 = $[3] !== b;
let x$1;
let x$0;
if (c_3) {
x$1 = { b: b };
x$0 = { b: b };
$[3] = b;
$[4] = x$1;
$[4] = x$0;
} else {
x$1 = $[4];
x$0 = $[4];
}
x$0 = x$1;
x = x$0;
} else {
const c_5 = $[5] !== c;
let x$2;
let x$1;
if (c_5) {
x$2 = { c: c };
x$1 = { c: c };
$[5] = c;
$[6] = x$2;
$[6] = x$1;
} else {
x$2 = $[6];
x$1 = $[6];
}
x$0 = x$2;
x = x$1;
}
$[0] = b;
$[1] = c;
$[2] = x$0;
$[2] = x;
} else {
x$0 = $[2];
x = $[2];
}
return x$0;
return x;
}
```
@@ -21,27 +21,26 @@ function foo(a, b, c, d) {
```javascript
function foo(a, b, c, d) {
const $ = React.useMemoCache();
const x = {};
const c_0 = $[0] !== b;
const c_1 = $[1] !== c;
let x$0;
let x;
if (c_0 || c_1) {
x$0 = undefined;
x = undefined;
if (someVal) {
const x$1 = { b: b };
x$0 = x$1;
const x$0 = { b: b };
x = x$0;
} else {
const x$2 = { c: c };
x$0 = x$2;
const x$1 = { c: c };
x = x$1;
}
x$0.f = 1;
x.f = 1;
$[0] = b;
$[1] = c;
$[2] = x$0;
$[2] = x;
} else {
x$0 = $[2];
x = $[2];
}
return x$0;
return x;
}
```
@@ -21,25 +21,24 @@ function component(a) {
```javascript
function component(a) {
const $ = React.useMemoCache();
const x = "foo";
const c_0 = $[0] !== a;
let x$0;
let x;
if (c_0) {
x$0 = undefined;
x = undefined;
if (a) {
const x$1 = "bar";
x$0 = x$1;
const x$0 = "bar";
x = x$0;
} else {
const x$2 = "baz";
x$0 = x$2;
const x$1 = "baz";
x = x$1;
}
$[0] = a;
$[1] = x$0;
$[1] = x;
} else {
x$0 = $[1];
x = $[1];
}
const y = x$0;
const y = x;
mutate(y);
return y;
}
@@ -36,59 +36,24 @@ function Component(props) {
```javascript
function Component(props) {
const $ = React.useMemoCache();
const c_0 = $[0] !== props.cond;
const c_1 = $[1] !== props.test;
const c_2 = $[2] !== props.v0;
const c_3 = $[3] !== props.v1;
const c_4 = $[4] !== props.v2;
const c_5 = $[5] !== props.cond2;
const c_6 = $[6] !== props.b;
const c_7 = $[7] !== props.c;
let x;
if (c_0 || c_1 || c_2 || c_3 || c_4 || c_5 || c_6 || c_7) {
x = undefined;
bb1: if (props.cond) {
switch (props.test) {
case 0: {
const x$0 = props.v0;
x = x$0;
break bb1;
}
case 1: {
const x$1 = props.v1;
x = x$1;
break bb1;
}
case 2: {
}
default: {
const x$2 = props.v2;
x = x$2;
}
bb1: if (props.cond) {
switch (props.test) {
case 0: {
break bb1;
}
} else {
if (props.cond2) {
const x$3 = props.b;
x = x$3;
} else {
const x$4 = props.c;
x = x$4;
case 1: {
break bb1;
}
case 2: {
}
default: {
}
}
$[0] = props.cond;
$[1] = props.test;
$[2] = props.v0;
$[3] = props.v1;
$[4] = props.v2;
$[5] = props.cond2;
$[6] = props.b;
$[7] = props.c;
$[8] = x;
} else {
x = $[8];
if (props.cond2) {
} else {
}
}
x;
}
```
@@ -40,7 +40,6 @@ function Component(props) {
t0 = $[2];
}
if (t0) {
const _ = <div a={a}></div>;
}
foo(a, b);
@@ -20,18 +20,7 @@ function foo() {
## Code
```javascript
function foo() {
const x = 1;
const y = 2;
2;
true;
const x$0 = 3;
3;
false;
const y$1 = x$0;
}
function foo() {}
```
@@ -17,16 +17,7 @@ function foo() {
## Code
```javascript
function foo() {
const x = 1;
const y = 2;
2;
true;
const x$0 = 3;
const y$1 = x$0;
}
function foo() {}
```
@@ -18,12 +18,7 @@ function foo() {
## Code
```javascript
function foo() {
const x = 1;
const y = 2;
const z = 3;
}
function foo() {}
```
@@ -25,10 +25,7 @@ function foo(a, b, c) {
const x = 0;
while (a) {
while (b) {
while (c) {
1;
1;
}
while (c) {}
}
}
return x;
@@ -18,23 +18,9 @@ function foo(a, b, c) {
```javascript
function foo(a, b, c) {
const $ = React.useMemoCache();
const x = a;
if (b) {
const c_0 = $[0] !== c;
let x$0;
if (c_0) {
x$0 = x;
if (c) {
const x$1 = c;
x$0 = x$1;
}
$[0] = c;
$[1] = x$0;
} else {
x$0 = $[1];
if (c) {
}
x$0;
}
}
@@ -27,14 +27,7 @@ function foo(a, b, c, d) {
## Code
```javascript
function foo(a, b, c, d) {
const x = 0;
true;
true;
const x$0 = a;
x$0;
}
function foo(a, b, c, d) {}
```
@@ -19,15 +19,7 @@ function foo() {
## Code
```javascript
function foo() {
const y = 2;
1;
true;
const y$0 = 1;
const x = y$0;
}
function foo() {}
```
@@ -12,10 +12,7 @@ function foo() {
## Code
```javascript
function foo() {
const x = 1;
const y = 2;
}
function foo() {}
```
@@ -16,12 +16,7 @@ function foo() {
## Code
```javascript
function foo() {
const x = 1;
const y = 2;
const z = 3;
}
function foo() {}
```
@@ -28,39 +28,17 @@ function foo() {
```javascript
function foo() {
const $ = React.useMemoCache();
const x = 1;
let x$0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
x$0 = undefined;
bb1: switch (x) {
case 1: {
1;
const x$1 = 2;
x$0 = x$1;
break bb1;
}
case 2: {
2;
const x$2 = 3;
x$0 = x$2;
break bb1;
}
default: {
3;
const x$3 = 4;
x$0 = x$3;
}
bb1: switch (x) {
case 1: {
break bb1;
}
case 2: {
break bb1;
}
default: {
}
$[0] = x$0;
} else {
x$0 = $[0];
}
const y = x$0;
}
```
@@ -18,10 +18,7 @@ function foo() {
```javascript
function foo() {
const x = 1;
while (true) {
1;
2;
}
while (true) {}
return x;
}
@@ -38,27 +38,21 @@ function foo(x) {
function foo(x) {
bb1: switch (x) {
case 0: {
const y = 0;
}
case 1: {
const y = 1;
break bb1;
}
case 2: {
break bb1;
}
case 3: {
const y = 3;
break bb1;
}
case 4: {
const y = 4;
}
case 5: {
const y = 5;
}
default: {
const y = 0;
}
}
}
@@ -15,7 +15,6 @@ function component(a, b) {
```javascript
function component(a, b) {
if (a > b) {
const m = {};
}
}
@@ -32,7 +32,6 @@ function component() {
b = $[1];
}
if (a > b) {
const m = {};
}
}
@@ -12,17 +12,7 @@ function component() {
## Code
```javascript
function component() {
const $ = React.useMemoCache();
let x;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
x = { t: 1 };
$[0] = x;
} else {
x = $[0];
}
const p = x.t;
}
function component() {}
```
@@ -45,9 +45,6 @@ function component() {
const v = x.v;
if (u > v) {
}
const y = x.u;
const z = x.v;
}
```
@@ -12,10 +12,7 @@ function component() {
## Code
```javascript
function component() {
const x = 1;
const y = 2;
}
function component() {}
```
@@ -34,7 +34,6 @@ function component() {
y = $[1];
}
if (x > y) {
const z = {};
}
const z_0 = foo();