Optimize DCE to visit CFG only once when there are no loops

Optimizes dead code elimination. Currently it keeps iterating the control flow 
graph until no new usages have been discovered, which accounts for usages across 
loops. However, when there are no loops it's sufficient to iterate the CFG 
exactly once.
This commit is contained in:
Joe Savona
2023-02-13 13:59:44 -08:00
parent 6b67f597a6
commit 225fe0835c
@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import { HIRFunction, Identifier, InstructionValue } from "../HIR";
import { BlockId, HIRFunction, Identifier, InstructionValue } from "../HIR";
import {
eachInstructionValueOperand,
eachTerminalOperand,
@@ -20,11 +20,13 @@ import { assertExhaustive, retainWhere } from "../Utils/utils";
export function deadCodeElimination(fn: HIRFunction): void {
const used = new Set<Identifier>();
// 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;
// If there are no back-edges the algorithm can terminate after a single iteration
// of the blocks
const hasLoop = hasBackEdge(fn);
let size = used.size;
do {
size = used.size;
// Iterate blocks in postorder (successors before predecessors, excepting loops)
// to find usages before declarations
@@ -57,7 +59,7 @@ export function deadCodeElimination(fn: HIRFunction): void {
}
}
}
}
} while (used.size > size && hasLoop);
for (const [, block] of fn.body.blocks) {
for (const phi of block.phis) {
if (!used.has(phi.id)) {
@@ -115,3 +117,16 @@ function pruneableValue(value: InstructionValue): boolean {
}
}
}
function hasBackEdge(fn: HIRFunction): boolean {
const visited = new Set<BlockId>();
for (const [blockId, block] of fn.body.blocks) {
for (const predId of block.preds) {
if (!visited.has(predId)) {
return true;
}
}
visited.add(blockId);
}
return false;
}