mirror of
https://github.com/uutils/awk.git
synced 2026-06-10 16:15:04 -07:00
interpreter: add conditional branches, etc.
This commit is contained in:
+25
-37
@@ -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::<Instruction>() <= 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<OpCode>, 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<OpCode>, 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<OpCode>, 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)
|
||||
}
|
||||
|
||||
+81
-23
@@ -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();
|
||||
|
||||
+15
-2
@@ -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;
|
||||
}
|
||||
|
||||
+3
-5
@@ -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
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user