remove bounds check

This commit is contained in:
nora 2022-04-15 18:57:39 +02:00
parent 2df17352d1
commit 1e1a2a277b
3 changed files with 29 additions and 6 deletions

View file

@ -13,6 +13,9 @@
//! //!
//! technically, the `JumpIfNotZero` would be an unconditional Jmp to the `JmpIfZero`, but that's //! technically, the `JumpIfNotZero` would be an unconditional Jmp to the `JmpIfZero`, but that's
//! a needless indirection. //! a needless indirection.
//!
//! this module must not produce out of bounds jumps and always put the `End` instruction at the
//! end
use crate::opts::{Ir, Stmt as IrStmt, StmtKind}; use crate::opts::{Ir, Stmt as IrStmt, StmtKind};
use crate::parse::Span; use crate::parse::Span;
@ -34,8 +37,18 @@ pub enum Stmt {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Code<'c> { pub struct Code<'c> {
pub stmts: Vec<Stmt, &'c Bump>, stmts: Vec<Stmt, &'c Bump>,
pub debug: Vec<Span, &'c Bump>, debug: Vec<Span, &'c Bump>,
}
impl Code<'_> {
pub fn stmts(&self) -> &[Stmt] {
&self.stmts
}
pub fn debug(&self) -> &[Span] {
&self.debug
}
} }
pub fn generate<'c>(alloc: &'c Bump, ir: &Ir<'_>) -> Code<'c> { pub fn generate<'c>(alloc: &'c Bump, ir: &Ir<'_>) -> Code<'c> {
@ -70,7 +83,7 @@ fn ir_to_stmt<'c>(code: &mut Code<'c>, ir_stmt: &IrStmt<'_>) {
StmtKind::SetNull => Stmt::SetNull, StmtKind::SetNull => Stmt::SetNull,
StmtKind::Loop(instr) => { StmtKind::Loop(instr) => {
let skip_jmp_idx = code.stmts.len(); let skip_jmp_idx = code.stmts.len();
code.stmts.push(Stmt::JmpIfZero(usize::MAX)); // placeholder code.stmts.push(Stmt::JmpIfZero(0)); // placeholder
code.debug.push(ir_stmt.span); code.debug.push(ir_stmt.span);
// compile the loop body now // compile the loop body now

View file

@ -30,13 +30,22 @@ where
mem: [Wrapping(0u8); MEM_SIZE], mem: [Wrapping(0u8); MEM_SIZE],
}; };
interpreter.execute(); // SAFETY: `Code` can only be produced by the `crate::codegen` module, which is trusted to not
// produce out of bounds jumps and put the `End` at the end
unsafe {
interpreter.execute();
}
} }
impl<'c, W: Write, R: Read> Interpreter<'c, W, R> { impl<'c, W: Write, R: Read> Interpreter<'c, W, R> {
fn execute(&mut self) { unsafe fn execute(&mut self) {
let stmts = self.code.stmts();
loop { loop {
let instr = self.code.stmts[self.ip]; // SAFETY: If the code ends with an `End` and there are no out of bounds jumps,
// `self.ip` will never be out of bounds
// Removing this bounds check speeds up execution by about 40%
debug_assert!(self.ip < stmts.len());
let instr = unsafe { *stmts.get_unchecked(self.ip) };
self.ip += 1; self.ip += 1;
match instr { match instr {
Stmt::Add(n) => { Stmt::Add(n) => {

View file

@ -1,4 +1,5 @@
#![feature(allocator_api, let_else)] #![feature(allocator_api, let_else)]
#![deny(unsafe_op_in_unsafe_fn)]
#![warn(rust_2018_idioms)] #![warn(rust_2018_idioms)]
#![allow(dead_code)] #![allow(dead_code)]