codegen works

This commit is contained in:
nora 2022-04-15 18:32:29 +02:00
parent eda2476021
commit 2df17352d1
6 changed files with 52 additions and 53 deletions

View file

@ -23,9 +23,10 @@ impl Write for MockReadWrite {
fn run_bf(bf: &str) { fn run_bf(bf: &str) {
let bump = Bump::new(); let bump = Bump::new();
let parsed = brainfuck::parse::parse(&bump, bf.bytes().enumerate()).unwrap(); let ast = brainfuck::parse::parse(&bump, bf.bytes().enumerate()).unwrap();
let optimized = brainfuck::opts::optimize(&bump, &parsed); let ir = brainfuck::opts::optimize(&bump, &ast);
brainfuck::ir_interpreter::run(&optimized, MockReadWrite, MockReadWrite); let code = brainfuck::codegen::generate(&bump, &ir);
brainfuck::codegen_interpreter::run(&code, MockReadWrite, MockReadWrite);
} }
fn optimized(c: &mut Criterion) { fn optimized(c: &mut Criterion) {

View file

@ -5,11 +5,14 @@
//! ``` //! ```
//! compiles down to //! compiles down to
//! ```text //! ```text
//! Add | Add | JmpIfZero | Out | End | Sub | JmpIfNonZero | Jmp //! Add | Add | JmpIfZero | Sub | JumpIfNotZero | Out | End
//! | | ^ | | //! | ^ | ^
//! +-------------------+---------|----------+ //! +-------|-----------|---------|
//! +---------------------+ //! +-----------+
//! ``` //! ```
//!
//! technically, the `JumpIfNotZero` would be an unconditional Jmp to the `JmpIfZero`, but that's
//! a needless indirection.
use crate::opts::{Ir, Stmt as IrStmt, StmtKind}; use crate::opts::{Ir, Stmt as IrStmt, StmtKind};
use crate::parse::Span; use crate::parse::Span;
@ -26,7 +29,6 @@ pub enum Stmt {
SetNull, SetNull,
JmpIfZero(usize), JmpIfZero(usize),
JmpIfNonZero(usize), JmpIfNonZero(usize),
Jmp(usize),
End, End,
} }
@ -36,36 +38,28 @@ pub struct Code<'c> {
pub debug: Vec<Span, &'c Bump>, pub debug: Vec<Span, &'c Bump>,
} }
struct UnlinkedCode<'u> {
pub stmts: Vec<Vec<Stmt, &'u Bump>, &'u Bump>,
pub debug: Vec<Vec<Span, &'u Bump>, &'u Bump>,
}
pub fn generate<'c>(alloc: &'c Bump, ir: &Ir<'_>) -> Code<'c> { pub fn generate<'c>(alloc: &'c Bump, ir: &Ir<'_>) -> Code<'c> {
let unlinked_alloc = Bump::new(); let stmts = Vec::new_in(alloc);
let debug = Vec::new_in(alloc);
let mut code = Code { stmts, debug };
let stmts = Vec::new_in(&unlinked_alloc); generate_stmts(&mut code, &ir.stmts);
let debug = Vec::new_in(&unlinked_alloc); code.stmts.push(Stmt::End);
let mut unlinked = UnlinkedCode { stmts, debug }; code.debug.push(Span::default());
generate_stmts(&unlinked_alloc, &mut unlinked, &ir.stmts);
link(alloc, &unlinked)
}
fn generate_stmts<'u>(alloc: &'u Bump, code: &mut UnlinkedCode<'u>, ir: &[IrStmt<'_>]) {
for ir_stmt in ir {
ir_to_stmt(alloc, code, ir_stmt, 0);
}
assert_eq!(code.stmts.len(), code.debug.len()); assert_eq!(code.stmts.len(), code.debug.len());
code
} }
fn ir_to_stmt<'u>( fn generate_stmts<'c>(code: &mut Code<'c>, ir: &[IrStmt<'_>]) {
alloc: &'u Bump, for ir_stmt in ir {
code: &mut UnlinkedCode<'u>, ir_to_stmt(code, ir_stmt);
ir_stmt: &IrStmt<'_>, }
current_block: usize, debug_assert_eq!(code.stmts.len(), code.debug.len());
) { }
fn ir_to_stmt<'c>(code: &mut Code<'c>, ir_stmt: &IrStmt<'_>) {
let stmt = match &ir_stmt.kind { let stmt = match &ir_stmt.kind {
StmtKind::Add(n) => Stmt::Add(*n), StmtKind::Add(n) => Stmt::Add(*n),
StmtKind::Sub(n) => Stmt::Sub(*n), StmtKind::Sub(n) => Stmt::Sub(*n),
@ -75,20 +69,27 @@ fn ir_to_stmt<'u>(
StmtKind::In => Stmt::In, StmtKind::In => Stmt::In,
StmtKind::SetNull => Stmt::SetNull, StmtKind::SetNull => Stmt::SetNull,
StmtKind::Loop(instr) => { StmtKind::Loop(instr) => {
let new_block = Vec::new_in(alloc); let skip_jmp_idx = code.stmts.len();
let new_block_debug = Vec::new_in(alloc); code.stmts.push(Stmt::JmpIfZero(usize::MAX)); // placeholder
code.stmts.push(new_block); code.debug.push(ir_stmt.span);
code.stmts.push(new_block_debug);
// compile the loop body now
generate_stmts(code, &instr.stmts);
// if the loop body is empty, we jmp to ourselves, which is an infinite loop - as expected
let first_loop_body_idx = skip_jmp_idx + 1;
code.stmts.push(Stmt::JmpIfNonZero(first_loop_body_idx));
code.debug.push(ir_stmt.span);
// there will always at least be an `End` instruction after the loop
let after_loop_idx = code.stmts.len();
// fix the placeholder with the actual index
code.stmts[skip_jmp_idx] = Stmt::JmpIfZero(after_loop_idx);
let current_block = code.stmts.len() - 1;
return; return;
} }
}; };
code.stmts[current_block].push(stmt); code.stmts.push(stmt);
code.debug[current_block].push(ir_stmt.span); code.debug.push(ir_stmt.span);
}
fn link<'c>(alloc: &'c Bump, code: &UnlinkedCode<'_>) -> Code<'c> {
todo!()
} }

