From ebf56167ff0dad464bf4f0e83f6a4341b85754be Mon Sep 17 00:00:00 2001 From: "Guillem L. Jara" <4lon3ly0@tutanota.com> Date: Fri, 22 May 2026 12:01:37 +0200 Subject: [PATCH] interpreter: hinted registers --- interpreter/src/ir.rs | 58 ++++++++++++++++++++++++++----------- interpreter/src/ir/lower.rs | 31 ++++++++++---------- 2 files changed, 57 insertions(+), 32 deletions(-) diff --git a/interpreter/src/ir.rs b/interpreter/src/ir.rs index 33f50da..42676e9 100644 --- a/interpreter/src/ir.rs +++ b/interpreter/src/ir.rs @@ -14,13 +14,10 @@ mod lower; -use std::{ - fmt::{Debug, Display}, - ops::Deref, -}; - pub use lower::test_interpreter; +use std::fmt::{Debug, Display}; + #[derive(Clone, Copy, Debug)] #[repr(transparent)] struct NonLocal(u16); @@ -85,7 +82,7 @@ const _: () = const { assert!(size_of::() <= 8) }; #[repr(C, align(8))] struct Instruction { opcode: OpCode, - // hint: Hint, + hint: Hint, args: Arguments, } @@ -104,30 +101,39 @@ union Arguments { } impl Instruction { - fn unary(opcode: impl Into, dest: Reg, src: &impl Deref) -> Self { + fn unary(opcode: impl Into, dest: Reg, src: &impl HintedReg) -> Self { let opcode = opcode.into(); debug_assert!(opcode.is_unary()); Self { opcode, args: Arguments { - unary_local: (dest, **src), + unary_local: (dest, src.reg()), }, + hint: src.hint(), } } fn binary( opcode: impl Into, dest: Reg, - lhs: &impl Deref, - rhs: &impl Deref, + lhs: &impl HintedReg, + rhs: &impl HintedReg, ) -> Self { let opcode = opcode.into(); debug_assert!(opcode.is_binary()); + let hint = match (lhs.hint(), rhs.hint()) { + // TODO: Remove once we get const folding. + (Hint::UnboxedFloat64, Hint::UnboxedFloat64) => Hint::UnboxedFloat64, + (_, Hint::UnboxedFloat64) => Hint::UnboxedRhsFloat64, + (Hint::UnboxedFloat64, _) => Hint::UnboxedLhsFloat64, + _ => Hint::None, + }; Self { opcode, args: Arguments { - binary_local: (dest, **lhs, **rhs), + binary_local: (dest, lhs.reg(), rhs.reg()), }, + hint, } } @@ -139,6 +145,7 @@ impl Instruction { args: Arguments { load_store: (dest, src), }, + hint: Hint::None, } } @@ -148,6 +155,7 @@ impl Instruction { Self { opcode, args: Arguments { jump: to }, + hint: Hint::None, } } @@ -159,6 +167,7 @@ impl Instruction { args: Arguments { branch: (cond, true_to, false_to), }, + hint: Hint::None, } } @@ -168,6 +177,7 @@ impl Instruction { Self { opcode, args: Arguments { br_if: (cond, to) }, + hint: Hint::None, } } } @@ -219,12 +229,19 @@ impl OpCode { } } -// #[derive(Clone, Copy)] -// #[repr(u8, align(1))] -// enum Hint { -// // Next instruction contains additional data; for use in pipes, etc. -// // Also allows us to move past (2^16 - 1) variables if we want to. -// } +#[repr(u8, align(1))] +#[derive(Clone, Copy, Debug)] +enum Hint { + None = 0, + UnboxedFloat64, + UnboxedLhsFloat64, + UnboxedRhsFloat64, +} + +trait HintedReg { + fn reg(&self) -> Reg; + fn hint(&self) -> Hint; +} impl Debug for Instruction { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -323,6 +340,13 @@ impl Display for Instruction { write!(f, "{op} {label}") } _ => todo!(), + }?; + match self.hint { + Hint::UnboxedFloat64 if self.opcode.is_binary() => write!(f, " @ all_unboxedf64"), + Hint::UnboxedFloat64 => write!(f, " @ all_unboxedf64"), + Hint::UnboxedLhsFloat64 => write!(f, " @ lhs_unboxedf64"), + Hint::UnboxedRhsFloat64 => write!(f, " @ rhs_unboxedf64"), + Hint::None => Ok(()), } } } diff --git a/interpreter/src/ir/lower.rs b/interpreter/src/ir/lower.rs index 67a32a2..a4afce2 100644 --- a/interpreter/src/ir/lower.rs +++ b/interpreter/src/ir/lower.rs @@ -6,15 +6,14 @@ use std::fmt::Display; use std::hash::Hash; use std::mem::forget; -use std::ops::Deref; use indexmap::IndexSet; use parser::{Atom, BinaryOperator, Expr, ExprNode, UnaryOperator}; -use crate::ir::{Instruction, Label, NonLocal, OpCode, Reg}; +use crate::ir::{Hint, HintedReg, Instruction, Label, NonLocal, OpCode, Reg}; #[derive(Clone, Copy, Debug, PartialEq, PartialOrd)] -struct Value(f64); // TODO: use NaN-boxing. +pub struct Value(f64); // TODO: use NaN-boxing. #[derive(Debug)] struct Code { @@ -26,16 +25,15 @@ struct Code { #[must_use] #[derive(Debug)] -#[repr(transparent)] -struct LinearReg(Reg); +struct LinearReg(Reg, Hint); impl Code { fn lower_expr(&mut self, expr: &Expr) -> LinearReg { let dest = self.alloc_reg(); - self.lower_expr_into(expr, dest); - LinearReg(dest) + let hint = self.lower_expr_into(expr, dest); + LinearReg(dest, hint) } - fn lower_expr_into(&mut self, expr: &Expr, dest: Reg) { + fn lower_expr_into(&mut self, expr: &Expr, dest: Reg) -> Hint { match expr { Expr::Leaf(atom) => match atom { Atom::Variable(var) => { @@ -47,6 +45,7 @@ impl Code { let src = self.register_const(Value(n)); self.bc .emit(Instruction::load_store(OpCode::LoadConst, dest, src)); + return Hint::UnboxedFloat64; } _ => todo!(), }, @@ -84,6 +83,7 @@ impl Code { _ => todo!(), }, } + Hint::None } fn alloc_reg(&mut self) -> Reg { @@ -141,7 +141,7 @@ impl RegsState { n_free_regs: code.free_regs.len(), } } - fn scope(self, code: &mut Code, f: impl FnOnce(&mut Code)) -> Self { + fn scope(self, code: &mut Code, f: impl FnOnce(&mut Code) -> T) -> Self { f(code); let old = code.reg_pointer; code.reg_pointer = self.reg_pointer; @@ -151,7 +151,7 @@ impl RegsState { n_free_regs: self.n_free_regs, } } - fn scope_hwm(self, code: &mut Code, f: impl FnOnce(&mut Code)) { + fn scope_hwm(self, code: &mut Code, f: impl FnOnce(&mut Code) -> T) { f(code); code.reg_pointer = code.reg_pointer.max(self.reg_pointer); code.free_regs.truncate(self.n_free_regs); @@ -166,7 +166,6 @@ pub fn test_interpreter(expr: &Expr<'_>) -> impl Display { free_regs: Vec::new(), }; let result = c.lower_expr(expr); - dbg!(&c, &result); forget(result); c } @@ -254,11 +253,13 @@ impl LinearReg { } } -impl Deref for LinearReg { - type Target = Reg; +impl HintedReg for LinearReg { + fn reg(&self) -> Reg { + self.0 + } - fn deref(&self) -> &Self::Target { - &self.0 + fn hint(&self) -> Hint { + self.1 } }