implementing comptime constants

This commit is contained in:
mlokr 2024-05-19 18:20:42 +02:00
parent b922dbd232
commit aef9951bc5
19 changed files with 743 additions and 646 deletions

View file

@ -3,7 +3,7 @@ global_var := 10;
complex_global_var := fib(global_var) - 5;
fib := fn(n: int): int {
if n <= 2 {
if 2 > n {
return n;
}
return fib(n - 1) + fib(n - 2);
@ -12,3 +12,4 @@ fib := fn(n: int): int {
main := fn(): int {
return complex_global_var;
}

File diff suppressed because it is too large Load diff

View file

@ -1,3 +1,5 @@
use std::simd::cmp::SimdPartialEq;
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct Token {
pub kind: TokenKind,
@ -290,85 +292,117 @@ impl<'a> Lexer<'a> {
}
}
pub fn line_col(bytes: &[u8], mut start: u32) -> (usize, usize) {
bytes
pub fn line_col(bytes: &[u8], pos: u32) -> (usize, usize) {
bytes[..pos as usize]
.split(|&b| b == b'\n')
.map(<[u8]>::len)
.enumerate()
.find_map(|(i, line)| {
if start < line.len() as u32 {
return Some((i + 1, start as usize + 1));
}
start -= line.len() as u32 + 1;
None
})
.last()
.map(|(line, col)| (line + 1, col + 1))
.unwrap_or((1, 1))
}
impl<'a> Iterator for Lexer<'a> {
type Item = Token;
pub struct LineMap {
lines: Box<[u8]>,
}
fn next(&mut self) -> Option<Self::Item> {
use TokenKind as T;
loop {
let mut start = self.pos;
let kind = match self.advance()? {
b'\n' | b'\r' | b'\t' | b' ' => continue,
b'0'..=b'9' => {
while let Some(b'0'..=b'9') = self.peek() {
self.advance();
}
T::Number
impl LineMap {
pub fn line_col(&self, mut pos: u32) -> (usize, usize) {
let mut line = 1;
let mut iter = self.lines.iter().copied();
while let Some(mut len) = iter.next() {
let mut acc = 0;
while len & 0x80 != 0 {
acc = (acc << 7) | (len & 0x7F) as u32;
len = iter.next().unwrap();
}
acc += len as u32;
if pos < acc {
break;
}
pos = pos.saturating_sub(acc);
line += 1;
}
(line, pos as usize + 1)
}
pub fn new(input: &str) -> Self {
let bytes = input.as_bytes();
let (start, simd_mid, end) = bytes.as_simd::<16>();
let query = std::simd::u8x16::splat(b'\n');
let nl_count = start.iter().map(|&b| (b == b'\n') as usize).sum::<usize>()
+ simd_mid
.iter()
.map(|s| s.simd_eq(query).to_bitmask().count_ones())
.sum::<u32>() as usize
+ end.iter().map(|&b| (b == b'\n') as usize).sum::<usize>();
let mut lines = Vec::with_capacity(nl_count);
let mut last_nl = 0;
let handle_rem = |offset: usize, bytes: &[u8], last_nl: &mut usize, lines: &mut Vec<u8>| {
bytes
.iter()
.copied()
.enumerate()
.filter_map(|(i, b)| (b == b'\n').then_some(i + offset))
.for_each(|i| {
lines.push((i - *last_nl + 1) as u8);
*last_nl = i + 1;
});
};
handle_rem(0, start, &mut last_nl, &mut lines);
for (i, simd) in simd_mid.iter().enumerate() {
let mask = simd.simd_eq(query);
let mut mask = mask.to_bitmask();
while mask != 0 {
let idx = mask.trailing_zeros() as usize + i * 16 + start.len();
let mut len = idx - last_nl + 1;
while len >= 0x80 {
lines.push((0x80 | (len & 0x7F)) as u8);
len >>= 7;
}
c @ (b'a'..=b'z' | b'A'..=b'Z' | b'_' | b'@') => {
while let Some(b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_') = self.peek() {
self.advance();
}
lines.push(len as u8);
last_nl = idx + 1;
mask &= mask - 1;
}
}
if c == b'@' {
start += 1;
T::Driective
} else {
let ident = &self.bytes[start as usize..self.pos as usize];
T::from_ident(ident)
}
}
b':' if self.advance_if(b'=') => T::Decl,
b':' => T::Colon,
b',' => T::Comma,
b'.' if self.advance_if(b'{') => T::Ctor,
b'.' if self.advance_if(b'(') => T::Tupl,
b'.' => T::Dot,
b';' => T::Semi,
b'!' if self.advance_if(b'=') => T::Ne,
b'=' if self.advance_if(b'=') => T::Eq,
b'=' => T::Assign,
b'<' if self.advance_if(b'=') => T::Le,
b'<' => T::Lt,
b'>' if self.advance_if(b'=') => T::Ge,
b'>' => T::Gt,
b'+' => T::Add,
b'-' => T::Sub,
b'*' => T::Mul,
b'/' => T::Div,
b'&' => T::Band,
b'(' => T::LParen,
b')' => T::RParen,
b'{' => T::LBrace,
b'}' => T::RBrace,
_ => T::Error,
};
handle_rem(bytes.len() - end.len(), end, &mut last_nl, &mut lines);
return Some(Token {
kind,
start,
end: self.pos,
});
Self {
lines: Box::from(lines),
}
}
}
#[cfg(test)]
mod tests {
fn map_lines(input: &'static str, _: &mut String) {
let line_map = super::LineMap::new(input);
for i in 0..input.len() {
assert_eq!(
line_map.line_col(i as u32),
//line_map.line_col(i as u32),
super::line_col(input.as_bytes(), i as u32)
);
}
}
crate::run_tests! { map_lines:
empty_file => "";
log_line => " ".repeat(1000).leak();
this_file => &include_str!("parser.rs")[..1000];
}
fn lex(input: &'static str, output: &mut String) {
use {
super::{Lexer, TokenKind as T},

View file

@ -1,4 +1,6 @@
#![feature(if_let_guard)]
#![feature(noop_waker)]
#![feature(portable_simd)]
#![feature(iter_collect_into)]
#![feature(macro_metavar_expr)]
#![feature(let_chains)]
@ -7,11 +9,7 @@
#![feature(slice_ptr_get)]
#![allow(dead_code)]
use std::{
collections::{HashSet, VecDeque},
io,
sync::{mpsc, Arc, Mutex},
};
use std::{collections::VecDeque, sync::Mutex};
#[macro_export]
macro_rules! run_tests {

View file

@ -14,13 +14,11 @@ fn main() -> io::Result<()> {
.skip(1)
.map(|path| std::fs::read_to_string(&path).map(|src| (path, src)))
.collect::<io::Result<Vec<_>>>()?;
let mut arena = parser::Arena::default();
let mut codegen = codegen::Codegen::default();
for (path, content) in files.iter() {
let mut parser = parser::Parser::new(&arena, &mut codegen.symbols, &parser::no_loader);
let file = parser.file(&path, content.as_str());
codegen.file(path, content.as_bytes(), file);
arena.clear();
codegen.files = vec![parser::Ast::new(&path, &content, &parser::no_loader)];
codegen.generate();
}
codegen.dump(&mut std::io::stdout())
}

View file

@ -1,20 +1,17 @@
use std::{
cell::{Cell, UnsafeCell},
collections::{HashMap, HashSet},
collections::HashMap,
io::{self, Read},
ops::Not,
ops::{Deref, Not},
path::{Path, PathBuf},
ptr::NonNull,
sync::{
atomic::{AtomicU32, AtomicUsize},
Mutex,
},
sync::{atomic::AtomicUsize, Mutex},
};
use crate::{
codegen::bt,
ident::{self, Ident},
lexer::{Lexer, Token, TokenKind},
lexer::{Lexer, LineMap, Token, TokenKind},
TaskQueue,
};
@ -22,13 +19,13 @@ pub type Pos = u32;
pub type IdentFlags = u32;
pub type Symbols = Vec<Symbol>;
pub type FileId = u32;
pub type Loader<'a> = &'a (dyn Fn(&str, &str) -> io::Result<Option<FileId>> + 'a);
pub type Loader<'a> = &'a (dyn Fn(&str, &str) -> io::Result<FileId> + 'a);
pub const MUTABLE: IdentFlags = 1 << std::mem::size_of::<IdentFlags>() * 8 - 1;
pub const REFERENCED: IdentFlags = 1 << std::mem::size_of::<IdentFlags>() * 8 - 2;
pub const MUTABLE: IdentFlags = 1 << (std::mem::size_of::<IdentFlags>() * 8 - 1);
pub const REFERENCED: IdentFlags = 1 << (std::mem::size_of::<IdentFlags>() * 8 - 2);
const GIT_DEPS_DIR: &str = "git-deps";
pub fn parse_all(root: &str, threads: usize) -> io::Result<Vec<Ast>> {
pub fn parse_all(threads: usize) -> io::Result<Vec<Ast>> {
enum ImportPath<'a> {
Root {
path: &'a str,
@ -86,9 +83,9 @@ pub fn parse_all(root: &str, threads: usize) -> io::Result<Vec<Ast>> {
}
impl<'a> ImportPath<'a> {
fn resolve(&self, from: &str, root: &str) -> Result<PathBuf, CantLoadFile> {
fn resolve(&self, from: &str) -> Result<PathBuf, CantLoadFile> {
match self {
Self::Root { path } => Ok(PathBuf::from_iter([root, path])),
Self::Root { path } => Ok(Path::new(path).to_owned()),
Self::Rel { path } => {
let path = PathBuf::from_iter([from, path]);
match path.canonicalize() {
@ -98,7 +95,7 @@ pub fn parse_all(root: &str, threads: usize) -> io::Result<Vec<Ast>> {
}
Self::Git { path, link, .. } => {
let link = preprocess_git(link);
Ok(PathBuf::from_iter([root, GIT_DEPS_DIR, link, path]))
Ok(PathBuf::from_iter([GIT_DEPS_DIR, link, path]))
}
}
}
@ -179,17 +176,7 @@ pub fn parse_all(root: &str, threads: usize) -> io::Result<Vec<Ast>> {
}
}
enum Task {
LoadFile {
id: FileId,
physiscal_path: PathBuf,
},
FetchGit {
id: FileId,
physiscal_path: PathBuf,
command: std::process::Command,
},
}
type Task = (FileId, PathBuf, Option<std::process::Command>);
let seen = Mutex::new(HashMap::<PathBuf, FileId>::new());
let tasks = TaskQueue::<Task>::new(threads);
@ -198,14 +185,14 @@ pub fn parse_all(root: &str, threads: usize) -> io::Result<Vec<Ast>> {
let loader = |path: &str, from: &str| {
let path = ImportPath::try_from(path)?;
let physiscal_path = path.resolve(from, root)?;
let physiscal_path = path.resolve(from)?;
let id = {
let mut seen = seen.lock().unwrap();
let len = seen.len();
match seen.entry(physiscal_path.clone()) {
std::collections::hash_map::Entry::Occupied(entry) => {
return Ok(Some(*entry.get()));
return Ok(*entry.get());
}
std::collections::hash_map::Entry::Vacant(entry) => {
entry.insert(len as _);
@ -214,91 +201,74 @@ pub fn parse_all(root: &str, threads: usize) -> io::Result<Vec<Ast>> {
}
};
if physiscal_path.exists() {
tasks.push(Task::LoadFile { id, physiscal_path });
return Ok(Some(id));
}
let command = if !physiscal_path.exists() {
let ImportPath::Git {
link,
branch,
rev,
tag,
..
} = path
else {
return Err(io::Error::new(
io::ErrorKind::NotFound,
format!("can't find file: {}", physiscal_path.display()),
));
};
let ImportPath::Git {
link,
path,
branch,
rev,
tag,
} = path
else {
return Err(io::Error::new(
io::ErrorKind::NotFound,
format!("can't find file: {}", physiscal_path.display()),
));
let root = PathBuf::from_iter([GIT_DEPS_DIR, preprocess_git(link)]);
let mut command = std::process::Command::new("git");
command
.args(["clone", "--depth", "1"])
.args(branch.map(|b| ["--branch", b]).into_iter().flatten())
.args(tag.map(|t| ["--tag", t]).into_iter().flatten())
.args(rev.map(|r| ["--rev", r]).into_iter().flatten())
.arg(link)
.arg(root);
Some(command)
} else {
None
};
let root = PathBuf::from_iter([root, GIT_DEPS_DIR, preprocess_git(link)]);
let mut command = std::process::Command::new("git");
command
.args(["clone", "--depth", "1"])
.args(branch.map(|b| ["--branch", b]).into_iter().flatten())
.args(tag.map(|t| ["--tag", t]).into_iter().flatten())
.args(rev.map(|r| ["--rev", r]).into_iter().flatten())
.arg(link)
.arg(root);
tasks.push(Task::FetchGit {
id,
physiscal_path,
command,
});
Ok(Some(id))
tasks.push((id, physiscal_path, command));
Ok(id)
};
let load_from_path = |path: &Path, buffer: &mut Vec<u8>| -> io::Result<Ast> {
let path = path.to_str().ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidData,
"path contains invalid characters",
)
})?;
let mut file = std::fs::File::open(&path)?;
file.read_to_end(buffer)?;
let src = std::str::from_utf8(buffer).map_err(InvalidFileData)?;
Ok(Ast::new(&path, src, &loader))
};
let execute_task = |task: Task, buffer: &mut Vec<u8>| match task {
Task::LoadFile { id, physiscal_path } => (id, load_from_path(&physiscal_path, buffer)),
Task::FetchGit {
id,
physiscal_path,
mut command,
} => {
let output = match command.output() {
Ok(output) => output,
Err(e) => return (id, Err(e)),
};
let execute_task = |(_, path, command): Task, buffer: &mut Vec<u8>| {
if let Some(mut command) = command {
let output = command.output()?;
if !output.status.success() {
let msg = format!(
"git command failed: {}",
String::from_utf8_lossy(&output.stderr)
);
return (id, Err(io::Error::new(io::ErrorKind::Other, msg)));
return Err(io::Error::new(io::ErrorKind::Other, msg));
}
(id, load_from_path(&physiscal_path, buffer))
}
let path = path.to_str().ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidData,
format!("path contains invalid characters: {}", path.display()),
)
})?;
let mut file = std::fs::File::open(path)?;
file.read_to_end(buffer)?;
let src = std::str::from_utf8(buffer).map_err(InvalidFileData)?;
Ok(Ast::new(path, src, &loader))
};
let thread = || {
let mut buffer = Vec::new();
while let Some(task) = tasks.pop() {
let (indx, res) = execute_task(task, &mut buffer);
while let Some(task @ (indx, ..)) = tasks.pop() {
let res = execute_task(task, &mut buffer);
buffer.clear();
let mut ast = ast.lock().unwrap();
let len = ast.len().max(indx as usize + 1);
ast.resize_with(len, || Err(io::ErrorKind::InvalidData.into()));
ast[indx as usize] = res;
buffer.clear();
}
};
@ -314,10 +284,11 @@ pub fn ident_flag_index(flag: IdentFlags) -> u32 {
flag & !(MUTABLE | REFERENCED)
}
pub fn no_loader(_: &str, _: &str) -> io::Result<Option<FileId>> {
Ok(None)
pub fn no_loader(_: &str, _: &str) -> io::Result<FileId> {
Err(io::ErrorKind::NotFound.into())
}
#[derive(Debug)]
pub struct Symbol {
pub name: Ident,
pub flags: IdentFlags,
@ -490,6 +461,21 @@ impl<'a, 'b> Parser<'a, 'b> {
let frame = self.idents.len();
let token = self.next();
let mut expr = match token.kind {
T::Driective if self.lexer.slice(token.range()) == "use" => {
self.expect_advance(TokenKind::LParen);
let str = self.expect_advance(TokenKind::String);
self.expect_advance(TokenKind::RParen);
let path = self.lexer.slice(str.range()).trim_matches('"');
E::Mod {
pos: token.start,
path: self.arena.alloc_str(path),
id: match (self.loader)(path, self.path) {
Ok(id) => id,
Err(e) => self.report(format_args!("error loading dependency: {e:#?}")),
},
}
}
T::Driective => E::Directive {
pos: token.start,
name: self.move_str(token),
@ -562,7 +548,9 @@ impl<'a, 'b> Parser<'a, 'b> {
op: token.kind,
val: {
let expr = self.ptr_unit_expr();
self.flag_idents(*expr, REFERENCED);
if token.kind == T::Band {
self.flag_idents(*expr, REFERENCED);
}
expr
},
},
@ -808,6 +796,11 @@ pub enum Expr<'a> {
name: &'a str,
args: &'a [Self],
},
Mod {
pos: Pos,
id: FileId,
path: &'a str,
},
}
impl<'a> Expr<'a> {
@ -816,6 +809,7 @@ impl<'a> Expr<'a> {
Self::Call { func, .. } => func.pos(),
Self::Ident { id, .. } => ident::pos(*id),
Self::Break { pos }
| Self::Mod { pos, .. }
| Self::Directive { pos, .. }
| Self::Continue { pos }
| Self::Closure { pos, .. }
@ -837,13 +831,13 @@ impl<'a> Expr<'a> {
impl<'a> std::fmt::Display for Expr<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
thread_local! {
static INDENT: Cell<usize> = Cell::new(0);
static INDENT: Cell<usize> = const { Cell::new(0) };
}
fn fmt_list<'a, T>(
fn fmt_list<T>(
f: &mut std::fmt::Formatter,
end: &str,
list: &'a [T],
list: &[T],
fmt: impl Fn(&T, &mut std::fmt::Formatter) -> std::fmt::Result,
) -> std::fmt::Result {
let first = &mut true;
@ -881,9 +875,8 @@ impl<'a> std::fmt::Display for Expr<'a> {
}
match *self {
Self::Field { target, field } => {
write!(f, "{}.{field}", Postfix(target))
}
Self::Mod { path, .. } => write!(f, "@mod(\"{path}\")"),
Self::Field { target, field } => write!(f, "{}.{field}", Postfix(target)),
Self::Directive { name, args, .. } => {
write!(f, "@{name}(")?;
fmt_list(f, ")", args, std::fmt::Display::fmt)
@ -980,12 +973,14 @@ impl<'a> std::fmt::Display for Expr<'a> {
}
#[repr(C)]
struct AstInner<T: ?Sized> {
pub struct AstInner<T: ?Sized> {
ref_count: AtomicUsize,
mem: ArenaChunk,
exprs: *const [Expr<'static>],
path: String,
symbols: T,
pub path: Box<str>,
pub nlines: LineMap,
pub symbols: T,
}
impl AstInner<[Symbol]> {
@ -1006,19 +1001,25 @@ impl AstInner<[Symbol]> {
let layout = Self::layout(syms.len());
let ptr = unsafe { std::alloc::alloc(layout) };
let inner: *mut Self = std::ptr::from_raw_parts_mut(ptr as *mut _, syms.len());
unsafe {
*(inner as *mut AstInner<()>) = AstInner {
ref_count: AtomicUsize::new(1),
mem: ArenaChunk::default(),
exprs,
path: path.to_owned(),
symbols: (),
};
let ptr = std::alloc::alloc(layout);
let inner: *mut Self = std::ptr::from_raw_parts_mut(ptr as *mut _, syms.len());
std::ptr::write(
inner as *mut AstInner<()>,
AstInner {
ref_count: AtomicUsize::new(1),
mem: arena.chunk.into_inner(),
exprs,
path: path.into(),
nlines: LineMap::new(content),
symbols: (),
},
);
std::ptr::addr_of_mut!((*inner).symbols)
.as_mut_ptr()
.copy_from_nonoverlapping(syms.as_ptr(), syms.len());
NonNull::new_unchecked(inner)
}
}
@ -1036,17 +1037,44 @@ impl Ast {
unsafe { &*self.inner().exprs }
}
pub fn symbols(&self) -> &[Symbol] {
&self.inner().symbols
}
pub fn path(&self) -> &str {
&self.inner().path
}
fn inner(&self) -> &AstInner<[Symbol]> {
unsafe { self.0.as_ref() }
}
pub fn find_decl(&self, id: Result<Ident, &str>) -> Option<&Expr> {
self.exprs().iter().find_map(|expr| match expr {
Expr::BinOp {
left: &Expr::Ident { id: iden, name, .. },
op: TokenKind::Decl,
..
} if Ok(iden) == id || Err(name) == id => Some(expr),
_ => None,
})
}
}
impl Default for Ast {
fn default() -> Self {
Self(AstInner::new("", "", &no_loader))
}
}
#[derive(Clone, Copy)]
#[repr(packed)]
pub struct ExprRef(NonNull<Expr<'static>>);
impl ExprRef {
pub fn new(expr: &Expr) -> Self {
Self(NonNull::from(expr).cast())
}
pub fn get<'a>(&self, from: &'a Ast) -> Option<&'a Expr<'a>> {
// SAFETY: the pointer is or was a valid reference in the past, if it points within one of
// arenas regions, it muts be walid, since arena does not give invalid pointers to its
// allocations
ArenaChunk::contains(from.mem.base, self.0.as_ptr() as _)
.then(|| unsafe { { self.0 }.as_ref() })
}
}
unsafe impl Send for Ast {}
@ -1079,6 +1107,14 @@ impl Drop for Ast {
}
}
impl Deref for Ast {
type Target = AstInner<[Symbol]>;
fn deref(&self) -> &Self::Target {
self.inner()
}
}
#[derive(Default)]
pub struct Arena<'a> {
chunk: UnsafeCell<ArenaChunk>,
@ -1092,6 +1128,10 @@ impl<'a> Arena<'a> {
}
pub fn alloc<T>(&self, value: T) -> &'a mut T {
if std::mem::size_of::<T>() == 0 {
return unsafe { NonNull::dangling().as_mut() };
}
let layout = std::alloc::Layout::new::<T>();
let ptr = self.alloc_low(layout);
unsafe { ptr.cast::<T>().write(value) };
@ -1099,6 +1139,10 @@ impl<'a> Arena<'a> {
}
pub fn alloc_slice<T: Copy>(&self, slice: &[T]) -> &'a mut [T] {
if slice.is_empty() || std::mem::size_of::<T>() == 0 {
return &mut [];
}
let layout = std::alloc::Layout::array::<T>(slice.len()).unwrap();
let ptr = self.alloc_low(layout);
unsafe {
@ -1109,23 +1153,6 @@ impl<'a> Arena<'a> {
unsafe { std::slice::from_raw_parts_mut(ptr.as_ptr() as _, slice.len()) }
}
pub fn clear(&mut self) {
let chunk = self.chunk.get_mut();
if chunk.base.is_null() {
return;
}
loop {
let prev = ArenaChunk::prev(chunk.base);
if prev.is_null() {
break;
}
chunk.base = prev;
}
chunk.end = unsafe { chunk.base.add(ArenaChunk::PREV_OFFSET) };
}
fn alloc_low(&self, layout: std::alloc::Layout) -> NonNull<u8> {
assert!(layout.align() <= ArenaChunk::ALIGN);
assert!(layout.size() <= ArenaChunk::CHUNK_SIZE);
@ -1136,10 +1163,8 @@ impl<'a> Arena<'a> {
return ptr;
}
if let Some(prev) = ArenaChunk::reset(ArenaChunk::prev(chunk.base)) {
*chunk = prev;
} else {
*chunk = ArenaChunk::new(chunk.base);
unsafe {
std::ptr::write(chunk, ArenaChunk::new(chunk.base));
}
chunk.alloc(layout).unwrap()
@ -1164,18 +1189,13 @@ impl ArenaChunk {
const CHUNK_SIZE: usize = 1 << 16;
const ALIGN: usize = std::mem::align_of::<Self>();
const NEXT_OFFSET: usize = Self::CHUNK_SIZE - std::mem::size_of::<*mut u8>();
const PREV_OFFSET: usize = Self::NEXT_OFFSET - std::mem::size_of::<*mut u8>();
const LAYOUT: std::alloc::Layout =
unsafe { std::alloc::Layout::from_size_align_unchecked(Self::CHUNK_SIZE, Self::ALIGN) };
fn new(next: *mut u8) -> Self {
let base = unsafe { std::alloc::alloc(Self::LAYOUT) };
let end = unsafe { base.add(Self::PREV_OFFSET) };
if !next.is_null() {
Self::set_prev(next, base);
}
let end = unsafe { base.add(Self::NEXT_OFFSET) };
Self::set_next(base, next);
Self::set_prev(base, std::ptr::null_mut());
Self { base, end }
}
@ -1183,21 +1203,10 @@ impl ArenaChunk {
unsafe { std::ptr::write(curr.add(Self::NEXT_OFFSET) as *mut _, next) };
}
fn set_prev(curr: *mut u8, prev: *mut u8) {
unsafe { std::ptr::write(curr.add(Self::PREV_OFFSET) as *mut _, prev) };
}
fn next(curr: *mut u8) -> *mut u8 {
unsafe { std::ptr::read(curr.add(Self::NEXT_OFFSET) as *mut _) }
}
fn prev(curr: *mut u8) -> *mut u8 {
if curr.is_null() {
return std::ptr::null_mut();
}
unsafe { std::ptr::read(curr.add(Self::PREV_OFFSET) as *mut _) }
}
fn reset(prev: *mut u8) -> Option<Self> {
if prev.is_null() {
return None;
@ -1218,19 +1227,17 @@ impl ArenaChunk {
unsafe { self.end = self.end.sub(size) };
unsafe { Some(NonNull::new_unchecked(self.end)) }
}
fn contains(base: *mut u8, arg: *mut u8) -> bool {
!base.is_null()
&& ((unsafe { base.add(Self::CHUNK_SIZE) } > arg && base <= arg)
|| Self::contains(Self::next(base), arg))
}
}
impl Drop for ArenaChunk {
fn drop(&mut self) {
let mut current = self.base;
let mut prev = Self::prev(current);
while !prev.is_null() {
let next = Self::prev(prev);
unsafe { std::alloc::dealloc(prev, Self::LAYOUT) };
prev = next;
}
while !current.is_null() {
let next = Self::next(current);
unsafe { std::alloc::dealloc(current, Self::LAYOUT) };
@ -1241,16 +1248,14 @@ impl Drop for ArenaChunk {
#[cfg(test)]
mod tests {
fn parse(input: &'static str, output: &mut String) {
use std::fmt::Write;
let mut arena = super::Arena::default();
let arena = super::Arena::default();
let mut symbols = Vec::new();
let mut parser = super::Parser::new(&arena, &mut symbols, &super::no_loader);
for expr in parser.file(input, "test") {
writeln!(output, "{}", expr).unwrap();
}
arena.clear();
}
crate::run_tests! { parse:

View file

@ -1,4 +1,4 @@
ev: Ecall
code size: 217
code size: 204
ret: 0
status: Ok(())

View file

@ -1,3 +1,3 @@
code size: 657
code size: 569
ret: 0
status: Ok(())

View file

@ -1,3 +1,3 @@
code size: 308
ret: 44
code size: 314
ret: 33
status: Ok(())

View file

@ -0,0 +1,3 @@
code size: 305
ret: 50
status: Ok(())

View file

@ -1,3 +1,3 @@
code size: 281
ret: 16
code size: 287
ret: 55
status: Ok(())

View file

@ -1,3 +1,3 @@
code size: 379
code size: 366
ret: 0
status: Ok(())

View file

@ -1,3 +1,3 @@
code size: 461
code size: 464
ret: 3
status: Ok(())

View file

@ -78,3 +78,37 @@ impl From<StoreError> for crate::VmRunError {
Self::StoreAccessEx(value.0)
}
}
#[derive(Default)]
pub struct HostMemory;
impl Memory for HostMemory {
#[inline]
unsafe fn load(
&mut self,
addr: Address,
target: *mut u8,
count: usize,
) -> Result<(), LoadError> {
unsafe { core::ptr::copy(addr.get() as *const u8, target, count) }
Ok(())
}
#[inline]
unsafe fn store(
&mut self,
addr: Address,
source: *const u8,
count: usize,
) -> Result<(), StoreError> {
debug_assert!(addr.get() != 0);
debug_assert!(source != core::ptr::null());
unsafe { core::ptr::copy(source, addr.get() as *mut u8, count) }
Ok(())
}
#[inline]
unsafe fn prog_read<T: Copy>(&mut self, addr: Address) -> T {
debug_assert!(addr.get() != 0);
unsafe { core::ptr::read(addr.get() as *const T) }
}
}

View file

@ -54,7 +54,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut vm = unsafe {
Vm::<_, 0>::new(
mem::HostMemory,
hbvm::mem::HostMemory,
Address::new(mmap.as_ptr().add(stack.len()) as u64),
)
};

View file

@ -1,37 +1,5 @@
use std::alloc::Layout;
use hbvm::mem::{Address, LoadError, Memory, StoreError};
pub struct HostMemory;
impl Memory for HostMemory {
#[inline]
unsafe fn load(
&mut self,
addr: Address,
target: *mut u8,
count: usize,
) -> Result<(), LoadError> {
unsafe { core::ptr::copy(addr.get() as *const u8, target, count) }
Ok(())
}
#[inline]
unsafe fn store(
&mut self,
addr: Address,
source: *const u8,
count: usize,
) -> Result<(), StoreError> {
unsafe { core::ptr::copy(source, addr.get() as *mut u8, count) }
Ok(())
}
#[inline]
unsafe fn prog_read<T: Copy>(&mut self, addr: Address) -> T {
unsafe { core::ptr::read(addr.get() as *const T) }
}
}
const STACK_SIZE: usize = 2; // MiB
type Stack = [u8; 1024 * 1024 * STACK_SIZE];

View file

View file

View file