mirror of
https://github.com/Noratrieb/brainfuck.git
synced 2026-01-16 06:15:02 +01:00
remove bounds check
This commit is contained in:
parent
2df17352d1
commit
1e1a2a277b
3 changed files with 29 additions and 6 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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) => {
|
||||||
|
|
|
||||||
|
|
@ -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)]
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue