make stmt a single word big

This commit is contained in:
nora 2022-04-15 19:04:43 +02:00
parent 1e1a2a277b
commit 7dd2c82fa4
4 changed files with 22 additions and 17 deletions

View file

@ -35,12 +35,14 @@ fn optimized(c: &mut Criterion) {
let loopremove = include_str!("loopremove.bf"); let loopremove = include_str!("loopremove.bf");
let twinkle = include_str!("twinkle.bf"); let twinkle = include_str!("twinkle.bf");
let bottles = include_str!("bottles.bf"); let bottles = include_str!("bottles.bf");
let hanoi = include_str!("hanoi.bf");
c.bench_function("fizzbuzz", |b| b.iter(|| run_bf(black_box(fizzbuzz)))); c.bench_function("fizzbuzz", |b| b.iter(|| run_bf(black_box(fizzbuzz))));
c.bench_function("bench", |b| b.iter(|| run_bf(black_box(bench)))); c.bench_function("bench", |b| b.iter(|| run_bf(black_box(bench))));
c.bench_function("loopremove", |b| b.iter(|| run_bf(black_box(loopremove)))); c.bench_function("loopremove", |b| b.iter(|| run_bf(black_box(loopremove))));
c.bench_function("twinkle", |b| b.iter(|| run_bf(black_box(twinkle)))); c.bench_function("twinkle", |b| b.iter(|| run_bf(black_box(twinkle))));
c.bench_function("bottles", |b| b.iter(|| run_bf(black_box(bottles)))); c.bench_function("bottles", |b| b.iter(|| run_bf(black_box(bottles))));
c.bench_function("hanoi", |b| b.iter(|| run_bf(black_box(hanoi))));
} }
criterion_group!(benches, optimized); criterion_group!(benches, optimized);

View file

@ -25,16 +25,18 @@ use bumpalo::Bump;
pub enum Stmt { pub enum Stmt {
Add(u8), Add(u8),
Sub(u8), Sub(u8),
Right(usize), Right(u32),
Left(usize), Left(u32),
Out, Out,
In, In,
SetNull, SetNull,
JmpIfZero(usize), JmpIfZero(u32),
JmpIfNonZero(usize), JmpIfNonZero(u32),
End, End,
} }
const _: [(); 8] = [(); std::mem::size_of::<Stmt>()];
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Code<'c> { pub struct Code<'c> {
stmts: Vec<Stmt, &'c Bump>, stmts: Vec<Stmt, &'c Bump>,
@ -76,8 +78,8 @@ 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),
StmtKind::Right(n) => Stmt::Right(*n), StmtKind::Right(n) => Stmt::Right(u32::try_from(*n).unwrap()),
StmtKind::Left(n) => Stmt::Left(*n), StmtKind::Left(n) => Stmt::Left(u32::try_from(*n).unwrap()),
StmtKind::Out => Stmt::Out, StmtKind::Out => Stmt::Out,
StmtKind::In => Stmt::In, StmtKind::In => Stmt::In,
StmtKind::SetNull => Stmt::SetNull, StmtKind::SetNull => Stmt::SetNull,
@ -90,14 +92,15 @@ fn ir_to_stmt<'c>(code: &mut Code<'c>, ir_stmt: &IrStmt<'_>) {
generate_stmts(code, &instr.stmts); generate_stmts(code, &instr.stmts);
// if the loop body is empty, we jmp to ourselves, which is an infinite loop - as expected // 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; let first_loop_body_idx = skip_jmp_idx + 1;
code.stmts.push(Stmt::JmpIfNonZero(first_loop_body_idx)); code.stmts
.push(Stmt::JmpIfNonZero(first_loop_body_idx.try_into().unwrap()));
code.debug.push(ir_stmt.span); code.debug.push(ir_stmt.span);
// there will always at least be an `End` instruction after the loop // there will always at least be an `End` instruction after the loop
let after_loop_idx = code.stmts.len(); let after_loop_idx = code.stmts.len();
// fix the placeholder with the actual index // fix the placeholder with the actual index
code.stmts[skip_jmp_idx] = Stmt::JmpIfZero(after_loop_idx); code.stmts[skip_jmp_idx] = Stmt::JmpIfZero(after_loop_idx.try_into().unwrap());
return; return;
} }

View file

@ -2,15 +2,15 @@ use crate::codegen::{Code, Stmt};
use std::io::{Read, Write}; use std::io::{Read, Write};
use std::num::Wrapping; use std::num::Wrapping;
const MEM_SIZE: usize = 32_000; const MEM_SIZE: u32 = 32_000;
type Memory = [Wrapping<u8>; MEM_SIZE]; type Memory = [Wrapping<u8>; MEM_SIZE as usize];
// TODO maybe repr(C) to prevent field reordering? // TODO maybe repr(C) to prevent field reordering?
struct Interpreter<'c, W, R> { struct Interpreter<'c, W, R> {
code: &'c Code<'c>, code: &'c Code<'c>,
ip: usize, ip: u32,
ptr: usize, ptr: u32,
stdout: W, stdout: W,
stdin: R, stdin: R,
mem: Memory, mem: Memory,
@ -27,7 +27,7 @@ where
ptr: 0, ptr: 0,
stdout, stdout,
stdin, stdin,
mem: [Wrapping(0u8); MEM_SIZE], mem: [Wrapping(0u8); MEM_SIZE as usize],
}; };
// SAFETY: `Code` can only be produced by the `crate::codegen` module, which is trusted to not // SAFETY: `Code` can only be produced by the `crate::codegen` module, which is trusted to not
@ -44,8 +44,8 @@ impl<'c, W: Write, R: Read> Interpreter<'c, W, R> {
// SAFETY: If the code ends with an `End` and there are no out of bounds jumps, // SAFETY: If the code ends with an `End` and there are no out of bounds jumps,
// `self.ip` will never be out of bounds // `self.ip` will never be out of bounds
// Removing this bounds check speeds up execution by about 40% // Removing this bounds check speeds up execution by about 40%
debug_assert!(self.ip < stmts.len()); debug_assert!((self.ip as usize) < stmts.len());
let instr = unsafe { *stmts.get_unchecked(self.ip) }; let instr = unsafe { *stmts.get_unchecked(self.ip as usize) };
self.ip += 1; self.ip += 1;
match instr { match instr {
Stmt::Add(n) => { Stmt::Add(n) => {
@ -97,10 +97,10 @@ impl<'c, W: Write, R: Read> Interpreter<'c, W, R> {
} }
fn elem_mut(&mut self) -> &mut Wrapping<u8> { fn elem_mut(&mut self) -> &mut Wrapping<u8> {
&mut self.mem[self.ptr] &mut self.mem[self.ptr as usize]
} }
fn elem(&self) -> u8 { fn elem(&self) -> u8 {
self.mem[self.ptr].0 self.mem[self.ptr as usize].0
} }
} }