From 345e7c441160ec0a94cc0909e90bfba9b6fcdce9 Mon Sep 17 00:00:00 2001 From: "Guillem L. Jara" <4lon3ly0@tutanota.com> Date: Sun, 24 May 2026 01:04:06 +0200 Subject: [PATCH] interpreter: add conditional branches, etc. --- interpreter/src/ir.rs | 62 +++++++++------------ interpreter/src/ir/lower.rs | 104 ++++++++++++++++++++++++++++-------- interpreter/src/vm.rs | 17 +++++- src/main.rs | 8 ++- 4 files changed, 124 insertions(+), 67 deletions(-) diff --git a/interpreter/src/ir.rs b/interpreter/src/ir.rs index b594c16..53abc88 100644 --- a/interpreter/src/ir.rs +++ b/interpreter/src/ir.rs @@ -26,7 +26,7 @@ pub struct Reg(pub u16); #[derive(Clone, Copy, Debug)] #[repr(transparent)] -pub struct Label(u16); +pub struct Label(pub u16); #[derive(Clone, Copy, Debug)] #[repr(transparent)] @@ -73,7 +73,6 @@ pub enum OpCode { Jump, Return, Branch, - BrIf, } const _: () = const { assert!(size_of::() <= 8) }; @@ -95,7 +94,6 @@ pub union Arguments { jump: JumpArg, ret: RetArg, branch: BranchArg, - br_if: BrIfArg, call: CallArgs, ind_call: IndCallArgs, } @@ -106,7 +104,6 @@ pub type LoadStoreArg = (Reg, NonLocal); pub type JumpArg = Label; pub type RetArg = Reg; pub type BranchArg = (Reg, Label, Label); -pub type BrIfArg = (Reg, Label); pub type CallArgs = (Reg, NonLocal, ArgCount); pub type IndCallArgs = (Reg, Reg, ArgCount); @@ -159,21 +156,17 @@ impl Instruction { } } - fn jump(opcode: impl Into, to: Label) -> Self { - let opcode = opcode.into(); - debug_assert!(opcode.is_jump()); + fn jump(to: Label) -> Self { Self { - opcode, + opcode: OpCode::Jump, args: Arguments { jump: to }, hint: Hint::None, } } - fn branch(opcode: impl Into, cond: Reg, true_to: Label, false_to: Label) -> Self { - let opcode = opcode.into(); - debug_assert!(opcode.is_branch()); + fn branch(cond: Reg, true_to: Label, false_to: Label) -> Self { Self { - opcode, + opcode: OpCode::Branch, args: Arguments { branch: (cond, true_to, false_to), }, @@ -181,32 +174,32 @@ impl Instruction { } } - fn br_if(opcode: impl Into, cond: Reg, to: Label) -> Self { - let opcode = opcode.into(); - debug_assert!(opcode.is_branch_if()); - Self { - opcode, - args: Arguments { br_if: (cond, to) }, - hint: Hint::None, - } - } - pub fn get_unary(&self) -> Option<&UnaryArg> { self.opcode .is_unary() - .then(|| unsafe { &self.args.unary_local }) + .then_some(unsafe { &self.args.unary_local }) } pub fn get_binary(&self) -> Option<&BinaryArg> { self.opcode .is_binary() - .then(|| unsafe { &self.args.binary_local }) + .then_some(unsafe { &self.args.binary_local }) } pub fn get_load_store(&self) -> Option<&LoadStoreArg> { self.opcode .is_load_store() - .then(|| unsafe { &self.args.load_store }) + .then_some(unsafe { &self.args.load_store }) + } + + pub fn get_branch(&self) -> Option<&BranchArg> { + self.opcode + .is_branch() + .then_some(unsafe { &self.args.branch }) + } + + pub fn get_jump(&self) -> Option<&JumpArg> { + self.opcode.is_jump().then_some(unsafe { &self.args.jump }) } } @@ -258,10 +251,6 @@ impl OpCode { fn is_branch(self) -> bool { matches!(self, Self::Branch) } - - fn is_branch_if(self) -> bool { - matches!(self, Self::BrIf) - } } #[repr(u8, align(1))] @@ -294,9 +283,9 @@ impl Debug for Instruction { let (dest, src) = unsafe { &self.args.load_store }; write!(f, "({dest:?}, {src:?})") } - OpCode::BrIf => { - let (cond, label) = unsafe { self.args.br_if }; - write!(f, "({cond:?}, {label:?})") + OpCode::Branch => { + let (cond, label_then, label_else) = unsafe { self.args.branch }; + write!(f, "({cond:?}, {label_then:?}, {label_else:?})") } OpCode::Jump => { let label = unsafe { self.args.jump }; @@ -350,9 +339,9 @@ impl Display for Instruction { let (dest, src) = unsafe { &self.args.load_store }; write!(f, "{dest} <- {op} intrinsic[{src}]") } - op @ OpCode::BrIf => { - let (cond, label) = unsafe { self.args.br_if }; - write!(f, "{op} {cond}, {label}") + op @ OpCode::Branch => { + let (cond, label_then, label_else) = unsafe { self.args.branch }; + write!(f, "{op} {cond}, {label_then}, {label_else}") } op @ OpCode::Jump => { let label = unsafe { self.args.jump }; @@ -405,8 +394,7 @@ impl Display for OpCode { Self::IndirectCall => "vcall", Self::Jump => "jmp", Self::Return => "ret", - Self::Branch => "br", - Self::BrIf => "brif", + Self::Branch => "brif", }; <_ as Display>::fmt(str, f) } diff --git a/interpreter/src/ir/lower.rs b/interpreter/src/ir/lower.rs index d79a7e5..7f476e2 100644 --- a/interpreter/src/ir/lower.rs +++ b/interpreter/src/ir/lower.rs @@ -9,7 +9,10 @@ use std::mem::forget; use bumpalo::{Bump, collections::Vec}; use indexmap::IndexSet; -use parser::{Atom, BinaryOperator, Expr, ExprNode, UnaryOperator, Variable}; +use parser::{ + Atom, BinaryOperator, BinaryPlaceOperator, Body, Expr, ExprNode, Place, SimpleStatement, + Statement, UnaryOperator, Variable, +}; use crate::{ ir::{Hint, HintedReg, Instruction, Label, NonLocal, OpCode, Reg}, @@ -31,11 +34,51 @@ pub struct Code<'arena> { struct LinearReg(Reg, Hint); impl Code<'_> { - fn lower_expr(&mut self, expr: &Expr) -> LinearReg { - let dest = self.alloc_reg(); - let hint = self.lower_expr_into(expr, dest); - LinearReg(dest, hint) + fn lower_body(&mut self, body: &Body) { + for stmnt in &body.0 { + self.lower_statement(stmnt); + } } + + fn lower_statement(&mut self, stmnt: &Statement) { + match stmnt { + Statement::If { + condition, + then_body, + else_body, + } => { + let mut state = RegsState::new(self); + let condition = self.lower_expr(condition); + let label_then = self.following_instr(1); + let if_label = + self.bc + .emit(Instruction::branch(condition.reg(), label_then, Label(0))); + self.free_reg(condition); + self.lower_body(then_body); + self.bc.nth(if_label).args.branch.2 = self.following_instr(1); + + if let Some(else_body) = else_body { + state.reg_pointer += 1; + let end_label = self.bc.emit(Instruction::jump(Label(0))); + state.scope_hwm(self, |c| c.lower_body(else_body)); + self.bc.nth(end_label).args.jump = self.following_instr(0); + } + } + Statement::Simple(SimpleStatement::Expression(expr)) => { + let reg = self.lower_expr(expr); + self.free_reg(reg); + } + _ => todo!(), + } + } + + fn lower_expr(&mut self, expr: &Expr) -> LinearReg { + let mut dest = self.alloc_reg(); + let hint = self.lower_expr_into(expr, dest.reg()); + dest.1 = hint; + dest + } + fn lower_expr_into(&mut self, expr: &Expr, dest: Reg) -> Hint { match expr { Expr::Leaf(atom) => match atom { @@ -69,19 +112,28 @@ impl Code<'_> { self.lower_expr_into(cond, dest); let mut state = RegsState::new(self); - let br_if = self - .bc - .emit(Instruction::br_if(OpCode::BrIf, dest, Label(0))); + let branch = + self.bc + .emit(Instruction::branch(dest, self.following_instr(1), Label(0))); - state = state.scope(self, |c| c.lower_expr_into(false_then, dest)); + state = state.scope(self, |c| c.lower_expr_into(true_then, dest)); - let jump = self.bc.emit(Instruction::jump(OpCode::Jump, Label(0))); - let label = Label(self.bc.len()); + let jump = self.bc.emit(Instruction::jump(Label(0))); + let label = self.following_instr(0); - state.scope_hwm(self, |c| c.lower_expr_into(true_then, dest)); + state.scope_hwm(self, |c| c.lower_expr_into(false_then, dest)); - self.bc.nth(br_if).args.br_if.1 = label; - self.bc.nth(jump).args.jump = Label(self.bc.len()); + self.bc.nth(branch).args.branch.2 = label; + self.bc.nth(jump).args.jump = self.following_instr(0); + } + ExprNode::BinaryPlaceOperation(BinaryPlaceOperator::Assignment, place, expr) => { + self.lower_expr_into(expr, dest); + let Place::Variable(Variable::User(var)) = place else { + todo!() + }; + let var = self.symbols.register_user_var(var, self.arena); + self.bc + .emit(Instruction::load_store(OpCode::StoreUser, dest, var)); } _ => todo!(), }, @@ -89,12 +141,15 @@ impl Code<'_> { Hint::None } - fn alloc_reg(&mut self) -> Reg { - self.free_regs.pop().unwrap_or_else(|| { - let current = self.reg_pointer; - self.reg_pointer += 1; - Reg(current) - }) + fn alloc_reg(&mut self) -> LinearReg { + self.free_regs + .pop() + .map(|r| LinearReg(r, Hint::None)) + .unwrap_or_else(|| { + let current = self.reg_pointer; + self.reg_pointer += 1; + LinearReg(Reg(current), Hint::None) + }) } fn free_reg(&mut self, reg: LinearReg) { @@ -104,6 +159,10 @@ impl Code<'_> { fn register_const(&mut self, value: Value) -> NonLocal { NonLocal(self.consts.insert_full(value).0 as u16) } + + fn following_instr(&self, nth: u16) -> Label { + Label(self.bc.len() + nth) + } } #[derive(Debug, Clone)] @@ -163,7 +222,7 @@ impl RegsState { } } -pub fn test_interpreter(expr: &Expr<'_>) -> impl Display { +pub fn test_interpreter(stmnt: &Body<'_>) -> impl Display { let bump = Bump::with_capacity(16384); let mut c = Code { arena: &bump, @@ -173,8 +232,7 @@ pub fn test_interpreter(expr: &Expr<'_>) -> impl Display { reg_pointer: 0, free_regs: Vec::new_in(&bump), }; - let result = c.lower_expr(expr); - forget(result); + c.lower_body(stmnt); let code = c.to_string(); let mut vm = Interpreter::new(ExecMode::Uu, c); vm.run(); diff --git a/interpreter/src/vm.rs b/interpreter/src/vm.rs index 79354e9..4a4838c 100644 --- a/interpreter/src/vm.rs +++ b/interpreter/src/vm.rs @@ -87,7 +87,7 @@ impl Interpreter<'_> { pub fn run(&mut self) { while let Some(instr) = self.bc.code.get(self.program_counter) { match instr { - ix if let Some(&(dest, src)) = ix.get_unary() => {} + // ix if let Some(&(dest, src)) = ix.get_unary() => {} ix if let Some(&(dest, lhs, rhs)) = ix.get_binary() => { let lhs = self.registers.read(lhs); let rhs = self.registers.read(rhs); @@ -113,7 +113,20 @@ impl Interpreter<'_> { } _ => todo!(), }, - _ => todo!(), + ix if let Some((cond, true_to, false_to)) = ix.get_branch() => { + let label = if self.registers.read(*cond).0 == 0. { + false_to.0 + } else { + true_to.0 + }; + self.program_counter = label as _; + continue; + } + ix if let Some(label) = ix.get_jump() => { + self.program_counter = label.0 as _; + continue; + } + ix => todo!("{ix:?}"), } self.program_counter += 1; } diff --git a/src/main.rs b/src/main.rs index f5b4c94..0fadf68 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,7 +15,7 @@ use bumpalo::Bump; use clap::Parser as _; use color_eyre::Result; use interpreter::test_interpreter; -use parser::{Body, Parser, Rule}; +use parser::{Parser, Rule}; use crate::{ cli::Args, @@ -55,13 +55,11 @@ fn uu_main() -> Result<()> { dbg!(arena.chunk_capacity()); if let Some(Rule { - actions: Some(Body(a)), + actions: Some(body), pattern: _, }) = ast.rules.first() - && let Some(parser::Statement::Simple(parser::SimpleStatement::Expression(expr))) = - a.first() { - let x = test_interpreter(expr); + let x = test_interpreter(body); if let Err(e) = writeln!(io::stdout(), "---\n{x}") && e.kind() != io::ErrorKind::BrokenPipe {