diff --git a/compiler/forget/crates/build-hir/src/build.rs b/compiler/forget/crates/build-hir/src/build.rs index 7e10e94278..0ffa1394f1 100644 --- a/compiler/forget/crates/build-hir/src/build.rs +++ b/compiler/forget/crates/build-hir/src/build.rs @@ -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(()) diff --git a/compiler/forget/crates/build-hir/src/builder.rs b/compiler/forget/crates/build-hir/src/builder.rs index bdd9145e52..0acaa9905f 100644 --- a/compiler/forget/crates/build-hir/src/builder.rs +++ b/compiler/forget/crates/build-hir/src/builder.rs @@ -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(&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(&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 { diff --git a/compiler/forget/crates/fixtures/tests/fixtures/if-statement.js b/compiler/forget/crates/fixtures/tests/fixtures/if-statement.js new file mode 100644 index 0000000000..12e5f19ba3 --- /dev/null +++ b/compiler/forget/crates/fixtures/tests/fixtures/if-statement.js @@ -0,0 +1,8 @@ +function foo(a, b, c, d) { + if (a) { + return b; + } else { + c; + } + d; +} diff --git a/compiler/forget/crates/fixtures/tests/snapshots/fixtures_test__fixtures@if-statement.js.snap b/compiler/forget/crates/fixtures/tests/snapshots/fixtures_test__fixtures@if-statement.js.snap new file mode 100644 index 0000000000..1c938e8272 --- /dev/null +++ b/compiler/forget/crates/fixtures/tests/snapshots/fixtures_test__fixtures@if-statement.js.snap @@ -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 = + [8] Return unknown $8 + diff --git a/compiler/forget/crates/hir/src/print.rs b/compiler/forget/crates/hir/src/print.rs index cd0d96a178..dbfd46b244 100644 --- a/compiler/forget/crates/hir/src/print.rs +++ b/compiler/forget/crates/hir/src/print.rs @@ -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 => "".to_string(), + } + )?; + } _ => write!(out, "{:?}", self)?, } Ok(())