@samitouri / QOS-React / commits / 225fe0835c

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.

Joe Savona committed Feb 13, 2023 at 13:59 UTC 225fe0835cc34139ee8644dd4fd01e48f3d952d4
1 file changed +22 -7
compiler/forget/src/Optimization/DeadCodeElimination.ts
+22 -7
@@ -5,7 +5,7 @@
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 -import { HIRFunction, Identifier, InstructionValue } from "../HIR";
8 +import { BlockId, HIRFunction, Identifier, InstructionValue } from "../HIR";
9 import {
10 eachInstructionValueOperand,
11 eachTerminalOperand,
@@ -20,11 +20,13 @@ import { assertExhaustive, retainWhere } from "../Utils/utils";
20 export function deadCodeElimination(fn: HIRFunction): void {
21 const used = new Set<Identifier>();
22
23 - // TODO: ensure a single iteration of the CFG if there are no back-edges,
24 - // see EliminateRedundantPhi.ts for an example
25 - let lastSize = -1;
26 - while (used.size > lastSize) {
27 - lastSize = used.size;
23 + // If there are no back-edges the algorithm can terminate after a single iteration
24 + // of the blocks
25 + const hasLoop = hasBackEdge(fn);
26 +
27 + let size = used.size;
28 + do {
29 + size = used.size;
30
31 // Iterate blocks in postorder (successors before predecessors, excepting loops)
32 // to find usages before declarations
@@ -57,7 +59,7 @@ export function deadCodeElimination(fn: HIRFunction): void {
59 }
60 }
61 }
60 - }
62 + } while (used.size > size && hasLoop);
63 for (const [, block] of fn.body.blocks) {
64 for (const phi of block.phis) {
65 if (!used.has(phi.id)) {
@@ -115,3 +117,16 @@ function pruneableValue(value: InstructionValue): boolean {
117 }
118 }
119 }
120 +
121 +function hasBackEdge(fn: HIRFunction): boolean {
122 + const visited = new Set<BlockId>();
123 + for (const [blockId, block] of fn.body.blocks) {
124 + for (const predId of block.preds) {
125 + if (!visited.has(predId)) {
126 + return true;
127 + }
128 + }
129 + visited.add(blockId);
130 + }
131 + return false;
132 +}