[rust] IfStatement support

Lowering for IfStatements, which includes porting some of the HIRBuilder helpers 
like enter() and reserve().
This commit is contained in:
Joe Savona
2023-07-06 09:24:52 +09:00
parent c59ac387ae
commit 357e9de19b
5 changed files with 148 additions and 4 deletions
+41 -2
View File
@@ -1,7 +1,7 @@
use bumpalo::collections::{CollectIn, String};
use estree::{
AssignmentTarget, ExpressionLike, FunctionDeclaration, Literal, LiteralValue, Pattern,
Statement, VariableDeclarationKind,
AssignmentTarget, ExpressionLike, FunctionDeclaration, IfStatement, Literal, LiteralValue,
Pattern, Statement, VariableDeclarationKind,
};
use hir::{
ArrayElement, BlockKind, Environment, Function, GotoKind, Identifier, InstructionKind,
@@ -144,6 +144,45 @@ fn lower_statement<'a>(
}
}
}
Statement::IfStatement(stmt) => {
// block for what follows the if statement, though this may
// not be reachable
let fallthrough_block = builder.reserve(BlockKind::Block);
let IfStatement {
test,
consequent,
alternate,
..
} = *stmt;
let consequent_block = builder.enter(BlockKind::Block, |builder| {
lower_statement(env, builder, consequent, None).unwrap();
TerminalValue::GotoTerminal(hir::GotoTerminal {
block: fallthrough_block.id,
kind: GotoKind::Break,
})
});
let alternate_block = builder.enter(BlockKind::Block, |builder| {
if let Some(alternate) = alternate {
lower_statement(env, builder, alternate, None).unwrap();
}
TerminalValue::GotoTerminal(hir::GotoTerminal {
block: fallthrough_block.id,
kind: GotoKind::Break,
})
});
let test = lower_expression_to_temporary(env, builder, test);
let terminal = TerminalValue::IfTerminal(hir::IfTerminal {
test,
consequent: consequent_block,
alternate: alternate_block,
fallthrough: Some(fallthrough_block.id),
});
builder.terminate_with_fallthrough(terminal, fallthrough_block);
}
_ => todo!("Lower {stmt:#?}"),
}
Ok(())
@@ -96,7 +96,15 @@ impl<'a> Builder<'a> {
kind: next_kind,
instructions: Vec::new_in(&self.environment.allocator),
};
let prev_wip = std::mem::replace(&mut self.wip, next_wip);
self.terminate_with_fallthrough(terminal, next_wip)
}
pub(crate) fn terminate_with_fallthrough(
&mut self,
terminal: TerminalValue<'a>,
fallthrough: WipBlock<'a>,
) {
let prev_wip = std::mem::replace(&mut self.wip, fallthrough);
self.completed.insert(
prev_wip.id,
BasicBlock {
@@ -112,6 +120,46 @@ impl<'a> Builder<'a> {
);
}
pub(crate) fn reserve(&mut self, kind: BlockKind) -> WipBlock<'a> {
WipBlock {
id: self.environment.next_block_id(),
kind,
instructions: Vec::new_in(&self.environment.allocator),
}
}
pub(crate) fn enter<F>(&mut self, kind: BlockKind, f: F) -> BlockId
where
F: FnOnce(&mut Self) -> TerminalValue<'a>,
{
let wip = self.reserve(kind);
let id = wip.id;
self.enter_reserved(wip, f);
id
}
fn enter_reserved<F>(&mut self, wip: WipBlock<'a>, f: F)
where
F: FnOnce(&mut Self) -> TerminalValue<'a>,
{
let current = std::mem::replace(&mut self.wip, wip);
let terminal = f(self);
let completed = std::mem::replace(&mut self.wip, current);
self.completed.insert(
completed.id,
BasicBlock {
id: completed.id,
kind: completed.kind,
instructions: completed.instructions,
terminal: Terminal {
id: self.id_gen.next(),
value: terminal,
},
predecessors: Default::default(),
},
);
}
/// Returns a new temporary identifier
pub(crate) fn make_temporary(&self) -> hir::Identifier<'a> {
hir::Identifier {
@@ -0,0 +1,8 @@
function foo(a, b, c, d) {
if (a) {
return b;
} else {
c;
}
d;
}
@@ -0,0 +1,32 @@
---
source: crates/fixtures/tests/fixtures_test.rs
expression: "format!(\"Input:\\n{input}\\n\\nOutput:\\n{output}\")"
input_file: crates/fixtures/tests/fixtures/if-statement.js
---
Input:
function foo(a, b, c, d) {
if (a) {
return b;
} else {
c;
}
d;
}
Output:
entry bb0
bb0
[0] unknown $5 = LoadLocal unknown a$4
[1] If unknown $5 consequent=bb2 alternate=bb4 fallthrough=bb1
bb2
[2] unknown $1 = LoadLocal unknown b$0
[3] Return unknown $1
bb4
[4] unknown $3 = LoadLocal unknown c$2
[5] Goto bb1
bb1
[6] unknown $7 = LoadLocal unknown d$6
[7] unknown $8 = <undefined>
[8] Return unknown $8
+18 -1
View File
@@ -1,7 +1,7 @@
use std::fmt::{Result, Write};
use crate::{
ArrayElement, BasicBlock, Function, Instruction, InstructionValue, LValue, Place,
terminal, ArrayElement, BasicBlock, Function, Instruction, InstructionValue, LValue, Place,
PrimitiveValue, Terminal, TerminalValue,
};
@@ -150,6 +150,23 @@ impl<'a> Print for TerminalValue<'a> {
write!(out, "Return ")?;
terminal.value.print(out)?;
}
TerminalValue::GotoTerminal(terminal) => {
write!(out, "Goto {}", terminal.block)?;
}
TerminalValue::IfTerminal(terminal) => {
write!(out, "If ")?;
terminal.test.print(out)?;
write!(
out,
" consequent={} alternate={} fallthrough={}",
terminal.consequent,
terminal.alternate,
match terminal.fallthrough {
Some(fallthrough) => format!("{fallthrough}"),
None => "<none>".to_string(),
}
)?;
}
_ => write!(out, "{:?}", self)?,
}
Ok(())