mirror of
https://github.com/uutils/awk.git
synced 2026-06-10 16:15:04 -07:00
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:
+6
-74
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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 => {
|
||||
|
||||
Reference in New Issue
Block a user