interpreter: remove hinted instructions

This way, we can make room for bigger labels if necessary. We were not really using them, although this will surely force us to special-case `typeof()` and `isarray()` in the lowerer even more.
This commit is contained in:
Guillem L. Jara
2026-05-26 17:35:01 +02:00
parent 1ce27bf294
commit c5ae5cd710
3 changed files with 61 additions and 173 deletions
+6 -74
View File
@@ -16,8 +16,6 @@ use std::fmt::{Debug, Display};
pub use lower::test_interpreter;
use crate::ir::lower::ValueContext;
#[derive(Clone, Copy, Debug)]
#[repr(transparent)]
pub struct NonLocal(pub u16);
@@ -83,7 +81,6 @@ const _: () = const { assert!(size_of::<Instruction>() <= 8) };
#[repr(C, align(8))]
pub struct Instruction {
pub opcode: OpCode,
pub hint: Hint,
pub args: Arguments,
}
@@ -110,61 +107,41 @@ pub type CallArgs = (Reg, NonLocal, ArgCount);
pub type IndCallArgs = (Reg, Reg, ArgCount);
impl Instruction {
fn unary(opcode: impl Into<OpCode>, dest: Reg, src: &impl HintedReg) -> Self {
fn unary(opcode: impl Into<OpCode>, dest: Reg, src: Reg) -> Self {
let opcode = opcode.into();
debug_assert!(opcode.is_unary());
Self {
opcode,
args: Arguments { unary_local: (dest, src.reg()) },
hint: src.hint(),
args: Arguments { unary_local: (dest, src) },
}
}
fn binary(
opcode: impl Into<OpCode>,
dest: Reg,
lhs: &impl HintedReg,
rhs: &impl HintedReg,
) -> Self {
fn binary(opcode: impl Into<OpCode>, dest: Reg, lhs: Reg, rhs: Reg) -> 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.reg(), rhs.reg()) },
hint,
args: Arguments { binary_local: (dest, lhs, rhs) },
}
}
fn load_store(opcode: impl Into<OpCode>, dest: Reg, src: NonLocal, ctx: ValueContext) -> Self {
fn load_store(opcode: impl Into<OpCode>, dest: Reg, src: NonLocal) -> Self {
let opcode = opcode.into();
debug_assert!(opcode.is_load_store());
Self {
opcode,
args: Arguments { load_store: (dest, src) },
hint: ctx.into(),
}
}
fn jump(to: Label) -> Self {
Self {
opcode: OpCode::Jump,
args: Arguments { jump: to },
hint: Hint::None,
}
Self { opcode: OpCode::Jump, args: Arguments { jump: to } }
}
fn branch(cond: Reg, true_to: Label, false_to: Label) -> Self {
Self {
opcode: OpCode::Branch,
args: Arguments { branch: (cond, true_to, false_to) },
hint: Hint::None,
}
}
@@ -248,42 +225,6 @@ impl OpCode {
}
}
#[repr(u8, align(1))]
#[derive(Clone, Copy, Debug)]
pub enum Hint {
None = 0,
UnboxedFloat64,
UnboxedLhsFloat64,
UnboxedRhsFloat64,
ScalarCtx,
ArrayCtx,
}
trait HintedReg {
fn reg(&self) -> Reg;
fn hint(&self) -> Hint;
}
impl From<ValueContext> for Hint {
fn from(value: ValueContext) -> Self {
match value {
ValueContext::Scalar => Self::ScalarCtx,
ValueContext::Array => Self::ArrayCtx,
ValueContext::Untyped => Self::None,
}
}
}
impl From<Hint> for ValueContext {
fn from(value: Hint) -> Self {
match value {
Hint::ScalarCtx => Self::Scalar,
Hint::ArrayCtx => Self::Array,
_ => Self::Untyped,
}
}
}
impl Debug for Instruction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Instruction::{:?}", self.opcode)?;
@@ -365,15 +306,6 @@ 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::ScalarCtx => write!(f, "@ scalar_ctx"),
Hint::ArrayCtx => write!(f, "@ array_ctx"),
Hint::None => Ok(()),
}
}
}
+51 -91
View File
@@ -3,7 +3,7 @@
// For the full copyright and license information, please view the LICENSE
// files that was distributed with this source code.
use std::{borrow::Cow, mem::forget};
use std::{borrow::Cow, mem::forget, ops::Deref};
use bumpalo::{Bump, collections::Vec};
use parser::{
@@ -12,7 +12,7 @@ use parser::{
};
use crate::{
ir::{Hint, HintedReg, Instruction, Label, NonLocal, OpCode, Reg},
ir::{Instruction, Label, NonLocal, OpCode, Reg},
types::Value,
vm::{Consts, ExecMode, Interpreter, SymbolTable},
};
@@ -29,14 +29,8 @@ pub struct Code<'arena> {
#[must_use]
#[derive(Debug)]
struct LinearReg(Reg, Hint);
#[derive(Clone, Copy)]
pub enum ValueContext {
Untyped,
Scalar,
Array,
}
#[repr(transparent)]
struct LinearReg(Reg);
impl<'a> Code<'a> {
fn lower_body(&mut self, body: &Body) {
@@ -49,11 +43,11 @@ impl<'a> Code<'a> {
match stmnt {
Statement::If { condition, then_body, else_body } => {
let mut state = RegsState::new(self);
let condition = self.lower_expr(condition, ValueContext::Scalar);
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)));
let if_label = self
.bc
.emit(Instruction::branch(*condition, label_then, Label(0)));
self.free_reg(condition);
self.lower_body(then_body);
self.bc.nth(if_label).args.branch.2 = self.following_instr(0);
@@ -68,9 +62,9 @@ impl<'a> Code<'a> {
}
Statement::While { condition, then_body } => {
let cond_label = self.following_instr(0);
let condition = self.lower_expr(condition, ValueContext::Scalar);
let condition = self.lower_expr(condition);
let while_label = self.bc.emit(Instruction::branch(
condition.reg(),
*condition,
self.following_instr(1),
Label(0),
));
@@ -82,9 +76,9 @@ impl<'a> Code<'a> {
Statement::DoWhile { then_body, condition } => {
let do_label = self.following_instr(0);
self.lower_body(then_body);
let condition = self.lower_expr(condition, ValueContext::Scalar);
let condition = self.lower_expr(condition);
self.bc.emit(Instruction::branch(
condition.reg(),
*condition,
do_label,
self.following_instr(1),
));
@@ -92,20 +86,20 @@ impl<'a> Code<'a> {
}
Statement::For { init, condition, update, body } => {
if let Some(SimpleStatement::Expression(expr)) = init {
let reg = self.lower_expr(expr, ValueContext::Untyped);
let reg = self.lower_expr(expr);
self.free_reg(reg);
}
let cond_label = self.following_instr(0);
if let Some(condition) = condition {
let condition = self.lower_expr(condition, ValueContext::Scalar);
let condition = self.lower_expr(condition);
let body_label = self.following_instr(1);
let while_label =
self.bc
.emit(Instruction::branch(condition.reg(), body_label, Label(0)));
.emit(Instruction::branch(*condition, body_label, Label(0)));
self.free_reg(condition);
self.lower_body(body);
if let Some(SimpleStatement::Expression(expr)) = update {
let reg = self.lower_expr(expr, ValueContext::Untyped);
let reg = self.lower_expr(expr);
self.free_reg(reg);
}
self.bc.emit(Instruction::jump(cond_label));
@@ -113,44 +107,38 @@ impl<'a> Code<'a> {
} else {
self.lower_body(body);
if let Some(SimpleStatement::Expression(expr)) = update {
let reg = self.lower_expr(expr, ValueContext::Untyped);
let reg = self.lower_expr(expr);
self.free_reg(reg);
}
self.bc.emit(Instruction::jump(cond_label));
}
}
Statement::Simple(SimpleStatement::Expression(expr)) => {
let reg = self.lower_expr(expr, ValueContext::Untyped);
let reg = self.lower_expr(expr);
self.free_reg(reg);
}
_ => todo!(),
}
}
fn lower_expr(&mut self, expr: &Expr, ctx: ValueContext) -> LinearReg {
let mut dest = self.alloc_reg();
let hint = self.lower_expr_into(expr, dest.reg(), ctx);
dest.1 = hint;
fn lower_expr(&mut self, expr: &Expr) -> LinearReg {
let dest = self.alloc_reg();
self.lower_expr_into(expr, *dest);
dest
}
fn lower_expr_into(&mut self, expr: &Expr, dest: Reg, ctx: ValueContext) -> Hint {
fn lower_expr_into(&mut self, expr: &Expr, dest: Reg) {
match expr {
Expr::Leaf(atom) => match atom {
Atom::Variable(Variable::User(ident)) => {
let src = self.symbols.register_user_var(ident, self.arena);
self.bc
.emit(Instruction::load_store(OpCode::LoadUser, dest, src, ctx));
.emit(Instruction::load_store(OpCode::LoadUser, dest, src));
}
&Atom::Number(n) => {
let src = self.register_const(Value::Float(n));
self.bc.emit(Instruction::load_store(
OpCode::LoadConst,
dest,
src,
ValueContext::Scalar,
));
return Hint::UnboxedFloat64;
self.bc
.emit(Instruction::load_store(OpCode::LoadConst, dest, src));
}
atom @ (Atom::String(s) | Atom::TypedRegex(s)) => {
let val = if matches!(atom, Atom::String(_)) {
@@ -161,51 +149,37 @@ impl<'a> Code<'a> {
let src = self.register_const(val(Cow::Borrowed(
self.arena.alloc_slice_copy(s.as_ref()),
)));
self.bc.emit(Instruction::load_store(
OpCode::LoadConst,
dest,
src,
ValueContext::Scalar,
));
self.bc
.emit(Instruction::load_store(OpCode::LoadConst, dest, src));
}
Atom::Regex(r) => {
let src = self.register_const(Value::Regex(Cow::Borrowed(
self.arena.alloc_slice_copy(r.as_ref()),
)));
self.bc.emit(Instruction::load_store(
OpCode::LoadConst,
dest,
src,
ValueContext::Scalar,
));
let dest = LinearReg(dest, Hint::None);
let rec = self.lower_expr(&Expr::Leaf(Atom::Number(0.)), ctx);
self.bc.emit(Instruction::binary(
OpCode::Matches,
dest.reg(),
&rec,
&dest,
));
forget(dest);
self.bc
.emit(Instruction::load_store(OpCode::LoadConst, dest, src));
let rec = self.lower_expr(&Expr::Leaf(Atom::Number(0.)));
self.bc
.emit(Instruction::binary(OpCode::Matches, dest, *rec, dest));
self.free_reg(rec);
}
_ => todo!(),
},
Expr::Node(node) => match node.as_ref() {
ExprNode::UnaryOperation(op, expr) => {
let src = self.lower_expr(expr, ValueContext::Scalar);
self.bc.emit(Instruction::unary(*op, dest, &src));
let src = self.lower_expr(expr);
self.bc.emit(Instruction::unary(*op, dest, *src));
self.free_reg(src);
}
ExprNode::BinaryOperation(op, lhs, rhs) => {
let lhs = self.lower_expr(lhs, ValueContext::Scalar);
let rhs = self.lower_expr(rhs, ValueContext::Scalar);
self.bc.emit(Instruction::binary(*op, dest, &lhs, &rhs));
let lhs = self.lower_expr(lhs);
let rhs = self.lower_expr(rhs);
self.bc.emit(Instruction::binary(*op, dest, *lhs, *rhs));
self.free_reg(lhs);
self.free_reg(rhs);
}
ExprNode::Ternary(cond, true_then, false_then) => {
self.lower_expr_into(cond, dest, ValueContext::Scalar);
self.lower_expr_into(cond, dest);
let mut state = RegsState::new(self);
let branch =
@@ -213,43 +187,39 @@ impl<'a> Code<'a> {
.emit(Instruction::branch(dest, self.following_instr(1), Label(0)));
state = state.scope(self, |c| {
c.lower_expr_into(true_then, dest, ValueContext::Scalar)
c.lower_expr_into(true_then, dest);
});
let jump = self.bc.emit(Instruction::jump(Label(0)));
let label = self.following_instr(0);
state.scope_hwm(self, |c| {
c.lower_expr_into(false_then, dest, ValueContext::Scalar)
c.lower_expr_into(false_then, dest);
});
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, ctx);
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, ctx));
.emit(Instruction::load_store(OpCode::StoreUser, dest, var));
}
_ => todo!(),
},
}
Hint::None
}
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)
})
self.free_regs.pop().map(LinearReg).unwrap_or_else(|| {
let current = self.reg_pointer;
self.reg_pointer += 1;
LinearReg(Reg(current))
})
}
fn free_reg(&mut self, reg: LinearReg) {
@@ -381,24 +351,14 @@ impl LinearReg {
}
}
impl HintedReg for LinearReg {
fn reg(&self) -> Reg {
self.0
}
impl Deref for LinearReg {
type Target = Reg;
fn hint(&self) -> Hint {
self.1
fn deref(&self) -> &Self::Target {
&self.0
}
}
// impl Deref for Reg {
// type Target = Self;
// fn deref(&self) -> &Self::Target {
// self
// }
// }
#[cfg(debug_assertions)]
impl Drop for LinearReg {
fn drop(&mut self) {
+4 -8
View File
@@ -18,7 +18,7 @@ use parser::Identifier;
use crate::{
ir::{
Label, NonLocal, OpCode, Reg,
lower::{Bytecode, Code, ValueContext},
lower::{Bytecode, Code},
},
types::Value,
};
@@ -77,13 +77,9 @@ impl<'a> SymbolTable<'a> {
}
}
fn lookup_user_var(&mut self, var: NonLocal, ctx: ValueContext) -> &Value<'a> {
fn lookup_user_scalar(&mut self, var: NonLocal) -> &Value<'a> {
let v = self.user.get_index_mut(var.0 as _).unwrap().1;
match ctx {
ValueContext::Untyped => v,
ValueContext::Scalar => v.scalar_context(),
ValueContext::Array => v.array_context(),
}
v.scalar_context()
}
fn write_user_val(&mut self, var: NonLocal, value: Value<'a>) {
@@ -163,7 +159,7 @@ impl Interpreter<'_> {
self.registers.write(dest, val);
}
OpCode::LoadUser => {
let val = self.symbols.lookup_user_var(src, ix.hint.into()).clone();
let val = self.symbols.lookup_user_scalar(src).clone();
self.registers.write(dest, val);
}
OpCode::StoreUser => {