@samitouri / QOS-React-2 / commits / f3d40c8c24

[rust] Recurse into functions in other passes

Updates eliminate_redundant_phis and constant_propagation to recurse into function expressions. I also realized there was a bug in EliminateRedundantPhis in which we wouldn't traverse into function expressions encountered after finding a back edge, so i fixed that logic in both versions.

Joe Savona committed Jul 13, 2023 at 13:34 UTC f3d40c8c248f9369dcdfdac78a12188db7474a59
5 files changed +90 -18
compiler/forget/crates/fixtures/tests/fixtures/function-expressions.js
+9 -1
@@ -1,5 +1,13 @@
1 function Component(props) {
2 + const y = 2;
3 const foo = function foo(x) {
3 - return x + 1;
4 + let a = 1;
5 + let b;
6 + if (a === 1) {
7 + b = 5 + 3;
8 + } else {
9 + b = false;
10 + }
11 + return x + y + b;
12 };
13 }
compiler/forget/crates/fixtures/tests/snapshots/fixtures_test__fixtures@function-expressions.js.snap
+39 -13
@@ -5,30 +5,56 @@ input_file: crates/fixtures/tests/fixtures/function-expressions.js
5 ---
6 Input:
7 function Component(props) {
8 + const y = 2;
9 const foo = function foo(x) {
9 - return x + 1;
10 + let a = 1;
11 + let b;
12 + if (a === 1) {
13 + b = 5 + 3;
14 + } else {
15 + b = false;
16 + }
17 + return x + y + b;
18 };
19 }
20
21
22 Output:
23 function Component(
16 - unknown props$3,
24 + unknown props$6,
25 )
26 entry bb0
27 bb0 (block)
20 - [0] #0 = Function @deps[] @context[]:
28 + [0] #0 = 2
29 + [1] #1 = StoreLocal Const unknown y$7 = unknown #0
30 + [2] #2 = Function @deps[] @context[]:
31 function foo(
22 - unknown x$0,
32 + unknown x$8,
33 )
34 entry bb1
35 bb1 (block)
26 - [0] #0 = LoadLocal unknown x$0
27 - [1] #1 = 1
28 - [2] #2 = Binary unknown #0 + unknown #1
29 - [3] Return unknown #2
30 - [1] #1 = StoreLocal Const unknown foo$4 = unknown #0
31 - [2] #2 = <undefined>
32 - [3] Return unknown #2
33 -
34 -
36 + [0] #0 = 1
37 + [1] #1 = StoreLocal Let unknown a$9 = unknown #0
38 + [2] #2 = DeclareLocal Let unknown b$10
39 + [3] #9 = 1
40 + [4] #10 = 1
41 + [5] #11 = true
42 + [6] Goto bb3
43 + bb3 (block)
44 + predecessors: bb1
45 + [7] #3 = 5
46 + [8] #4 = 3
47 + [9] #5 = 8
48 + [10] #6 = StoreLocal Reassign unknown b$11 = unknown #5
49 + [11] Goto bb2
50 + bb2 (block)
51 + predecessors: bb3
52 + [12] #12 = LoadLocal unknown x$8
53 + [13] #13 = LoadLocal unknown y$0
54 + [14] #14 = Binary unknown #12 + unknown #13
55 + [15] #15 = 8
56 + [16] #16 = Binary unknown #14 + unknown #15
57 + [17] Return unknown #16
58 + [3] #3 = StoreLocal Const unknown foo$16 = unknown #2
59 + [4] #4 = <undefined>
60 + [5] Return unknown #4
compiler/forget/crates/hir-optimization/src/constant_propagation.rs
+30 -1
@@ -10,8 +10,16 @@ use hir_ssa::eliminate_redundant_phis;
10
11 pub fn constant_propagation<'a>(env: &Environment<'a>, fun: &mut Function<'a>) {
12 let mut constants = Constants::new();
13 + constant_propagation_impl(env, fun, &mut constants);
14 +}
15 +
16 +fn constant_propagation_impl<'a>(
17 + env: &Environment<'a>,
18 + fun: &mut Function<'a>,
19 + constants: &mut Constants<'a>,
20 +) {
21 loop {
14 - let have_terminals_changed = apply_constant_propagation(env, fun, &mut constants);
22 + let have_terminals_changed = apply_constant_propagation(env, fun, constants);
23 if !have_terminals_changed {
24 break;
25 }
@@ -162,6 +170,27 @@ fn evaluate_instruction<'a>(
170 constants.insert(value.lvalue.identifier.identifier.id, const_value);
171 }
172 }
173 + InstructionValue::Function(value) => {
174 + // TODO: due to the outer fixpoint iteration this could visit the same
175 + // function many times. However we only strictly have to visit the function
176 + // again if the context variable's constant values have changed since last
177 + // time.
178 + // Instead, we can:
179 + // - Create a filtered Constants instance that extracts just the values for
180 + // the function (using its context variables list)
181 + // - Track the last such filtered Constants instance we visited the function
182 + // with. Only visit again if the Constants have changed.
183 + let mut inner_constants: Constants<'a> = value
184 + .lowered_function
185 + .context
186 + .iter()
187 + .filter_map(|id| {
188 + let value = constants.get(&id.identifier.id);
189 + value.map(|value| (id.identifier.id, value.clone()))
190 + })
191 + .collect();
192 + constant_propagation_impl(env, &mut value.lowered_function, &mut inner_constants);
193 + }
194 _ => {
195 // no-op, not all instructions can be processed
196 }
compiler/forget/crates/hir-ssa/src/eliminate_redundant_phis.rs
+10 -2
@@ -1,6 +1,6 @@
1 use std::collections::{HashMap, HashSet};
2
3 -use hir::{BlockId, Environment, Function, Identifier, IdentifierId, HIR};
3 +use hir::{BlockId, Environment, Function, Identifier, IdentifierId, InstructionValue, HIR};
4 use utils::RetainMut;
5
6 /// Pass to eliminate redundant phi nodes:
@@ -16,7 +16,7 @@ use utils::RetainMut;
16 /// and phis rewrite all their identifiers based on this table. The algorithm loops over the CFG repeatedly
17 /// until there are no new rewrites: for a CFG without back-edges it completes in a single pass.
18 type Rewrites<'a> = HashMap<IdentifierId, Identifier<'a>>;
19 -pub fn eliminate_redundant_phis<'a>(_env: &Environment, fun: &mut Function<'a>) {
19 +pub fn eliminate_redundant_phis<'a>(env: &Environment, fun: &mut Function<'a>) {
20 let hir = &mut fun.body;
21 let mut rewrites = Rewrites::new();
22
@@ -25,6 +25,7 @@ pub fn eliminate_redundant_phis<'a>(_env: &Environment, fun: &mut Function<'a>)
25
26 let mut len;
27 loop {
28 + let is_first_iteration = !has_back_edge;
29 len = rewrites.len();
30
31 for (_, block) in hir.blocks.iter_mut() {
@@ -75,6 +76,13 @@ pub fn eliminate_redundant_phis<'a>(_env: &Environment, fun: &mut Function<'a>)
76 rewrite(&rewrites, &mut store.identifier.identifier)
77 });
78 instr.each_identifier_load(|load| rewrite(&rewrites, &mut load.identifier));
79 + // Visit function expressions on first iteration of each block to
80 + // recursively eliminate any of their redundant phis
81 + if is_first_iteration {
82 + if let InstructionValue::Function(fun) = &mut instr.value {
83 + eliminate_redundant_phis(env, &mut fun.lowered_function);
84 + }
85 + }
86 }
87 }
88
compiler/forget/packages/babel-plugin-react-forget/src/SSA/EliminateRedundantPhi.ts
+2 -1
@@ -42,6 +42,7 @@ export function eliminateRedundantPhi(fn: HIRFunction): void {
42 // compare to see if any new rewrites were added in that iteration.
43 let size = rewrites.size;
44 do {
45 + const isFirstIteration = !hasBackEdge;
46 size = rewrites.size;
47 for (const [blockId, block] of ir.blocks) {
48 // On the first iteration of the loop check for any back-edges.
@@ -105,7 +106,7 @@ export function eliminateRedundantPhi(fn: HIRFunction): void {
106
107 // visit function expressions on first iteration of each block
108 if (
108 - !hasBackEdge &&
109 + isFirstIteration &&
110 instr.value.kind === "FunctionExpression" &&
111 fn.env.enableOptimizeFunctionExpressions
112 ) {