@samitouri / QOS-React-2 / commits / 357e9de19b

[rust] IfStatement support

Lowering for IfStatements, which includes porting some of the HIRBuilder helpers like enter() and reserve().

Joe Savona committed Jul 6, 2023 at 09:24 UTC 357e9de19b891d4424a6d2888c8a849d7e2c3b92
5 files changed +148 -4
compiler/forget/crates/build-hir/src/build.rs
+41 -2
@@ -1,7 +1,7 @@
1 use bumpalo::collections::{CollectIn, String};
2 use estree::{
3 - AssignmentTarget, ExpressionLike, FunctionDeclaration, Literal, LiteralValue, Pattern,
4 - Statement, VariableDeclarationKind,
3 + AssignmentTarget, ExpressionLike, FunctionDeclaration, IfStatement, Literal, LiteralValue,
4 + Pattern, Statement, VariableDeclarationKind,
5 };
6 use hir::{
7 ArrayElement, BlockKind, Environment, Function, GotoKind, Identifier, InstructionKind,
@@ -144,6 +144,45 @@ fn lower_statement<'a>(
144 }
145 }
146 }
147 + Statement::IfStatement(stmt) => {
148 + // block for what follows the if statement, though this may
149 + // not be reachable
150 + let fallthrough_block = builder.reserve(BlockKind::Block);
151 +
152 + let IfStatement {
153 + test,
154 + consequent,
155 + alternate,
156 + ..
157 + } = *stmt;
158 +
159 + let consequent_block = builder.enter(BlockKind::Block, |builder| {
160 + lower_statement(env, builder, consequent, None).unwrap();
161 + TerminalValue::GotoTerminal(hir::GotoTerminal {
162 + block: fallthrough_block.id,
163 + kind: GotoKind::Break,
164 + })
165 + });
166 +
167 + let alternate_block = builder.enter(BlockKind::Block, |builder| {
168 + if let Some(alternate) = alternate {
169 + lower_statement(env, builder, alternate, None).unwrap();
170 + }
171 + TerminalValue::GotoTerminal(hir::GotoTerminal {
172 + block: fallthrough_block.id,
173 + kind: GotoKind::Break,
174 + })
175 + });
176 +
177 + let test = lower_expression_to_temporary(env, builder, test);
178 + let terminal = TerminalValue::IfTerminal(hir::IfTerminal {
179 + test,
180 + consequent: consequent_block,
181 + alternate: alternate_block,
182 + fallthrough: Some(fallthrough_block.id),
183 + });
184 + builder.terminate_with_fallthrough(terminal, fallthrough_block);
185 + }
186 _ => todo!("Lower {stmt:#?}"),
187 }
188 Ok(())
compiler/forget/crates/build-hir/src/builder.rs
+49 -1
@@ -96,7 +96,15 @@ impl<'a> Builder<'a> {
96 kind: next_kind,
97 instructions: Vec::new_in(&self.environment.allocator),
98 };
99 - let prev_wip = std::mem::replace(&mut self.wip, next_wip);
99 + self.terminate_with_fallthrough(terminal, next_wip)
100 + }
101 +
102 + pub(crate) fn terminate_with_fallthrough(
103 + &mut self,
104 + terminal: TerminalValue<'a>,
105 + fallthrough: WipBlock<'a>,
106 + ) {
107 + let prev_wip = std::mem::replace(&mut self.wip, fallthrough);
108 self.completed.insert(
109 prev_wip.id,
110 BasicBlock {
@@ -112,6 +120,46 @@ impl<'a> Builder<'a> {
120 );
121 }
122
123 + pub(crate) fn reserve(&mut self, kind: BlockKind) -> WipBlock<'a> {
124 + WipBlock {
125 + id: self.environment.next_block_id(),
126 + kind,
127 + instructions: Vec::new_in(&self.environment.allocator),
128 + }
129 + }
130 +
131 + pub(crate) fn enter<F>(&mut self, kind: BlockKind, f: F) -> BlockId
132 + where
133 + F: FnOnce(&mut Self) -> TerminalValue<'a>,
134 + {
135 + let wip = self.reserve(kind);
136 + let id = wip.id;
137 + self.enter_reserved(wip, f);
138 + id
139 + }
140 +
141 + fn enter_reserved<F>(&mut self, wip: WipBlock<'a>, f: F)
142 + where
143 + F: FnOnce(&mut Self) -> TerminalValue<'a>,
144 + {
145 + let current = std::mem::replace(&mut self.wip, wip);
146 + let terminal = f(self);
147 + let completed = std::mem::replace(&mut self.wip, current);
148 + self.completed.insert(
149 + completed.id,
150 + BasicBlock {
151 + id: completed.id,
152 + kind: completed.kind,
153 + instructions: completed.instructions,
154 + terminal: Terminal {
155 + id: self.id_gen.next(),
156 + value: terminal,
157 + },
158 + predecessors: Default::default(),
159 + },
160 + );
161 + }
162 +
163 /// Returns a new temporary identifier
164 pub(crate) fn make_temporary(&self) -> hir::Identifier<'a> {
165 hir::Identifier {
compiler/forget/crates/fixtures/tests/fixtures/if-statement.js new
+8
@@ -0,0 +1,8 @@
1 +function foo(a, b, c, d) {
2 + if (a) {
3 + return b;
4 + } else {
5 + c;
6 + }
7 + d;
8 +}
compiler/forget/crates/fixtures/tests/snapshots/fixtures_test__fixtures@if-statement.js.snap new
+32
@@ -0,0 +1,32 @@
1 +---
2 +source: crates/fixtures/tests/fixtures_test.rs
3 +expression: "format!(\"Input:\\n{input}\\n\\nOutput:\\n{output}\")"
4 +input_file: crates/fixtures/tests/fixtures/if-statement.js
5 +---
6 +Input:
7 +function foo(a, b, c, d) {
8 + if (a) {
9 + return b;
10 + } else {
11 + c;
12 + }
13 + d;
14 +}
15 +
16 +
17 +Output:
18 +entry bb0
19 +bb0
20 + [0] unknown $5 = LoadLocal unknown a$4
21 + [1] If unknown $5 consequent=bb2 alternate=bb4 fallthrough=bb1
22 +bb2
23 + [2] unknown $1 = LoadLocal unknown b$0
24 + [3] Return unknown $1
25 +bb4
26 + [4] unknown $3 = LoadLocal unknown c$2
27 + [5] Goto bb1
28 +bb1
29 + [6] unknown $7 = LoadLocal unknown d$6
30 + [7] unknown $8 = <undefined>
31 + [8] Return unknown $8
32 +
compiler/forget/crates/hir/src/print.rs
+18 -1
@@ -1,7 +1,7 @@
1 use std::fmt::{Result, Write};
2
3 use crate::{
4 - ArrayElement, BasicBlock, Function, Instruction, InstructionValue, LValue, Place,
4 + terminal, ArrayElement, BasicBlock, Function, Instruction, InstructionValue, LValue, Place,
5 PrimitiveValue, Terminal, TerminalValue,
6 };
7
@@ -150,6 +150,23 @@ impl<'a> Print for TerminalValue<'a> {
150 write!(out, "Return ")?;
151 terminal.value.print(out)?;
152 }
153 + TerminalValue::GotoTerminal(terminal) => {
154 + write!(out, "Goto {}", terminal.block)?;
155 + }
156 + TerminalValue::IfTerminal(terminal) => {
157 + write!(out, "If ")?;
158 + terminal.test.print(out)?;
159 + write!(
160 + out,
161 + " consequent={} alternate={} fallthrough={}",
162 + terminal.consequent,
163 + terminal.alternate,
164 + match terminal.fallthrough {
165 + Some(fallthrough) => format!("{fallthrough}"),
166 + None => "<none>".to_string(),
167 + }
168 + )?;
169 + }
170 _ => write!(out, "{:?}", self)?,
171 }
172 Ok(())