View file

@ -82,9 +82,6 @@ impl<'c, W: Write, R: Read> Interpreter<'c, W, R> {
self.ip = pos; self.ip = pos;
} }
} }
Stmt::Jmp(pos) => {
self.ip = pos;
}
Stmt::End => break, Stmt::End => break,
} }
} }

View file

@ -7,8 +7,8 @@ use bumpalo::Bump;
use std::fmt::Display; use std::fmt::Display;
use std::io::{Read, Write}; use std::io::{Read, Write};
mod codegen; pub mod codegen;
mod codegen_interpreter; pub mod codegen_interpreter;
pub mod opts; pub mod opts;
pub mod parse; pub mod parse;

View file

@ -20,7 +20,7 @@ fn main() {
let stdin = io::stdin(); let stdin = io::stdin();
let stdin = stdin.lock(); let stdin = stdin.lock();
brainfuck::run(&file, stdout, stdin, UseProfile::Yes).unwrap_or_else(|_| { brainfuck::run(&file, stdout, stdin, UseProfile::No).unwrap_or_else(|_| {
eprintln!("error: Failed to parse brainfuck code"); eprintln!("error: Failed to parse brainfuck code");
process::exit(1); process::exit(1);
}); });

View file

@ -37,7 +37,7 @@ impl Span {
} }
} }
pub type Instrs<'ast> = Vec<(Instr<'ast>, Span), &'ast Bump>; pub type Ast<'ast> = Vec<(Instr<'ast>, Span), &'ast Bump>;
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum Instr<'ast> { pub enum Instr<'ast> {
@ -47,13 +47,13 @@ pub enum Instr<'ast> {
Left, Left,
Out, Out,
In, In,
Loop(Instrs<'ast>), Loop(Ast<'ast>),
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParseError; pub struct ParseError;
pub fn parse<I>(alloc: &Bump, mut src: I) -> Result<Instrs<'_>, ParseError> pub fn parse<I>(alloc: &Bump, mut src: I) -> Result<Ast<'_>, ParseError>
where where
I: Iterator<Item = (usize, u8)>, I: Iterator<Item = (usize, u8)>,
{ {
@ -85,7 +85,7 @@ fn parse_loop<'ast, I>(
src: &mut I, src: &mut I,
depth: u16, depth: u16,
start_idx: usize, start_idx: usize,
) -> Result<(Instrs<'ast>, Span), ParseError> ) -> Result<(Ast<'ast>, Span), ParseError>
where where
I: Iterator<Item = (usize, u8)>, I: Iterator<Item = (usize, u8)>,
{ {