@samitouri / QOS-React-2 / commits / 7c38b15078

[rust] Align instruction data model closer to JS

Aligns the data model for `Instruction` closer to JS: * Adds an `lvalue: IdentifierOperand` property (Identifier + Effect) * Changes rvalues from being a reference to the definining instruction by instruction offset (InstrIx) to be a variable reference (IdentifierOperand) There are pros and cons to the previous offset-based approach. It's definitely convenient to be able to jump directly to the instruction that defined an operand value. However: * Many passes need to track things like basic reassignments, which mean they need to build up mappings of IdentifierId to some data. When all operands are IdentifierOperands, this can be a single mapping. * It aligns closer to JS, which makes porting a bit easier. But most of all: porting the `ReactiveFunction` data type — which is in tree form - is non-trivial with the offset-based approach. As we map HIR to the tree form, we'd have to remap every operand offset. Using identifiers for operands simplifies this. We can always revisit this design choice later.

Joe Savona committed Aug 10, 2023 at 10:59 UTC 7c38b1507805ad6db61702d52e5bdcd2d4859760
20 files changed +521 -535
compiler/forget/crates/forget_build_hir/src/build.rs
+28 -64
@@ -7,9 +7,9 @@ use forget_estree::{
7 VariableDeclarationKind,
8 };
9 use forget_hir::{
10 - BlockKind, BranchTerminal, Environment, ForTerminal, GotoKind, IdentifierOperand, InstrIx,
10 + BlockKind, BranchTerminal, Environment, ForTerminal, GotoKind, IdentifierOperand,
11 InstructionKind, InstructionValue, JSXAttribute, JSXElement, LValue, LoadGlobal, LoadLocal,
12 - Operand, PlaceOrSpread, TerminalValue,
12 + PlaceOrSpread, TerminalValue,
13 };
14
15 use crate::builder::{Builder, LoopScope};
@@ -69,10 +69,7 @@ pub fn build(env: &Environment, fun: &Function) -> Result<Box<forget_hir::Functi
69 }));
70 builder.terminate(
71 TerminalValue::Return(forget_hir::ReturnTerminal {
72 - value: Operand {
73 - ix: implicit_return_value,
74 - effect: None,
75 - },
72 + value: implicit_return_value,
73 }),
74 forget_hir::BlockKind::Block,
75 );
@@ -133,16 +130,14 @@ fn lower_statement(
130 );
131 }
132 Statement::ReturnStatement(stmt) => {
136 - let ix = match &stmt.argument {
133 + let value = match &stmt.argument {
134 Some(argument) => lower_expression(env, builder, argument)?,
135 None => builder.push(InstructionValue::Primitive(forget_hir::Primitive {
136 value: JsValue::Undefined,
137 })),
138 };
139 builder.terminate(
143 - TerminalValue::Return(forget_hir::ReturnTerminal {
144 - value: Operand { ix, effect: None },
145 - }),
140 + TerminalValue::Return(forget_hir::ReturnTerminal { value }),
141 BlockKind::Block,
142 );
143 }
@@ -180,10 +175,7 @@ fn lower_statement(
175
176 let test = lower_expression(env, builder, &stmt.test)?;
177 let terminal = TerminalValue::If(forget_hir::IfTerminal {
183 - test: Operand {
184 - ix: test,
185 - effect: None,
186 - },
178 + test,
179 consequent: consequent_block,
180 alternate: alternate_block,
181 fallthrough: Some(fallthrough_block.id),
@@ -253,10 +245,7 @@ fn lower_statement(
245 if let Some(test) = &stmt.test {
246 let test_value = lower_expression(env, builder, test)?;
247 let terminal = TerminalValue::Branch(BranchTerminal {
256 - test: Operand {
257 - ix: test_value,
258 - effect: None,
259 - },
248 + test: test_value,
249 consequent: body_block,
250 alternate: fallthrough_block.id,
251 });
@@ -333,7 +322,7 @@ fn lower_expression(
322 env: &Environment,
323 builder: &mut Builder,
324 expr: &Expression,
336 -) -> Result<InstrIx, Diagnostic> {
325 +) -> Result<IdentifierOperand, Diagnostic> {
326 let value = match expr {
327 Expression::Identifier(expr) => {
328 let identifier = env.resolve_variable_reference(expr.as_ref());
@@ -368,17 +357,11 @@ fn lower_expression(
357 let mut elements = Vec::with_capacity(expr.elements.len());
358 for expr in &expr.elements {
359 let element = match expr {
371 - Some(forget_estree::ExpressionOrSpread::SpreadElement(expr)) => {
372 - Some(PlaceOrSpread::Spread(Operand {
373 - ix: lower_expression(env, builder, &expr.argument)?,
374 - effect: None,
375 - }))
376 - }
360 + Some(forget_estree::ExpressionOrSpread::SpreadElement(expr)) => Some(
361 + PlaceOrSpread::Spread(lower_expression(env, builder, &expr.argument)?),
362 + ),
363 Some(forget_estree::ExpressionOrSpread::Expression(expr)) => {
378 - Some(PlaceOrSpread::Place(Operand {
379 - ix: lower_expression(env, builder, expr)?,
380 - effect: None,
381 - }))
364 + Some(PlaceOrSpread::Place(lower_expression(env, builder, expr)?))
365 }
366 None => None,
367 };
@@ -405,15 +388,9 @@ fn lower_expression(
388 let left = lower_expression(env, builder, &expr.left)?;
389 let right = lower_expression(env, builder, &expr.right)?;
390 InstructionValue::Binary(forget_hir::Binary {
408 - left: Operand {
409 - ix: left,
410 - effect: None,
411 - },
391 + left,
392 operator: expr.operator,
413 - right: Operand {
414 - ix: right,
415 - effect: None,
416 - },
393 + right,
394 })
395 }
396
@@ -442,13 +419,7 @@ fn lower_expression(
419
420 let callee = lower_expression(env, builder, &callee_expr)?;
421 let arguments = lower_arguments(env, builder, &expr.arguments)?;
445 - InstructionValue::Call(forget_hir::Call {
446 - callee: Operand {
447 - ix: callee,
448 - effect: None,
449 - },
450 - arguments,
451 - })
422 + InstructionValue::Call(forget_hir::Call { callee, arguments })
423 }
424
425 Expression::JSXElement(expr) => {
@@ -469,15 +440,11 @@ fn lower_arguments(
440 for arg in args {
441 let element = match arg {
442 forget_estree::ExpressionOrSpread::SpreadElement(arg) => {
472 - PlaceOrSpread::Spread(Operand {
473 - ix: lower_expression(env, builder, &arg.argument)?,
474 - effect: None,
475 - })
443 + PlaceOrSpread::Spread(lower_expression(env, builder, &arg.argument)?)
444 + }
445 + forget_estree::ExpressionOrSpread::Expression(arg) => {
446 + PlaceOrSpread::Place(lower_expression(env, builder, arg)?)
447 }
477 - forget_estree::ExpressionOrSpread::Expression(arg) => PlaceOrSpread::Place(Operand {
478 - ix: lower_expression(env, builder, arg)?,
479 - effect: None,
480 - }),
448 };
449 arguments.push(element);
450 }
@@ -524,12 +491,12 @@ fn lower_jsx_element(
491 .map(|attr| lower_jsx_attribute(env, builder, attr))
492 .collect();
493 let _props = props?;
527 - let children: Result<Vec<Operand>, Diagnostic> = expr
494 + let children: Result<Vec<IdentifierOperand>, Diagnostic> = expr
495 .children
496 .iter()
497 .map(|child| {
531 - let ix = lower_jsx_child(env, builder, child)?;
532 - Ok(Operand { effect: None, ix })
498 + let child = lower_jsx_child(env, builder, child)?;
499 + Ok(child)
500 })
501 .collect();
502 let _children = children?;
@@ -557,7 +524,7 @@ fn lower_jsx_child(
524 _env: &Environment,
525 _builder: &mut Builder,
526 _child: &forget_estree::JSXChildItem,
560 -) -> Result<InstrIx, Diagnostic> {
527 +) -> Result<IdentifierOperand, Diagnostic> {
528 todo!("lower jsx child")
529 }
530
@@ -566,8 +533,8 @@ fn lower_assignment(
533 builder: &mut Builder,
534 kind: InstructionKind,
535 lvalue: &AssignmentTarget,
569 - value: InstrIx,
570 -) -> Result<InstrIx, Diagnostic> {
536 + value: IdentifierOperand,
537 +) -> Result<IdentifierOperand, Diagnostic> {
538 Ok(match lvalue {
539 AssignmentTarget::Pattern(lvalue) => {
540 lower_assignment_pattern(env, builder, kind, lvalue, value)?
@@ -581,17 +548,14 @@ fn lower_assignment_pattern(
548 builder: &mut Builder,
549 kind: InstructionKind,
550 lvalue: &Pattern,
584 - value: InstrIx,
585 -) -> Result<InstrIx, Diagnostic> {
551 + value: IdentifierOperand,
552 +) -> Result<IdentifierOperand, Diagnostic> {
553 Ok(match lvalue {
554 Pattern::Identifier(lvalue) => {
555 let identifier = lower_identifier_for_assignment(env, builder, kind, lvalue)?;
556 builder.push(InstructionValue::StoreLocal(forget_hir::StoreLocal {
557 lvalue: LValue { identifier, kind },
591 - value: Operand {
592 - ix: value,
593 - effect: None,
594 - },
558 + value,
559 }))
560 }
561 _ => todo!("lower assignment pattern for {:#?}", lvalue),
compiler/forget/crates/forget_build_hir/src/builder.rs
+9 -4
@@ -4,8 +4,8 @@ use std::rc::Rc;
4 use forget_diagnostics::Diagnostic;
5 use forget_hir::{
6 initialize_hir, BasicBlock, BlockId, BlockKind, Blocks, Environment, GotoKind, IdentifierData,
7 - InstrIx, Instruction, InstructionIdGenerator, InstructionValue, Terminal, TerminalValue, Type,
8 - HIR,
7 + IdentifierOperand, InstrIx, Instruction, InstructionIdGenerator, InstructionValue, Terminal,
8 + TerminalValue, Type, HIR,
9 };
10
11 use crate::BuildHIRError;
@@ -116,15 +116,20 @@ impl<'e> Builder<'e> {
116 }
117
118 /// Adds a new instruction to the end of the work in progress block
119 - pub(crate) fn push(&mut self, value: InstructionValue) -> InstrIx {
119 + pub(crate) fn push(&mut self, value: InstructionValue) -> IdentifierOperand {
120 + let lvalue = IdentifierOperand {
121 + identifier: self.environment.new_temporary(),
122 + effect: None,
123 + };
124 let instr = Instruction {
125 id: self.id_gen.next(),
126 + lvalue: lvalue.clone(),
127 value,
128 };
129 let ix = InstrIx::new(self.instructions.len() as u32);
130 self.instructions.push(instr);
131 self.wip.instructions.push(ix);
127 - ix
132 + lvalue
133 }
134
135 /// Terminates the work in progress block with the given terminal, and starts a new
compiler/forget/crates/forget_fixtures/tests/snapshots/fixtures_test__fixtures@constant-propagation-constant-if-condition.js.snap
+26 -26
@@ -45,29 +45,29 @@ function Component(
45 )
46 entry bb0
47 bb0 (block)
48 - [0] #0 = 1
49 - [1] #1 = StoreLocal Let unknown a$5 = unknown #0
50 - [2] #2 = DeclareLocal Let unknown b$6
51 - [3] #7 = 1
52 - [4] #8 = 1
53 - [5] #9 = true
54 - [6] #3 = true
55 - [7] #4 = StoreLocal Reassign unknown b$7 = unknown #3
56 - [8] #10 = DeclareLocal Let unknown c$9
57 - [9] #15 = true
58 - [10] #11 = "hello"
59 - [11] #12 = StoreLocal Reassign unknown c$11 = unknown #11
60 - [12] #16 = DeclareLocal Let unknown d$13
61 - [13] #21 = "hello"
62 - [14] #22 = "hello"
63 - [15] #23 = true
64 - [16] #17 = 42
65 - [17] #18 = StoreLocal Reassign unknown d$15 = unknown #17
66 - [18] #24 = DeclareLocal Let unknown e$17
67 - [19] #29 = 42
68 - [20] #30 = 42
69 - [21] #31 = true
70 - [22] #25 = "ok"
71 - [23] #26 = StoreLocal Reassign unknown e$19 = unknown #25
72 - [24] #32 = "ok"
73 - [25] Return unknown #32
48 + [0] unknown $0 = 1
49 + [1] unknown $2 = StoreLocal Let unknown a$39 = unknown $0
50 + [2] unknown $4 = DeclareLocal Let unknown b$40
51 + [3] unknown $9 = 1
52 + [4] unknown $10 = 1
53 + [5] unknown $11 = true
54 + [6] unknown $5 = true
55 + [7] unknown $6 = StoreLocal Reassign unknown b$41 = unknown $5
56 + [8] unknown $13 = DeclareLocal Let unknown c$43
57 + [9] unknown $18 = true
58 + [10] unknown $14 = "hello"
59 + [11] unknown $15 = StoreLocal Reassign unknown c$45 = unknown $14
60 + [12] unknown $20 = DeclareLocal Let unknown d$47
61 + [13] unknown $25 = "hello"
62 + [14] unknown $26 = "hello"
63 + [15] unknown $27 = true
64 + [16] unknown $21 = 42
65 + [17] unknown $22 = StoreLocal Reassign unknown d$49 = unknown $21
66 + [18] unknown $29 = DeclareLocal Let unknown e$51
67 + [19] unknown $34 = 42
68 + [20] unknown $35 = 42
69 + [21] unknown $36 = true
70 + [22] unknown $30 = "ok"
71 + [23] unknown $31 = StoreLocal Reassign unknown e$53 = unknown $30
72 + [24] unknown $37 = "ok"
73 + [25] Return unknown $37
compiler/forget/crates/forget_fixtures/tests/snapshots/fixtures_test__fixtures@constant-propagation.js.snap
+109 -109
@@ -68,131 +68,131 @@ function Component(props) {
68
69 Output:
70 function Component(
71 - unknown props$5,
71 + unknown props$108,
72 )
73 entry bb0
74 bb0 (block)
75 - [0] #0 = DeclareLocal Let unknown a$6
76 - [1] #1 = LoadGlobal Math
77 - [2] #2 = StoreLocal Reassign unknown a$7 = unknown #1
78 - [3] #3 = LoadGlobal Math
79 - [4] #4 = DeclareLocal Let unknown b$8
80 - [5] #9 = LoadLocal unknown props$5
81 - [6] If unknown #9 consequent=bb2 alternate=bb3 fallthrough=bb1
75 + [0] unknown $2 = DeclareLocal Let unknown a$109
76 + [1] unknown $3 = LoadGlobal Math
77 + [2] unknown $4 = StoreLocal Reassign unknown a$110 = unknown $3
78 + [3] unknown $5 = LoadGlobal Math
79 + [4] unknown $7 = DeclareLocal Let unknown b$111
80 + [5] unknown $12 = LoadLocal unknown props$108
81 + [6] If unknown $12 consequent=bb2 alternate=bb3 fallthrough=bb1
82 bb2 (block)
83 predecessors: bb0
84 - [7] #5 = true
85 - [8] #6 = StoreLocal Reassign unknown b$9 = unknown #5
84 + [7] unknown $8 = true
85 + [8] unknown $9 = StoreLocal Reassign unknown b$112 = unknown $8
86 [9] Goto bb1
87 bb3 (block)
88 predecessors: bb0
89 - [10] #7 = true
90 - [11] #8 = StoreLocal Reassign unknown b$10 = unknown #7
89 + [10] unknown $10 = true
90 + [11] unknown $11 = StoreLocal Reassign unknown b$113 = unknown $10
91 [12] Goto bb1
92 bb1 (block)
93 predecessors: bb2, bb3
94 - b$11: phi(bb2: b$9, bb3: b$10)
95 - [13] #10 = true
96 - [14] #11 = DeclareLocal Let unknown c$12
97 - [15] #16 = LoadLocal unknown props$5
98 - [16] If unknown #16 consequent=bb5 alternate=bb6 fallthrough=bb4
94 + b$114: phi(bb2: b$112, bb3: b$113)
95 + [13] unknown $13 = true
96 + [14] unknown $15 = DeclareLocal Let unknown c$115
97 + [15] unknown $20 = LoadLocal unknown props$108
98 + [16] If unknown $20 consequent=bb5 alternate=bb6 fallthrough=bb4
99 bb5 (block)
100 predecessors: bb1
101 - [17] #12 = true
102 - [18] #13 = StoreLocal Reassign unknown c$14 = unknown #12
101 + [17] unknown $16 = true
102 + [18] unknown $17 = StoreLocal Reassign unknown c$117 = unknown $16
103 [19] Goto bb4
104 bb6 (block)
105 predecessors: bb1
106 - [20] #14 = 42
107 - [21] #15 = StoreLocal Reassign unknown c$15 = unknown #14
106 + [20] unknown $18 = 42
107 + [21] unknown $19 = StoreLocal Reassign unknown c$118 = unknown $18
108 [22] Goto bb4
109 bb4 (block)
110 predecessors: bb5, bb6
111 - c$16: phi(bb5: c$14, bb6: c$15)
112 - [23] #17 = LoadLocal unknown c$16
113 - [24] #18 = 42
114 - [25] #19 = 1
115 - [26] #20 = 43
116 - [27] #21 = 42
117 - [28] #22 = 1
118 - [29] #23 = 41
119 - [30] #24 = 42
120 - [31] #25 = 2
121 - [32] #26 = 84
122 - [33] #27 = 42
123 - [34] #28 = 2
124 - [35] #29 = 21
125 - [36] #30 = 0
126 - [37] #31 = 1
127 - [38] #32 = false
128 - [39] #33 = 0
129 - [40] #34 = 1
130 - [41] #35 = true
131 - [42] #36 = 0
132 - [43] #37 = 1
133 - [44] #38 = false
134 - [45] #39 = 0
135 - [46] #40 = 1
136 - [47] #41 = true
137 - [48] #42 = 0
138 - [49] #43 = 0
139 - [50] #44 = true
140 - [51] #45 = LoadGlobal NaN
141 - [52] #46 = LoadGlobal NaN
142 - [53] #47 = Binary unknown #45 == unknown #46
143 - [54] #48 = LoadGlobal NaN
144 - [55] #49 = LoadGlobal NaN
145 - [56] #50 = Binary unknown #48 != unknown #49
146 - [57] #51 = LoadGlobal NaN
147 - [58] #52 = LoadGlobal NaN
148 - [59] #53 = Binary unknown #51 !== unknown #52
149 - [60] #54 = LoadGlobal NaN
150 - [61] #55 = LoadGlobal NaN
151 - [62] #56 = Binary unknown #54 !== unknown #55
152 - [63] #57 = "hello"
153 - [64] #58 = "hello"
154 - [65] #59 = true
155 - [66] #60 = "hello"
156 - [67] #61 = "hello"
157 - [68] #62 = false
158 - [69] #63 = "hello"
159 - [70] #64 = "hello"
160 - [71] #65 = true
161 - [72] #66 = "hello"
162 - [73] #67 = "hello"
163 - [74] #68 = false
164 - [75] #69 = "hello"
165 - [76] #70 = "world"
166 - [77] #71 = false
167 - [78] #72 = "hello"
168 - [79] #73 = "world"
169 - [80] #74 = true
170 - [81] #75 = "hello"
171 - [82] #76 = "world"
172 - [83] #77 = false
173 - [84] #78 = "hello"
174 - [85] #79 = "world"
175 - [86] #80 = true
176 - [87] #81 = true
177 - [88] #82 = true
178 - [89] #83 = true
179 - [90] #84 = true
180 - [91] #85 = true
181 - [92] #86 = false
182 - [93] #87 = true
183 - [94] #88 = true
184 - [95] #89 = true
185 - [96] #90 = true
186 - [97] #91 = true
187 - [98] #92 = false
188 - [99] #93 = 5
189 - [100] #94 = 60
190 - [101] #95 = 300
191 - [102] #96 = 60
192 - [103] #97 = 18000
193 - [104] #98 = 1000
194 - [105] #99 = 18000000
195 - [106] #100 = StoreLocal Let unknown x$17 = unknown #99
196 - [107] #101 = 18000000
197 - [108] #102 = <undefined>
198 - [109] Return unknown #102
111 + c$119: phi(bb5: c$117, bb6: c$118)
112 + [23] unknown $21 = LoadLocal unknown c$119
113 + [24] unknown $22 = 42
114 + [25] unknown $23 = 1
115 + [26] unknown $24 = 43
116 + [27] unknown $25 = 42
117 + [28] unknown $26 = 1
118 + [29] unknown $27 = 41
119 + [30] unknown $28 = 42
120 + [31] unknown $29 = 2
121 + [32] unknown $30 = 84
122 + [33] unknown $31 = 42
123 + [34] unknown $32 = 2
124 + [35] unknown $33 = 21
125 + [36] unknown $34 = 0
126 + [37] unknown $35 = 1
127 + [38] unknown $36 = false
128 + [39] unknown $37 = 0
129 + [40] unknown $38 = 1
130 + [41] unknown $39 = true
131 + [42] unknown $40 = 0
132 + [43] unknown $41 = 1
133 + [44] unknown $42 = false
134 + [45] unknown $43 = 0
135 + [46] unknown $44 = 1
136 + [47] unknown $45 = true
137 + [48] unknown $46 = 0
138 + [49] unknown $47 = 0
139 + [50] unknown $48 = true
140 + [51] unknown $49 = LoadGlobal NaN
141 + [52] unknown $50 = LoadGlobal NaN
142 + [53] unknown $51 = Binary unknown $49 == unknown $50
143 + [54] unknown $52 = LoadGlobal NaN
144 + [55] unknown $53 = LoadGlobal NaN
145 + [56] unknown $54 = Binary unknown $52 != unknown $53
146 + [57] unknown $55 = LoadGlobal NaN
147 + [58] unknown $56 = LoadGlobal NaN
148 + [59] unknown $57 = Binary unknown $55 !== unknown $56
149 + [60] unknown $58 = LoadGlobal NaN
150 + [61] unknown $59 = LoadGlobal NaN
151 + [62] unknown $60 = Binary unknown $58 !== unknown $59
152 + [63] unknown $61 = "hello"
153 + [64] unknown $62 = "hello"
154 + [65] unknown $63 = true
155 + [66] unknown $64 = "hello"
156 + [67] unknown $65 = "hello"
157 + [68] unknown $66 = false
158 + [69] unknown $67 = "hello"
159 + [70] unknown $68 = "hello"
160 + [71] unknown $69 = true
161 + [72] unknown $70 = "hello"
162 + [73] unknown $71 = "hello"
163 + [74] unknown $72 = false
164 + [75] unknown $73 = "hello"
165 + [76] unknown $74 = "world"
166 + [77] unknown $75 = false
167 + [78] unknown $76 = "hello"
168 + [79] unknown $77 = "world"
169 + [80] unknown $78 = true
170 + [81] unknown $79 = "hello"
171 + [82] unknown $80 = "world"
172 + [83] unknown $81 = false
173 + [84] unknown $82 = "hello"
174 + [85] unknown $83 = "world"
175 + [86] unknown $84 = true
176 + [87] unknown $85 = true
177 + [88] unknown $86 = true
178 + [89] unknown $87 = true
179 + [90] unknown $88 = true
180 + [91] unknown $89 = true
181 + [92] unknown $90 = false
182 + [93] unknown $91 = true
183 + [94] unknown $92 = true
184 + [95] unknown $93 = true
185 + [96] unknown $94 = true
186 + [97] unknown $95 = true
187 + [98] unknown $96 = false
188 + [99] unknown $97 = 5
189 + [100] unknown $98 = 60
190 + [101] unknown $99 = 300
191 + [102] unknown $100 = 60
192 + [103] unknown $101 = 18000
193 + [104] unknown $102 = 1000
194 + [105] unknown $103 = 18000000
195 + [106] unknown $105 = StoreLocal Let unknown x$120 = unknown $103
196 + [107] unknown $106 = 18000000
197 + [108] unknown $107 = <undefined>
198 + [109] Return unknown $107
compiler/forget/crates/forget_fixtures/tests/snapshots/fixtures_test__fixtures@for-statement.js.snap
+20 -20
@@ -18,37 +18,37 @@ function foo(
18 )
19 entry bb0
20 bb0 (block)
21 - [0] #0 = 0
22 - [1] #1 = StoreLocal Let unknown x$2 = unknown #0
21 + [0] unknown $0 = 0
22 + [1] unknown $2 = StoreLocal Let unknown x$19 = unknown $0
23 [2] For init=bb3 test=bb1 update=bb4 body=bb5 fallthrough=bb2
24 bb3 (loop)
25 predecessors: bb0
26 - [3] #2 = 0
27 - [4] #3 = StoreLocal Let unknown i$3 = unknown #2
26 + [3] unknown $3 = 0
27 + [4] unknown $5 = StoreLocal Let unknown i$20 = unknown $3
28 [5] Goto bb1
29 bb1 (loop)
30 predecessors: bb3, bb4
31 - i$4: phi(bb3: i$3, bb4: i$7)
32 - x$5: phi(bb3: x$2, bb4: x$6)
33 - [6] #12 = LoadLocal unknown i$4
34 - [7] #13 = 10
35 - [8] #14 = Binary unknown #12 < unknown #13
36 - [9] Branch unknown #14 consequent=bb5 alternate=bb2
31 + i$21: phi(bb3: i$20, bb4: i$24)
32 + x$22: phi(bb3: x$19, bb4: x$23)
33 + [6] unknown $14 = LoadLocal unknown i$21
34 + [7] unknown $15 = 10
35 + [8] unknown $16 = Binary unknown $14 < unknown $15
36 + [9] Branch unknown $16 consequent=bb5 alternate=bb2
37 bb5 (block)
38 predecessors: bb1
39 - [10] #8 = LoadLocal unknown x$5
40 - [11] #9 = LoadLocal unknown i$4
41 - [12] #10 = Binary unknown #8 + unknown #9
42 - [13] #11 = StoreLocal Reassign unknown x$6 = unknown #10
39 + [10] unknown $10 = LoadLocal unknown x$22
40 + [11] unknown $11 = LoadLocal unknown i$21
41 + [12] unknown $12 = Binary unknown $10 + unknown $11
42 + [13] unknown $13 = StoreLocal Reassign unknown x$23 = unknown $12
43 [14] Goto bb4
44 bb4 (loop)
45 predecessors: bb5
46 - [15] #4 = LoadLocal unknown i$4
47 - [16] #5 = 1
48 - [17] #6 = Binary unknown #4 + unknown #5
49 - [18] #7 = StoreLocal Reassign unknown i$7 = unknown #6
46 + [15] unknown $6 = LoadLocal unknown i$21
47 + [16] unknown $7 = 1
48 + [17] unknown $8 = Binary unknown $6 + unknown $7
49 + [18] unknown $9 = StoreLocal Reassign unknown i$24 = unknown $8
50 [19] Goto bb1
51 bb2 (block)
52 predecessors: bb1
53 - [20] #15 = LoadLocal unknown x$5
54 - [21] Return unknown #15
53 + [20] unknown $17 = LoadLocal unknown x$22
54 + [21] Return unknown $17
compiler/forget/crates/forget_fixtures/tests/snapshots/fixtures_test__fixtures@function-expressions.js.snap
+52 -52
@@ -29,66 +29,66 @@ function Component(props) {
29
30 Output:
31 function Component(
32 - unknown props$10,
32 + unknown props$58,
33 )
34 entry bb0
35 bb0 (block)
36 - [0] #0 = 2
37 - [1] #1 = StoreLocal Const unknown x$11 = unknown #0
38 - [2] #2 = Function @deps[] @context[unknown x$11]:
36 + [0] unknown $1 = 2
37 + [1] unknown $3 = StoreLocal Const unknown x$59 = unknown $1
38 + [2] unknown $54 = Function @deps[] @context[unknown x$59]:
39 function foo(
40 - unknown y$12,
40 + unknown y$60,
41 )
42 entry bb1
43 bb1 (block)
44 - [0] #0 = 1
45 - [1] #1 = StoreLocal Let unknown a$13 = unknown #0
46 - [2] #2 = DeclareLocal Let unknown b$14
47 - [3] #9 = 1
48 - [4] #10 = 1
49 - [5] #11 = true
50 - [6] #3 = 5
51 - [7] #4 = 3
52 - [8] #5 = 8
53 - [9] #6 = StoreLocal Reassign unknown b$15 = unknown #5
54 - [10] #12 = 2
55 - [11] #13 = LoadLocal unknown y$12
56 - [12] #14 = Binary unknown #12 + unknown #13
57 - [13] #15 = 1
58 - [14] #16 = Binary unknown #14 + unknown #15
59 - [15] #17 = 8
60 - [16] #18 = Binary unknown #16 + unknown #17
61 - [17] #19 = Function @deps[] @context[unknown x$11, unknown y$12, unknown a$13, unknown b$15]:
44 + [0] unknown $5 = 1
45 + [1] unknown $7 = StoreLocal Let unknown a$61 = unknown $5
46 + [2] unknown $9 = DeclareLocal Let unknown b$62
47 + [3] unknown $16 = 1
48 + [4] unknown $17 = 1
49 + [5] unknown $18 = true
50 + [6] unknown $10 = 5
51 + [7] unknown $11 = 3
52 + [8] unknown $12 = 8
53 + [9] unknown $13 = StoreLocal Reassign unknown b$63 = unknown $12
54 + [10] unknown $19 = 2
55 + [11] unknown $20 = LoadLocal unknown y$60
56 + [12] unknown $21 = Binary unknown $19 + unknown $20
57 + [13] unknown $22 = 1
58 + [14] unknown $23 = Binary unknown $21 + unknown $22
59 + [15] unknown $24 = 8
60 + [16] unknown $25 = Binary unknown $23 + unknown $24
61 + [17] unknown $48 = Function @deps[] @context[unknown x$59, unknown y$60, unknown a$61, unknown b$63]:
62 function bar(
63 - unknown z$21,
63 + unknown z$69,
64 )
65 entry bb5
66 bb5 (block)
67 - [0] #0 = 2
68 - [1] #1 = StoreLocal Let unknown c$22 = unknown #0
69 - [2] #2 = DeclareLocal Let unknown d$23
70 - [3] #3 = 3
71 - [4] #4 = StoreLocal Reassign unknown d$24 = unknown #3
72 - [5] #5 = LoadLocal unknown x$17
73 - [6] #6 = LoadLocal unknown y$18
74 - [7] #7 = Binary unknown #5 + unknown #6
75 - [8] #8 = LoadLocal unknown a$19
76 - [9] #9 = Binary unknown #7 + unknown #8
77 - [10] #10 = LoadLocal unknown b$20
78 - [11] #11 = Binary unknown #9 + unknown #10
79 - [12] #12 = LoadLocal unknown z$21
80 - [13] #13 = Binary unknown #11 + unknown #12
81 - [14] #14 = 2
82 - [15] #15 = Binary unknown #13 + unknown #14
83 - [16] #16 = 3
84 - [17] #17 = Binary unknown #15 + unknown #16
85 - [18] #18 = <undefined>
86 - [19] Return unknown #18
87 - [18] #20 = StoreLocal Const unknown bar$25 = unknown #19
88 - [19] #21 = LoadLocal unknown bar$25
89 - [20] #22 = LoadGlobal foo
90 - [21] #23 = <undefined>
91 - [22] Return unknown #23
92 - [3] #3 = StoreLocal Const unknown foo$26 = unknown #2
93 - [4] #4 = <undefined>
94 - [5] Return unknown #4
67 + [0] unknown $27 = 2
68 + [1] unknown $29 = StoreLocal Let unknown c$70 = unknown $27
69 + [2] unknown $31 = DeclareLocal Let unknown d$71
70 + [3] unknown $32 = 3
71 + [4] unknown $33 = StoreLocal Reassign unknown d$72 = unknown $32
72 + [5] unknown $34 = LoadLocal unknown x$65
73 + [6] unknown $35 = LoadLocal unknown y$66
74 + [7] unknown $36 = Binary unknown $34 + unknown $35
75 + [8] unknown $37 = LoadLocal unknown a$67
76 + [9] unknown $38 = Binary unknown $36 + unknown $37
77 + [10] unknown $39 = LoadLocal unknown b$68
78 + [11] unknown $40 = Binary unknown $38 + unknown $39
79 + [12] unknown $41 = LoadLocal unknown z$69
80 + [13] unknown $42 = Binary unknown $40 + unknown $41
81 + [14] unknown $43 = 2
82 + [15] unknown $44 = Binary unknown $42 + unknown $43
83 + [16] unknown $45 = 3
84 + [17] unknown $46 = Binary unknown $44 + unknown $45
85 + [18] unknown $47 = <undefined>
86 + [19] Return unknown $47
87 + [18] unknown $50 = StoreLocal Const unknown bar$73 = unknown $48
88 + [19] unknown $51 = LoadLocal unknown bar$73
89 + [20] unknown $52 = LoadGlobal foo
90 + [21] unknown $53 = <undefined>
91 + [22] Return unknown $53
92 + [3] unknown $56 = StoreLocal Const unknown foo$74 = unknown $54
93 + [4] unknown $57 = <undefined>
94 + [5] Return unknown $57
compiler/forget/crates/forget_fixtures/tests/snapshots/fixtures_test__fixtures@identifiers.js.snap
+12 -12
@@ -24,18 +24,18 @@ function id(x) {
24
25 Output:
26 function id(
27 - unknown x$3,
27 + unknown x$14,
28 )
29 entry bb0
30 bb0 (block)
31 - [0] #0 = LoadGlobal Math
32 - [1] #1 = LoadGlobal id
33 - [2] #2 = true
34 - [3] #3 = StoreLocal Let unknown y$4 = unknown #2
35 - [4] #4 = false
36 - [5] #5 = StoreLocal Reassign unknown y$5 = unknown #4
37 - [6] #6 = false
38 - [7] #7 = DeclareLocal Let unknown z$6
39 - [8] #8 = LoadLocal unknown z$6
40 - [9] #9 = LoadLocal unknown x$3
41 - [10] Return unknown #9
31 + [0] unknown $1 = LoadGlobal Math
32 + [1] unknown $2 = LoadGlobal id
33 + [2] unknown $3 = true
34 + [3] unknown $5 = StoreLocal Let unknown y$15 = unknown $3
35 + [4] unknown $6 = false
36 + [5] unknown $7 = StoreLocal Reassign unknown y$16 = unknown $6
37 + [6] unknown $8 = false
38 + [7] unknown $10 = DeclareLocal Let unknown z$17
39 + [8] unknown $11 = LoadLocal unknown z$17
40 + [9] unknown $12 = LoadLocal unknown x$14
41 + [10] Return unknown $12
compiler/forget/crates/forget_fixtures/tests/snapshots/fixtures_test__fixtures@if-statement.js.snap
+12 -12
@@ -16,25 +16,25 @@ function foo(a, b, c, d) {
16
17 Output:
18 function foo(
19 - unknown a$4,
20 - unknown b$5,
21 - unknown c$6,
22 - unknown d$7,
19 + unknown a$9,
20 + unknown b$10,
21 + unknown c$11,
22 + unknown d$12,
23 )
24 entry bb0
25 bb0 (block)
26 - [0] #2 = LoadLocal unknown a$4
27 - [1] If unknown #2 consequent=bb2 alternate=bb4 fallthrough=bb1
26 + [0] unknown $6 = LoadLocal unknown a$9
27 + [1] If unknown $6 consequent=bb2 alternate=bb4 fallthrough=bb1
28 bb2 (block)
29 predecessors: bb0
30 - [2] #0 = LoadLocal unknown b$5
31 - [3] Return unknown #0
30 + [2] unknown $4 = LoadLocal unknown b$10
31 + [3] Return unknown $4
32 bb4 (block)
33 predecessors: bb0
34 - [4] #1 = LoadLocal unknown c$6
34 + [4] unknown $5 = LoadLocal unknown c$11
35 [5] Goto bb1
36 bb1 (block)
37 predecessors: bb4
38 - [6] #3 = LoadLocal unknown d$7
39 - [7] #4 = <undefined>
40 - [8] Return unknown #4
38 + [6] unknown $7 = LoadLocal unknown d$12
39 + [7] unknown $8 = <undefined>
40 + [8] Return unknown $8
compiler/forget/crates/forget_fixtures/tests/snapshots/fixtures_test__fixtures@simple-function.js.snap
+20 -20
@@ -17,30 +17,30 @@ function Component(a) {
17
18 Output:
19 function Component(
20 - unknown a$5,
20 + unknown a$23,
21 )
22 entry bb0
23 bb0 (block)
24 - [0] #0 = LoadGlobal Math
25 - [1] #1 = 0
26 - [2] #2 = StoreLocal Let unknown b$6 = unknown #1
27 - [3] #3 = Function @deps[] @context[unknown a$5, unknown b$6]:
24 + [0] unknown $1 = LoadGlobal Math
25 + [1] unknown $2 = 0
26 + [2] unknown $4 = StoreLocal Let unknown b$24 = unknown $2
27 + [3] unknown $17 = Function @deps[] @context[unknown a$23, unknown b$24]:
28 function foo_(
29 - unknown c$7,
29 + unknown c$25,
30 )
31 entry bb1
32 bb1 (block)
33 - [0] #0 = 1
34 - [1] #1 = StoreLocal Let unknown d$8 = unknown #0
35 - [2] #2 = LoadLocal unknown a$5
36 - [3] #3 = 0
37 - [4] #4 = Binary unknown #2 + unknown #3
38 - [5] #5 = LoadLocal unknown c$7
39 - [6] #6 = Binary unknown #4 + unknown #5
40 - [7] #7 = 1
41 - [8] #8 = Binary unknown #6 + unknown #7
42 - [9] Return unknown #8
43 - [4] #4 = StoreLocal Const unknown foo$9 = unknown #3
44 - [5] #5 = LoadLocal unknown foo$9
45 - [6] #6 = Call unknown #5()
46 - [7] Return unknown #6
33 + [0] unknown $6 = 1
34 + [1] unknown $8 = StoreLocal Let unknown d$26 = unknown $6
35 + [2] unknown $9 = LoadLocal unknown a$23
36 + [3] unknown $10 = 0
37 + [4] unknown $11 = Binary unknown $9 + unknown $10
38 + [5] unknown $12 = LoadLocal unknown c$25
39 + [6] unknown $13 = Binary unknown $11 + unknown $12
40 + [7] unknown $14 = 1
41 + [8] unknown $15 = Binary unknown $13 + unknown $14
42 + [9] Return unknown $15
43 + [4] unknown $19 = StoreLocal Const unknown foo$27 = unknown $17
44 + [5] unknown $20 = LoadLocal unknown foo$27
45 + [6] unknown $21 = Call unknown $20()
46 + [7] Return unknown $21
compiler/forget/crates/forget_fixtures/tests/snapshots/fixtures_test__fixtures@simple.js.snap
+10 -10
@@ -15,13 +15,13 @@ function test(
15 )
16 entry bb0
17 bb0 (block)
18 - [0] #0 = true
19 - [1] #1 = false
20 - [2] #2 = null
21 - [3] #3 = 1
22 - [4] #4 = 3.14
23 - [5] #5 = "hello world!"
24 - [6] #6 = Array [unknown #5]
25 - [7] #7 = Array [unknown #0, unknown #1, unknown #2, unknown #3, unknown #4, ...unknown #6]
26 - [8] #8 = 2
27 - [9] Return unknown #8
18 + [0] unknown $0 = true
19 + [1] unknown $1 = false
20 + [2] unknown $2 = null
21 + [3] unknown $3 = 1
22 + [4] unknown $4 = 3.14
23 + [5] unknown $5 = "hello world!"
24 + [6] unknown $6 = Array [unknown $5]
25 + [7] unknown $7 = Array [unknown $0, unknown $1, unknown $2, unknown $3, unknown $4, ...unknown $6]
26 + [8] unknown $8 = 2
27 + [9] Return unknown $8
compiler/forget/crates/forget_fixtures/tests/snapshots/fixtures_test__fixtures@ssa-reassign-if.js.snap
+28 -28
@@ -24,50 +24,50 @@ function Component(a, b) {
24
25 Output:
26 function Component(
27 - unknown a$5,
28 - unknown b$6,
27 + unknown a$26,
28 + unknown b$27,
29 )
30 entry bb0
31 bb0 (block)
32 - [0] #0 = DeclareLocal Let unknown x$7
33 - [1] #1 = 0
34 - [2] #2 = StoreLocal Let unknown y$8 = unknown #1
35 - [3] #3 = 10
36 - [4] #4 = StoreLocal Let unknown z$9 = unknown #3
37 - [5] #14 = LoadLocal unknown a$5
38 - [6] If unknown #14 consequent=bb2 alternate=bb6 fallthrough=bb1
32 + [0] unknown $3 = DeclareLocal Let unknown x$28
33 + [1] unknown $4 = 0
34 + [2] unknown $6 = StoreLocal Let unknown y$29 = unknown $4
35 + [3] unknown $7 = 10
36 + [4] unknown $9 = StoreLocal Let unknown z$30 = unknown $7
37 + [5] unknown $19 = LoadLocal unknown a$26
38 + [6] If unknown $19 consequent=bb2 alternate=bb6 fallthrough=bb1
39 bb2 (block)
40 predecessors: bb0
41 - [7] #5 = 1
42 - [8] #6 = StoreLocal Reassign unknown x$10 = unknown #5
43 - [9] #11 = LoadLocal unknown b$6
44 - [10] If unknown #11 consequent=bb4 alternate=bb5 fallthrough=bb3
41 + [7] unknown $10 = 1
42 + [8] unknown $11 = StoreLocal Reassign unknown x$31 = unknown $10
43 + [9] unknown $16 = LoadLocal unknown b$27
44 + [10] If unknown $16 consequent=bb4 alternate=bb5 fallthrough=bb3
45 bb4 (block)
46 predecessors: bb2
47 - [11] #7 = 20
48 - [12] #8 = StoreLocal Reassign unknown z$11 = unknown #7
47 + [11] unknown $12 = 20
48 + [12] unknown $13 = StoreLocal Reassign unknown z$32 = unknown $12
49 [13] Goto bb3
50 bb5 (block)
51 predecessors: bb2
52 - [14] #9 = 30
53 - [15] #10 = StoreLocal Reassign unknown z$12 = unknown #9
52 + [14] unknown $14 = 30
53 + [15] unknown $15 = StoreLocal Reassign unknown z$33 = unknown $14
54 [16] Goto bb3
55 bb3 (block)
56 predecessors: bb4, bb5
57 - z$19: phi(bb4: z$11, bb5: z$12)
57 + z$40: phi(bb4: z$32, bb5: z$33)
58 [17] Goto bb1
59 bb6 (block)
60 predecessors: bb0
61 - [18] #12 = 2
62 - [19] #13 = StoreLocal Reassign unknown x$13 = unknown #12
61 + [18] unknown $17 = 2
62 + [19] unknown $18 = StoreLocal Reassign unknown x$34 = unknown $17
63 [20] Goto bb1
64 bb1 (block)
65 predecessors: bb3, bb6
66 - x$14: phi(bb3: x$10, bb6: x$13)
67 - z$18: phi(bb3: z$19, bb6: z$9)
68 - [21] #15 = LoadLocal unknown x$14
69 - [22] #16 = 0
70 - [23] #17 = Binary unknown #15 + unknown #16
71 - [24] #18 = LoadLocal unknown z$18
72 - [25] #19 = Binary unknown #17 + unknown #18
73 - [26] Return unknown #19
66 + x$35: phi(bb3: x$31, bb6: x$34)
67 + z$39: phi(bb3: z$40, bb6: z$30)
68 + [21] unknown $20 = LoadLocal unknown x$35
69 + [22] unknown $21 = 0
70 + [23] unknown $22 = Binary unknown $20 + unknown $21
71 + [24] unknown $23 = LoadLocal unknown z$39
72 + [25] unknown $24 = Binary unknown $22 + unknown $23
73 + [26] Return unknown $24
compiler/forget/crates/forget_fixtures/tests/snapshots/fixtures_test__fixtures@use-memo.js.snap
+9 -9
@@ -14,21 +14,21 @@ function Component(x) {
14
15 Output:
16 function Component(
17 - unknown x$2,
17 + unknown x$10,
18 )
19 entry bb0
20 bb0 (block)
21 - [0] #0 = LoadGlobal useMemo
22 - [1] #6 = DeclareLocal Let unknown t$4
21 + [0] unknown $1 = LoadGlobal useMemo
22 + [1] unknown $14 = DeclareLocal Let unknown t$12
23 [2] Label block=bb1 fallthrough=bb6
24 bb1 (block)
25 predecessors: bb0
26 - [3] #7 = LoadGlobal y
27 - [4] #9 = StoreLocal Reassign unknown t$4 = unknown #7
26 + [3] unknown $2 = LoadGlobal y
27 + [4] unknown $13 = StoreLocal Reassign unknown t$12 = unknown $2
28 [5] Goto bb6
29 bb6 (block)
30 predecessors: bb1
31 - [6] #2 = LoadLocal unknown t$4
32 - [7] #3 = StoreLocal Const unknown x$3 = unknown #2
33 - [8] #4 = LoadLocal unknown x$3
34 - [9] Return unknown #4
31 + [6] unknown $5 = LoadLocal unknown t$12
32 + [7] unknown $7 = StoreLocal Const unknown x$11 = unknown $5
33 + [8] unknown $8 = LoadLocal unknown x$11
34 + [9] Return unknown $8
compiler/forget/crates/forget_hir/src/environment.rs
+14 -1
@@ -6,7 +6,8 @@ use forget_estree::ESTreeNode;
6 use forget_semantic_analysis::{DeclarationId, ScopeManager, ScopeView};
7
8 use crate::{
9 - BlockId, Features, Identifier, IdentifierData, IdentifierId, Registry, Type, TypeVarId,
9 + BlockId, Features, Identifier, IdentifierData, IdentifierId, MutableRange, Registry, Type,
10 + TypeVarId,
11 };
12
13 /// Stores all the contextual information about the top-level React function being
@@ -111,4 +112,16 @@ impl Environment {
112 pub fn scope<T: ESTreeNode>(&self, node: &T) -> Option<ScopeView<'_>> {
113 self.analysis.node_scope_view(node)
114 }
115 +
116 + pub fn new_temporary(&self) -> Identifier {
117 + Identifier {
118 + id: self.next_identifier_id(),
119 + name: None,
120 + data: Rc::new(RefCell::new(IdentifierData {
121 + mutable_range: MutableRange::new(),
122 + scope: None,
123 + type_: Type::Var(self.next_type_var_id()),
124 + })),
125 + }
126 + }
127 }
compiler/forget/crates/forget_hir/src/function.rs
+1 -7
@@ -34,19 +34,13 @@ pub struct HIR {
34 impl HIR {
35 pub fn inline(&mut self, other: FunctionExpression) -> () {
36 let offset = self.instructions.len();
37 - for mut instr in other.lowered_function.body.instructions.into_iter() {
38 - instr.each_operand(|operand| {
39 - operand.ix = InstrIx::new((offset + usize::from(operand.ix)) as u32);
40 - });
37 + for instr in other.lowered_function.body.instructions.into_iter() {
38 self.instructions.push(instr);
39 }
40 for mut block in other.lowered_function.body.blocks.into_iter() {
41 for ix in block.instructions.iter_mut() {
42 *ix = InstrIx::new((offset + usize::from(*ix)) as u32);
43 }
47 - block.terminal.value.each_operand(|operand| {
48 - operand.ix = InstrIx::new((offset + usize::from(operand.ix)) as u32);
49 - });
44 self.blocks.insert(block);
45 }
46 }
compiler/forget/crates/forget_hir/src/inline_use_memo.rs
+25 -19
@@ -1,14 +1,14 @@
1 use std::cell::RefCell;
2 -use std::collections::HashSet;
2 +use std::collections::{HashMap, HashSet};
3 use std::rc::Rc;
4
5 use forget_diagnostics::Diagnostic;
6
7 use crate::{
8 initialize_hir, BasicBlock, BlockRewriter, BlockRewriterAction, DeclareLocal, Environment,
9 - Function, GotoKind, GotoTerminal, Identifier, IdentifierData, IdentifierOperand, InstrIx,
10 - Instruction, InstructionKind, InstructionValue, LValue, LabelTerminal, LoadLocal, MutableRange,
11 - Operand, PlaceOrSpread, ReturnTerminal, StoreLocal, Terminal, TerminalValue, Type,
9 + Function, GotoKind, GotoTerminal, Identifier, IdentifierData, IdentifierId, IdentifierOperand,
10 + InstrIx, Instruction, InstructionKind, InstructionValue, LValue, LabelTerminal, LoadLocal,
11 + MutableRange, PlaceOrSpread, ReturnTerminal, StoreLocal, Terminal, TerminalValue, Type,
12 };
13
14 /// Inlines `useMemo()` calls, rewriting so that the lambda body becomes part of the
@@ -63,8 +63,8 @@ use crate::{
63 /// ```
64 ///
65 pub fn inline_use_memo(env: &Environment, fun: &mut Function) -> Result<(), Diagnostic> {
66 - let mut use_memo_globals: HashSet<InstrIx> = Default::default();
67 - let mut functions: HashSet<InstrIx> = Default::default();
66 + let mut use_memo_globals: HashSet<IdentifierId> = Default::default();
67 + let mut functions: HashMap<IdentifierId, InstrIx> = Default::default();
68
69 let blocks = &mut fun.body.blocks;
70 let instructions = &mut fun.body.instructions;
@@ -78,25 +78,26 @@ pub fn inline_use_memo(env: &Environment, fun: &mut Function) -> Result<(), Diag
78 match &mut instr.value {
79 InstructionValue::LoadGlobal(value) => {
80 if value.name.as_str() == "useMemo" {
81 - use_memo_globals.insert(instr_ix);
81 + use_memo_globals.insert(instr.lvalue.identifier.id);
82 }
83 }
84 InstructionValue::Function(_) => {
85 - functions.insert(instr_ix);
85 + functions.insert(instr.lvalue.identifier.id, instr_ix);
86 }
87 InstructionValue::Call(value) => {
88 - if !use_memo_globals.contains(&value.callee.ix) {
88 + if !use_memo_globals.contains(&value.callee.identifier.id) {
89 continue;
90 }
91 // Skip useMemo calls where the argument is a spread element
92 - let lambda_ix = match &value.arguments.get(0) {
93 - Some(PlaceOrSpread::Place(place)) => place.ix,
92 + let lambda_id = match &value.arguments.get(0) {
93 + Some(PlaceOrSpread::Place(place)) => place.identifier.id,
94 + _ => continue,
95 + };
96 + let lambda_ix = match functions.get(&lambda_id) {
97 + Some(ix) => *ix,
98 + // Skip useMemo calls where the argument is not a function expression
99 _ => continue,
100 };
96 - // Skip useMemo where the argument is not a function expression
97 - if !functions.contains(&lambda_ix) {
98 - continue;
99 - }
101 let instr_id = instr.id;
102
103 // Create a temporary variable to store the useMemo result into
@@ -166,6 +167,10 @@ pub fn inline_use_memo(env: &Environment, fun: &mut Function) -> Result<(), Diag
167 );
168 lambda.lowered_function.body.instructions.push(Instruction {
169 id: instr_id,
170 + lvalue: IdentifierOperand {
171 + identifier: env.new_temporary(),
172 + effect: None,
173 + },
174 value: InstructionValue::StoreLocal(StoreLocal {
175 lvalue: LValue {
176 identifier: IdentifierOperand {
@@ -174,10 +179,7 @@ pub fn inline_use_memo(env: &Environment, fun: &mut Function) -> Result<(), Diag
179 },
180 kind: InstructionKind::Reassign,
181 },
177 - value: Operand {
178 - ix: value.ix,
179 - effect: None,
180 - },
182 + value: value.clone(),
183 }),
184 });
185 block.instructions.push(store_ix);
@@ -211,6 +213,10 @@ pub fn inline_use_memo(env: &Environment, fun: &mut Function) -> Result<(), Diag
213 let declare_ix = InstrIx::new(instructions.len() as u32);
214 instructions.push(Instruction {
215 id: instr_id,
216 + lvalue: IdentifierOperand {
217 + identifier: env.new_temporary(),
218 + effect: None,
219 + },
220 value: InstructionValue::DeclareLocal(DeclareLocal {
221 lvalue: LValue {
222 identifier: IdentifierOperand {
compiler/forget/crates/forget_hir/src/instruction.rs
+29 -21
@@ -4,11 +4,12 @@ use std::rc::Rc;
4
5 use forget_estree::{BinaryOperator, JsValue};
6
7 -use crate::{Function, IdentifierId, InstrIx, InstructionId, ScopeId, Type};
7 +use crate::{Function, IdentifierId, InstructionId, ScopeId, Type};
8
9 #[derive(Debug)]
10 pub struct Instruction {
11 pub id: InstructionId,
12 + pub lvalue: IdentifierOperand,
13 pub value: InstructionValue,
14 }
15
@@ -91,7 +92,7 @@ impl Instruction {
92
93 pub fn each_operand<F>(&mut self, mut f: F) -> ()
94 where
94 - F: FnMut(&mut Operand) -> (),
95 + F: FnMut(&mut IdentifierOperand) -> (),
96 {
97 match &mut self.value {
98 InstructionValue::Array(value) => {
@@ -194,26 +195,26 @@ pub struct Array {
195
196 #[derive(Debug)]
197 pub enum PlaceOrSpread {
197 - Place(Operand),
198 - Spread(Operand),
198 + Place(IdentifierOperand),
199 + Spread(IdentifierOperand),
200 }
201
202 #[derive(Debug)]
203 pub struct Binary {
203 - pub left: Operand,
204 + pub left: IdentifierOperand,
205 pub operator: BinaryOperator,
205 - pub right: Operand,
206 + pub right: IdentifierOperand,
207 }
208
209 #[derive(Debug)]
210 pub struct Call {
210 - pub callee: Operand,
211 + pub callee: IdentifierOperand,
212 pub arguments: Vec<PlaceOrSpread>,
213 }
214
215 #[derive(Debug)]
216 pub struct FunctionExpression {
216 - pub dependencies: Vec<Operand>,
217 + pub dependencies: Vec<IdentifierOperand>,
218 pub lowered_function: Box<Function>,
219 }
220
@@ -229,7 +230,7 @@ pub struct LoadLocal {
230
231 #[derive(Debug)]
232 pub struct LoadContext {
232 - pub place: Operand,
233 + pub place: IdentifierOperand,
234 }
235
236 #[derive(Debug, PartialEq, Eq, Clone)]
@@ -250,26 +251,25 @@ pub struct DeclareContext {
251 #[derive(Debug)]
252 pub struct StoreLocal {
253 pub lvalue: LValue,
253 - pub value: Operand,
254 + pub value: IdentifierOperand,
255 }
256
257 #[derive(Debug)]
258 pub struct JSXElement {
258 - pub tag: Operand,
259 + pub tag: IdentifierOperand,
260 pub props: Vec<JSXAttribute>,
260 - pub children: Option<Vec<Operand>>,
261 + pub children: Option<Vec<IdentifierOperand>>,
262 }
263
264 #[derive(Debug)]
265 pub enum JSXAttribute {
265 - Spread { argument: Operand },
266 - Attribute { name: String, value: Operand },
267 -}
268 -
269 -#[derive(Clone, Debug)]
270 -pub struct Operand {
271 - pub ix: InstrIx,
272 - pub effect: Option<Effect>,
266 + Spread {
267 + argument: IdentifierOperand,
268 + },
269 + Attribute {
270 + name: String,
271 + value: IdentifierOperand,
272 + },
273 }
274
275 #[derive(Clone, Debug)]
@@ -357,10 +357,16 @@ impl Display for Effect {
357
358 #[derive(Clone, Debug)]
359 pub struct Identifier {
360 - /// Uniquely identifiers this identifier
360 + /// Uniquely identifies this identifier
361 pub id: IdentifierId,
362 +
363 + /// The name of the identifier, if this corresponds to a named identifier in the
364 + /// original program. May also be set for generated identifiers that must be
365 + /// emitted as a variable declaration.
366 pub name: Option<String>,
367
368 + /// Shared data, such as the mutable range and scope of the value referred to by
369 + /// the identifier.
370 pub data: Rc<RefCell<IdentifierData>>,
371 }
372
@@ -370,6 +376,8 @@ pub struct IdentifierData {
376
377 pub scope: Option<ReactiveScope>,
378
379 + /// NOTE: consider moving this to `Identifier` to support control-flow specific
380 + /// type information
381 pub type_: Type,
382 }
383
compiler/forget/crates/forget_hir/src/merge_consecutive_blocks.rs
+12 -3
@@ -6,7 +6,7 @@ use thiserror::Error;
6 use crate::{
7 mark_instruction_ids, mark_predecessors, BasicBlock, BlockId, BlockKind, BlockRewriter,
8 BlockRewriterAction, Environment, Function, IdentifierOperand, InstrIx, Instruction,
9 - InstructionKind, InstructionValue, LValue, LoadLocal, Operand, StoreLocal, TerminalValue,
9 + InstructionKind, InstructionValue, LValue, LoadLocal, StoreLocal, TerminalValue,
10 };
11
12 /// Merges sequences of blocks that will always execute consecutively —
@@ -59,8 +59,13 @@ pub fn merge_consecutive_blocks(env: &Environment, fun: &mut Function) -> Result
59 })?;
60 let (_, operand) = phi.operands.first().unwrap();
61 // load the operand
62 + let temporary = env.new_temporary();
63 let load = Instruction {
64 id: predecessor.terminal.id,
65 + lvalue: IdentifierOperand {
66 + identifier: temporary.clone(),
67 + effect: None,
68 + },
69 value: InstructionValue::LoadLocal(LoadLocal {
70 place: IdentifierOperand {
71 effect: None,
@@ -74,6 +79,10 @@ pub fn merge_consecutive_blocks(env: &Environment, fun: &mut Function) -> Result
79 // store it into the phi id
80 let store = Instruction {
81 id: predecessor.terminal.id,
82 + lvalue: IdentifierOperand {
83 + identifier: env.new_temporary(),
84 + effect: None,
85 + },
86 value: InstructionValue::StoreLocal(StoreLocal {
87 lvalue: LValue {
88 kind: InstructionKind::Reassign,
@@ -82,9 +91,9 @@ pub fn merge_consecutive_blocks(env: &Environment, fun: &mut Function) -> Result
91 effect: None,
92 },
93 },
85 - value: Operand {
94 + value: IdentifierOperand {
95 + identifier: temporary,
96 effect: None,
87 - ix: load_ix,
97 },
98 }),
99 };
compiler/forget/crates/forget_hir/src/print.rs
+4 -16
@@ -5,7 +5,7 @@ use forget_utils::ensure_sufficient_stack;
5
6 use crate::{
7 BasicBlock, Function, Identifier, IdentifierOperand, Instruction, InstructionValue, LValue,
8 - Operand, Phi, PlaceOrSpread, Terminal, TerminalValue, HIR,
8 + Phi, PlaceOrSpread, Terminal, TerminalValue, HIR,
9 };
10
11 /// Trait for HIR types to describe how they print themselves.
@@ -75,7 +75,9 @@ impl Print for BasicBlock {
75 continue;
76 }
77 let instr = &hir.instructions[usize::from(*ix)];
78 - write!(out, " {} {} = ", instr.id, ix)?;
78 + write!(out, " {} ", instr.id)?;
79 + instr.lvalue.print(hir, out)?;
80 + write!(out, " = ")?;
81 instr.value.print(hir, out)?;
82 writeln!(out, "")?;
83 }
@@ -225,20 +227,6 @@ impl Print for PlaceOrSpread {
227 }
228 }
229
228 -impl Print for Operand {
229 - fn print(&self, _hir: &HIR, out: &mut impl Write) -> Result {
230 - write!(
231 - out,
232 - "{} {}",
233 - match self.effect {
234 - Some(effect) => format!("{}", effect),
235 - None => "unknown".to_string(),
236 - },
237 - self.ix
238 - )
239 - }
240 -}
241 -
230 impl Print for LValue {
231 fn print(&self, hir: &HIR, out: &mut impl Write) -> Result {
232 write!(out, "{} ", self.kind)?;
compiler/forget/crates/forget_hir/src/terminal.rs
+5 -5
@@ -1,4 +1,4 @@
1 -use crate::instruction::Operand;
1 +use crate::instruction::IdentifierOperand;
2 use crate::{BlockId, InstructionId};
3
4 /// Terminals represent statements or expressions that affect control flow,
@@ -87,7 +87,7 @@ impl TerminalValue {
87
88 pub fn each_operand<F>(&mut self, mut f: F) -> ()
89 where
90 - F: FnMut(&mut Operand) -> (),
90 + F: FnMut(&mut IdentifierOperand) -> (),
91 {
92 match self {
93 TerminalValue::Branch(terminal) => f(&mut terminal.test),
@@ -107,7 +107,7 @@ pub struct UnsupportedTerminal {}
107
108 #[derive(Debug)]
109 pub struct BranchTerminal {
110 - pub test: Operand,
110 + pub test: IdentifierOperand,
111 pub consequent: BlockId,
112 pub alternate: BlockId,
113 }
@@ -133,7 +133,7 @@ pub struct DoWhileTerminal {
133
134 #[derive(Debug)]
135 pub struct IfTerminal {
136 - pub test: Operand,
136 + pub test: IdentifierOperand,
137 pub consequent: BlockId,
138 pub alternate: BlockId,
139 pub fallthrough: Option<BlockId>,
@@ -141,7 +141,7 @@ pub struct IfTerminal {
141
142 #[derive(Debug)]
143 pub struct ReturnTerminal {
144 - pub value: Operand,
144 + pub value: IdentifierOperand,
145 }
146
147 #[derive(Debug)]
compiler/forget/crates/forget_optimization/src/constant_propagation.rs
+96 -97
@@ -4,13 +4,12 @@ use forget_diagnostics::Diagnostic;
4 use forget_estree::{BinaryOperator, JsValue};
5 use forget_hir::{
6 initialize_hir, merge_consecutive_blocks, BlockKind, Environment, Function, GotoKind,
7 - GotoTerminal, IdentifierId, Instruction, InstructionValue, LoadGlobal, Operand, Primitive,
8 - TerminalValue,
7 + GotoTerminal, IdentifierId, InstructionValue, LoadGlobal, Primitive, TerminalValue,
8 };
9 use forget_ssa::eliminate_redundant_phis;
10
11 pub fn constant_propagation(env: &Environment, fun: &mut Function) -> Result<(), Diagnostic> {
13 - let mut constants = Constants::new();
12 + let mut constants = Constants::default();
13 constant_propagation_impl(env, fun, &mut constants)
14 }
15
@@ -63,7 +62,7 @@ fn apply_constant_propagation(
62 for phi in block.phis.iter() {
63 let mut value: Option<Constant> = None;
64 for (_, operand) in &phi.operands {
66 - if let Some(operand_value) = constants.get(&operand.id) {
65 + if let Some(operand_value) = constants.get(operand.id) {
66 match &mut value {
67 Some(value) if value == operand_value => {
68 // no-op
@@ -93,21 +92,23 @@ fn apply_constant_propagation(
92 continue;
93 }
94 let instr_ix = usize::from(*instr_ix);
96 - let mut instr = std::mem::replace(
95 + let lvalue_id = fun.body.instructions[instr_ix].lvalue.identifier.id;
96 + let mut value = std::mem::replace(
97 &mut fun.body.instructions[instr_ix].value,
98 InstructionValue::Tombstone,
99 );
100 - evaluate_instruction(env, &fun.body.instructions, &mut instr, constants)?;
101 - fun.body.instructions[instr_ix].value = instr;
100 + let const_value = evaluate_instruction(env, &mut value, constants)?;
101 + if let Some(const_value) = const_value {
102 + constants.insert(lvalue_id, const_value);
103 + }
104 + fun.body.instructions[instr_ix].value = value;
105 }
106
107 // If the block ends in an `if` and the test value is a constant primitive,
108 // then convert the terminal into a goto to either the consequent or alternate
109 // in this case, only the selected branch is reachable
110 if let TerminalValue::If(terminal) = &mut block.terminal.value {
108 - if let Some(primitive) =
109 - read_primitive_instruction(&fun.body.instructions, &terminal.test)
110 - {
111 + if let Some(primitive) = constants.get_primitive(terminal.test.identifier.id) {
112 let target_block_id = if primitive.value.is_truthy() {
113 terminal.consequent
114 } else {
@@ -125,147 +126,145 @@ fn apply_constant_propagation(
126 Ok(has_changes)
127 }
128
128 -fn read_primitive_instruction(instrs: &[Instruction], operand: &Operand) -> Option<Primitive> {
129 - let instr = &instrs[usize::from(operand.ix)].value;
130 - if let InstructionValue::Primitive(primitive) = instr {
131 - Some(primitive.clone())
132 - } else {
133 - None
134 - }
135 -}
136 -
129 fn evaluate_instruction(
130 env: &Environment,
139 - instrs: &[Instruction],
131 mut instr: &mut InstructionValue,
132 constants: &mut Constants,
142 -) -> Result<(), Diagnostic> {
143 - let read_constant = |operand: &Operand| {
144 - let instr = &instrs[usize::from(operand.ix)].value;
145 - match instr {
146 - InstructionValue::Primitive(value) => Some(Constant::Primitive(value.clone())),
147 - InstructionValue::LoadGlobal(value) => Some(Constant::Global(value.clone())),
148 - _ => None,
149 - }
150 - };
133 +) -> Result<Option<Constant>, Diagnostic> {
134 match &mut instr {
135 + InstructionValue::Primitive(value) => Ok(Some(Constant::Primitive(value.clone()))),
136 + InstructionValue::LoadGlobal(value) => Ok(Some(Constant::Global(value.clone()))),
137 InstructionValue::Binary(value) => {
153 - let left = read_primitive_instruction(instrs, &value.left);
154 - let right = read_primitive_instruction(instrs, &value.right);
138 + let left = constants.get_primitive(value.left.identifier.id);
139 + let right = constants.get_primitive(value.right.identifier.id);
140 match (left, right) {
141 (Some(left), Some(right)) => {
157 - if let Some(result) = apply_binary_operator(env, left, value.operator, right) {
158 - *instr = InstructionValue::Primitive(result);
142 + if let Some(result) =
143 + apply_binary_operator(env, &left.value, value.operator, &right.value)
144 + {
145 + *instr = InstructionValue::Primitive(Primitive {
146 + value: result.clone(),
147 + });
148 + Ok(Some(Constant::Primitive(Primitive { value: result })))
149 + } else {
150 + Ok(None)
151 }
152 }
153 _ => {
154 // no-op, not all operands are known
155 + Ok(None)
156 }
157 }
158 }
159 InstructionValue::LoadLocal(value) => {
167 - if let Some(const_value) = constants.get(&value.place.identifier.id) {
160 + if let Some(const_value) = constants.get(value.place.identifier.id) {
161 *instr = const_value.into();
162 + Ok(Some(const_value.clone()))
163 + } else {
164 + Ok(None)
165 }
166 }
167 InstructionValue::StoreLocal(value) => {
172 - if let Some(const_value) = read_constant(&value.value) {
173 - constants.insert(value.lvalue.identifier.identifier.id, const_value);
168 + if let Some(const_value) = constants.get(value.value.identifier.id).cloned() {
169 + constants.insert(value.lvalue.identifier.identifier.id, const_value.clone());
170 + Ok(Some(const_value))
171 + } else {
172 + Ok(None)
173 }
174 }
175 InstructionValue::Function(value) => {
176 // TODO: due to the outer fixpoint iteration this could visit the same
177 // function many times. However we only strictly have to visit the function
178 // again if the context variable's constant values have changed since last
180 - // time.
181 - // Instead, we can:
182 - // - Create a filtered Constants instance that extracts just the values for
183 - // the function (using its context variables list)
184 - // - Track the last such filtered Constants instance we visited the function
185 - // with. Only visit again if the Constants have changed.
179 + // time. Improve this by tracking the inner_constants value with which we
180 + // last visited, and skip visiting if the same
181 let mut inner_constants: Constants = value
182 .lowered_function
183 .context
184 .iter()
185 .filter_map(|id| {
191 - let value = constants.get(&id.identifier.id);
186 + let value = constants.get(id.identifier.id);
187 value.map(|value| (id.identifier.id, value.clone()))
188 })
189 .collect();
190 constant_propagation_impl(env, &mut value.lowered_function, &mut inner_constants)?;
191 + Ok(None)
192 }
193 _ => {
194 // no-op, not all instructions can be processed
195 + Ok(None)
196 }
197 }
201 - Ok(())
198 }
199
200 fn apply_binary_operator(
201 _env: &Environment,
206 - left: Primitive,
202 + left: &JsValue,
203 operator: BinaryOperator,
208 - right: Primitive,
209 -) -> Option<Primitive> {
210 - match (left.value, right.value) {
204 + right: &JsValue,
205 +) -> Option<JsValue> {
206 + match (left, right) {
207 (JsValue::Number(left), JsValue::Number(right)) => match operator {
212 - BinaryOperator::Add => Some(Primitive {
213 - value: JsValue::Number(left + right),
214 - }),
215 - BinaryOperator::Subtract => Some(Primitive {
216 - value: JsValue::Number(left - right),
217 - }),
218 - BinaryOperator::Multiply => Some(Primitive {
219 - value: JsValue::Number(left * right),
220 - }),
221 - BinaryOperator::Divide => Some(Primitive {
222 - value: JsValue::Number(left / right),
223 - }),
224 - BinaryOperator::LessThan => Some(Primitive {
225 - value: JsValue::Boolean(left < right),
226 - }),
227 - BinaryOperator::LessThanOrEqual => Some(Primitive {
228 - value: JsValue::Boolean(left <= right),
229 - }),
230 - BinaryOperator::GreaterThan => Some(Primitive {
231 - value: JsValue::Boolean(left > right),
232 - }),
233 - BinaryOperator::GreaterThanOrEqual => Some(Primitive {
234 - value: JsValue::Boolean(left >= right),
235 - }),
236 - BinaryOperator::Equals => Some(Primitive {
237 - value: JsValue::Boolean(left.equals(right)),
238 - }),
239 - BinaryOperator::NotEquals => Some(Primitive {
240 - value: JsValue::Boolean(left.not_equals(right)),
241 - }),
242 - BinaryOperator::StrictEquals => Some(Primitive {
243 - value: JsValue::Boolean(left.equals(right)),
244 - }),
245 - BinaryOperator::NotStrictEquals => Some(Primitive {
246 - value: JsValue::Boolean(left.not_equals(right)),
247 - }),
208 + BinaryOperator::Add => Some(JsValue::Number(*left + *right)),
209 + BinaryOperator::Subtract => Some(JsValue::Number(*left - *right)),
210 + BinaryOperator::Multiply => Some(JsValue::Number(*left * *right)),
211 + BinaryOperator::Divide => Some(JsValue::Number(*left / *right)),
212 + BinaryOperator::LessThan => Some(JsValue::Boolean(*left < *right)),
213 + BinaryOperator::LessThanOrEqual => Some(JsValue::Boolean(*left <= *right)),
214 + BinaryOperator::GreaterThan => Some(JsValue::Boolean(*left > *right)),
215 + BinaryOperator::GreaterThanOrEqual => Some(JsValue::Boolean(*left >= *right)),
216 + BinaryOperator::Equals => Some(JsValue::Boolean(left.equals(*right))),
217 + BinaryOperator::NotEquals => Some(JsValue::Boolean(left.not_equals(*right))),
218 + BinaryOperator::StrictEquals => Some(JsValue::Boolean(left.equals(*right))),
219 + BinaryOperator::NotStrictEquals => Some(JsValue::Boolean(left.not_equals(*right))),
220 _ => None,
221 },
222 (left, right) => match operator {
251 - BinaryOperator::Equals => left.loosely_equals(&right).map(|value| Primitive {
252 - value: JsValue::Boolean(value),
253 - }),
254 - BinaryOperator::NotEquals => left.not_loosely_equals(&right).map(|value| Primitive {
255 - value: JsValue::Boolean(value),
256 - }),
257 - BinaryOperator::StrictEquals => Some(Primitive {
258 - value: JsValue::Boolean(left.strictly_equals(&right)),
259 - }),
260 - BinaryOperator::NotStrictEquals => Some(Primitive {
261 - value: JsValue::Boolean(left.not_strictly_equals(&right)),
262 - }),
223 + BinaryOperator::Equals => left
224 + .loosely_equals(&right)
225 + .map(|value| JsValue::Boolean(value)),
226 + BinaryOperator::NotEquals => left
227 + .not_loosely_equals(&right)
228 + .map(|value| JsValue::Boolean(value)),
229 + BinaryOperator::StrictEquals => Some(JsValue::Boolean(left.strictly_equals(&right))),
230 + BinaryOperator::NotStrictEquals => {
231 + Some(JsValue::Boolean(left.not_strictly_equals(&right)))
232 + }
233 _ => None,
234 },
235 }
236 }
237
268 -type Constants = HashMap<IdentifierId, Constant>;
238 +#[derive(Default)]
239 +struct Constants {
240 + data: HashMap<IdentifierId, Constant>,
241 +}
242 +
243 +impl Constants {
244 + fn get_primitive(&self, id: IdentifierId) -> Option<&Primitive> {
245 + if let Some(Constant::Primitive(primitive)) = &self.data.get(&id) {
246 + Some(primitive)
247 + } else {
248 + None
249 + }
250 + }
251 +
252 + fn get(&self, id: IdentifierId) -> Option<&Constant> {
253 + self.data.get(&id)
254 + }
255 +
256 + fn insert(&mut self, id: IdentifierId, constant: Constant) {
257 + self.data.insert(id, constant);
258 + }
259 +}
260 +
261 +impl FromIterator<(IdentifierId, Constant)> for Constants {
262 + fn from_iter<T: IntoIterator<Item = (IdentifierId, Constant)>>(iter: T) -> Self {
263 + Self {
264 + data: FromIterator::from_iter(iter),
265 + }
266 + }
267 +}
268
269 #[derive(Debug, Clone, PartialEq, Eq)]
270 enum Constant {