implementing comptime constants
This commit is contained in:
parent
b922dbd232
commit
aef9951bc5
|
@ -3,7 +3,7 @@ global_var := 10;
|
||||||
complex_global_var := fib(global_var) - 5;
|
complex_global_var := fib(global_var) - 5;
|
||||||
|
|
||||||
fib := fn(n: int): int {
|
fib := fn(n: int): int {
|
||||||
if n <= 2 {
|
if 2 > n {
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
return fib(n - 1) + fib(n - 2);
|
return fib(n - 1) + fib(n - 2);
|
||||||
|
@ -12,3 +12,4 @@ fib := fn(n: int): int {
|
||||||
main := fn(): int {
|
main := fn(): int {
|
||||||
return complex_global_var;
|
return complex_global_var;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,3 +1,5 @@
|
||||||
|
use std::simd::cmp::SimdPartialEq;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
pub struct Token {
|
pub struct Token {
|
||||||
pub kind: TokenKind,
|
pub kind: TokenKind,
|
||||||
|
@ -290,85 +292,117 @@ impl<'a> Lexer<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn line_col(bytes: &[u8], mut start: u32) -> (usize, usize) {
|
pub fn line_col(bytes: &[u8], pos: u32) -> (usize, usize) {
|
||||||
bytes
|
bytes[..pos as usize]
|
||||||
.split(|&b| b == b'\n')
|
.split(|&b| b == b'\n')
|
||||||
|
.map(<[u8]>::len)
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.find_map(|(i, line)| {
|
.last()
|
||||||
if start < line.len() as u32 {
|
.map(|(line, col)| (line + 1, col + 1))
|
||||||
return Some((i + 1, start as usize + 1));
|
|
||||||
}
|
|
||||||
start -= line.len() as u32 + 1;
|
|
||||||
None
|
|
||||||
})
|
|
||||||
.unwrap_or((1, 1))
|
.unwrap_or((1, 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iterator for Lexer<'a> {
|
pub struct LineMap {
|
||||||
type Item = Token;
|
lines: Box<[u8]>,
|
||||||
|
}
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
impl LineMap {
|
||||||
use TokenKind as T;
|
pub fn line_col(&self, mut pos: u32) -> (usize, usize) {
|
||||||
loop {
|
let mut line = 1;
|
||||||
let mut start = self.pos;
|
|
||||||
let kind = match self.advance()? {
|
let mut iter = self.lines.iter().copied();
|
||||||
b'\n' | b'\r' | b'\t' | b' ' => continue,
|
|
||||||
b'0'..=b'9' => {
|
while let Some(mut len) = iter.next() {
|
||||||
while let Some(b'0'..=b'9') = self.peek() {
|
let mut acc = 0;
|
||||||
self.advance();
|
while len & 0x80 != 0 {
|
||||||
}
|
acc = (acc << 7) | (len & 0x7F) as u32;
|
||||||
T::Number
|
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'@') => {
|
lines.push(len as u8);
|
||||||
while let Some(b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_') = self.peek() {
|
last_nl = idx + 1;
|
||||||
self.advance();
|
mask &= mask - 1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if c == b'@' {
|
handle_rem(bytes.len() - end.len(), end, &mut last_nl, &mut lines);
|
||||||
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,
|
|
||||||
};
|
|
||||||
|
|
||||||
return Some(Token {
|
Self {
|
||||||
kind,
|
lines: Box::from(lines),
|
||||||
start,
|
|
||||||
end: self.pos,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
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) {
|
fn lex(input: &'static str, output: &mut String) {
|
||||||
use {
|
use {
|
||||||
super::{Lexer, TokenKind as T},
|
super::{Lexer, TokenKind as T},
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
|
#![feature(if_let_guard)]
|
||||||
#![feature(noop_waker)]
|
#![feature(noop_waker)]
|
||||||
|
#![feature(portable_simd)]
|
||||||
#![feature(iter_collect_into)]
|
#![feature(iter_collect_into)]
|
||||||
#![feature(macro_metavar_expr)]
|
#![feature(macro_metavar_expr)]
|
||||||
#![feature(let_chains)]
|
#![feature(let_chains)]
|
||||||
|
@ -7,11 +9,7 @@
|
||||||
#![feature(slice_ptr_get)]
|
#![feature(slice_ptr_get)]
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use std::{
|
use std::{collections::VecDeque, sync::Mutex};
|
||||||
collections::{HashSet, VecDeque},
|
|
||||||
io,
|
|
||||||
sync::{mpsc, Arc, Mutex},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! run_tests {
|
macro_rules! run_tests {
|
||||||
|
|
|
@ -14,13 +14,11 @@ fn main() -> io::Result<()> {
|
||||||
.skip(1)
|
.skip(1)
|
||||||
.map(|path| std::fs::read_to_string(&path).map(|src| (path, src)))
|
.map(|path| std::fs::read_to_string(&path).map(|src| (path, src)))
|
||||||
.collect::<io::Result<Vec<_>>>()?;
|
.collect::<io::Result<Vec<_>>>()?;
|
||||||
let mut arena = parser::Arena::default();
|
|
||||||
let mut codegen = codegen::Codegen::default();
|
let mut codegen = codegen::Codegen::default();
|
||||||
for (path, content) in files.iter() {
|
for (path, content) in files.iter() {
|
||||||
let mut parser = parser::Parser::new(&arena, &mut codegen.symbols, &parser::no_loader);
|
codegen.files = vec![parser::Ast::new(&path, &content, &parser::no_loader)];
|
||||||
let file = parser.file(&path, content.as_str());
|
codegen.generate();
|
||||||
codegen.file(path, content.as_bytes(), file);
|
|
||||||
arena.clear();
|
|
||||||
}
|
}
|
||||||
codegen.dump(&mut std::io::stdout())
|
codegen.dump(&mut std::io::stdout())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,17 @@
|
||||||
use std::{
|
use std::{
|
||||||
cell::{Cell, UnsafeCell},
|
cell::{Cell, UnsafeCell},
|
||||||
collections::{HashMap, HashSet},
|
collections::HashMap,
|
||||||
io::{self, Read},
|
io::{self, Read},
|
||||||
ops::Not,
|
ops::{Deref, Not},
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
ptr::NonNull,
|
ptr::NonNull,
|
||||||
sync::{
|
sync::{atomic::AtomicUsize, Mutex},
|
||||||
atomic::{AtomicU32, AtomicUsize},
|
|
||||||
Mutex,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
codegen::bt,
|
codegen::bt,
|
||||||
ident::{self, Ident},
|
ident::{self, Ident},
|
||||||
lexer::{Lexer, Token, TokenKind},
|
lexer::{Lexer, LineMap, Token, TokenKind},
|
||||||
TaskQueue,
|
TaskQueue,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -22,13 +19,13 @@ pub type Pos = u32;
|
||||||
pub type IdentFlags = u32;
|
pub type IdentFlags = u32;
|
||||||
pub type Symbols = Vec<Symbol>;
|
pub type Symbols = Vec<Symbol>;
|
||||||
pub type FileId = u32;
|
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 MUTABLE: IdentFlags = 1 << (std::mem::size_of::<IdentFlags>() * 8 - 1);
|
||||||
pub const REFERENCED: IdentFlags = 1 << std::mem::size_of::<IdentFlags>() * 8 - 2;
|
pub const REFERENCED: IdentFlags = 1 << (std::mem::size_of::<IdentFlags>() * 8 - 2);
|
||||||
const GIT_DEPS_DIR: &str = "git-deps";
|
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> {
|
enum ImportPath<'a> {
|
||||||
Root {
|
Root {
|
||||||
path: &'a str,
|
path: &'a str,
|
||||||
|
@ -86,9 +83,9 @@ pub fn parse_all(root: &str, threads: usize) -> io::Result<Vec<Ast>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ImportPath<'a> {
|
impl<'a> ImportPath<'a> {
|
||||||
fn resolve(&self, from: &str, root: &str) -> Result<PathBuf, CantLoadFile> {
|
fn resolve(&self, from: &str) -> Result<PathBuf, CantLoadFile> {
|
||||||
match self {
|
match self {
|
||||||
Self::Root { path } => Ok(PathBuf::from_iter([root, path])),
|
Self::Root { path } => Ok(Path::new(path).to_owned()),
|
||||||
Self::Rel { path } => {
|
Self::Rel { path } => {
|
||||||
let path = PathBuf::from_iter([from, path]);
|
let path = PathBuf::from_iter([from, path]);
|
||||||
match path.canonicalize() {
|
match path.canonicalize() {
|
||||||
|
@ -98,7 +95,7 @@ pub fn parse_all(root: &str, threads: usize) -> io::Result<Vec<Ast>> {
|
||||||
}
|
}
|
||||||
Self::Git { path, link, .. } => {
|
Self::Git { path, link, .. } => {
|
||||||
let link = preprocess_git(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 {
|
type Task = (FileId, PathBuf, Option<std::process::Command>);
|
||||||
LoadFile {
|
|
||||||
id: FileId,
|
|
||||||
physiscal_path: PathBuf,
|
|
||||||
},
|
|
||||||
FetchGit {
|
|
||||||
id: FileId,
|
|
||||||
physiscal_path: PathBuf,
|
|
||||||
command: std::process::Command,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
let seen = Mutex::new(HashMap::<PathBuf, FileId>::new());
|
let seen = Mutex::new(HashMap::<PathBuf, FileId>::new());
|
||||||
let tasks = TaskQueue::<Task>::new(threads);
|
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 loader = |path: &str, from: &str| {
|
||||||
let path = ImportPath::try_from(path)?;
|
let path = ImportPath::try_from(path)?;
|
||||||
|
|
||||||
let physiscal_path = path.resolve(from, root)?;
|
let physiscal_path = path.resolve(from)?;
|
||||||
|
|
||||||
let id = {
|
let id = {
|
||||||
let mut seen = seen.lock().unwrap();
|
let mut seen = seen.lock().unwrap();
|
||||||
let len = seen.len();
|
let len = seen.len();
|
||||||
match seen.entry(physiscal_path.clone()) {
|
match seen.entry(physiscal_path.clone()) {
|
||||||
std::collections::hash_map::Entry::Occupied(entry) => {
|
std::collections::hash_map::Entry::Occupied(entry) => {
|
||||||
return Ok(Some(*entry.get()));
|
return Ok(*entry.get());
|
||||||
}
|
}
|
||||||
std::collections::hash_map::Entry::Vacant(entry) => {
|
std::collections::hash_map::Entry::Vacant(entry) => {
|
||||||
entry.insert(len as _);
|
entry.insert(len as _);
|
||||||
|
@ -214,91 +201,74 @@ pub fn parse_all(root: &str, threads: usize) -> io::Result<Vec<Ast>> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if physiscal_path.exists() {
|
let command = if !physiscal_path.exists() {
|
||||||
tasks.push(Task::LoadFile { id, physiscal_path });
|
let ImportPath::Git {
|
||||||
return Ok(Some(id));
|
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 {
|
let root = PathBuf::from_iter([GIT_DEPS_DIR, preprocess_git(link)]);
|
||||||
link,
|
|
||||||
path,
|
let mut command = std::process::Command::new("git");
|
||||||
branch,
|
command
|
||||||
rev,
|
.args(["clone", "--depth", "1"])
|
||||||
tag,
|
.args(branch.map(|b| ["--branch", b]).into_iter().flatten())
|
||||||
} = path
|
.args(tag.map(|t| ["--tag", t]).into_iter().flatten())
|
||||||
else {
|
.args(rev.map(|r| ["--rev", r]).into_iter().flatten())
|
||||||
return Err(io::Error::new(
|
.arg(link)
|
||||||
io::ErrorKind::NotFound,
|
.arg(root);
|
||||||
format!("can't find file: {}", physiscal_path.display()),
|
Some(command)
|
||||||
));
|
} else {
|
||||||
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let root = PathBuf::from_iter([root, GIT_DEPS_DIR, preprocess_git(link)]);
|
tasks.push((id, physiscal_path, command));
|
||||||
|
Ok(id)
|
||||||
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))
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let load_from_path = |path: &Path, buffer: &mut Vec<u8>| -> io::Result<Ast> {
|
let execute_task = |(_, path, command): Task, buffer: &mut Vec<u8>| {
|
||||||
let path = path.to_str().ok_or_else(|| {
|
if let Some(mut command) = command {
|
||||||
io::Error::new(
|
let output = command.output()?;
|
||||||
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)),
|
|
||||||
};
|
|
||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
let msg = format!(
|
let msg = format!(
|
||||||
"git command failed: {}",
|
"git command failed: {}",
|
||||||
String::from_utf8_lossy(&output.stderr)
|
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 thread = || {
|
||||||
let mut buffer = Vec::new();
|
let mut buffer = Vec::new();
|
||||||
while let Some(task) = tasks.pop() {
|
while let Some(task @ (indx, ..)) = tasks.pop() {
|
||||||
let (indx, res) = execute_task(task, &mut buffer);
|
let res = execute_task(task, &mut buffer);
|
||||||
|
buffer.clear();
|
||||||
|
|
||||||
let mut ast = ast.lock().unwrap();
|
let mut ast = ast.lock().unwrap();
|
||||||
let len = ast.len().max(indx as usize + 1);
|
let len = ast.len().max(indx as usize + 1);
|
||||||
ast.resize_with(len, || Err(io::ErrorKind::InvalidData.into()));
|
ast.resize_with(len, || Err(io::ErrorKind::InvalidData.into()));
|
||||||
ast[indx as usize] = res;
|
ast[indx as usize] = res;
|
||||||
|
|
||||||
buffer.clear();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -314,10 +284,11 @@ pub fn ident_flag_index(flag: IdentFlags) -> u32 {
|
||||||
flag & !(MUTABLE | REFERENCED)
|
flag & !(MUTABLE | REFERENCED)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn no_loader(_: &str, _: &str) -> io::Result<Option<FileId>> {
|
pub fn no_loader(_: &str, _: &str) -> io::Result<FileId> {
|
||||||
Ok(None)
|
Err(io::ErrorKind::NotFound.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Symbol {
|
pub struct Symbol {
|
||||||
pub name: Ident,
|
pub name: Ident,
|
||||||
pub flags: IdentFlags,
|
pub flags: IdentFlags,
|
||||||
|
@ -490,6 +461,21 @@ impl<'a, 'b> Parser<'a, 'b> {
|
||||||
let frame = self.idents.len();
|
let frame = self.idents.len();
|
||||||
let token = self.next();
|
let token = self.next();
|
||||||
let mut expr = match token.kind {
|
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 {
|
T::Driective => E::Directive {
|
||||||
pos: token.start,
|
pos: token.start,
|
||||||
name: self.move_str(token),
|
name: self.move_str(token),
|
||||||
|
@ -562,7 +548,9 @@ impl<'a, 'b> Parser<'a, 'b> {
|
||||||
op: token.kind,
|
op: token.kind,
|
||||||
val: {
|
val: {
|
||||||
let expr = self.ptr_unit_expr();
|
let expr = self.ptr_unit_expr();
|
||||||
self.flag_idents(*expr, REFERENCED);
|
if token.kind == T::Band {
|
||||||
|
self.flag_idents(*expr, REFERENCED);
|
||||||
|
}
|
||||||
expr
|
expr
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -808,6 +796,11 @@ pub enum Expr<'a> {
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
args: &'a [Self],
|
args: &'a [Self],
|
||||||
},
|
},
|
||||||
|
Mod {
|
||||||
|
pos: Pos,
|
||||||
|
id: FileId,
|
||||||
|
path: &'a str,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Expr<'a> {
|
impl<'a> Expr<'a> {
|
||||||
|
@ -816,6 +809,7 @@ impl<'a> Expr<'a> {
|
||||||
Self::Call { func, .. } => func.pos(),
|
Self::Call { func, .. } => func.pos(),
|
||||||
Self::Ident { id, .. } => ident::pos(*id),
|
Self::Ident { id, .. } => ident::pos(*id),
|
||||||
Self::Break { pos }
|
Self::Break { pos }
|
||||||
|
| Self::Mod { pos, .. }
|
||||||
| Self::Directive { pos, .. }
|
| Self::Directive { pos, .. }
|
||||||
| Self::Continue { pos }
|
| Self::Continue { pos }
|
||||||
| Self::Closure { pos, .. }
|
| Self::Closure { pos, .. }
|
||||||
|
@ -837,13 +831,13 @@ impl<'a> Expr<'a> {
|
||||||
impl<'a> std::fmt::Display for Expr<'a> {
|
impl<'a> std::fmt::Display for Expr<'a> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
thread_local! {
|
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,
|
f: &mut std::fmt::Formatter,
|
||||||
end: &str,
|
end: &str,
|
||||||
list: &'a [T],
|
list: &[T],
|
||||||
fmt: impl Fn(&T, &mut std::fmt::Formatter) -> std::fmt::Result,
|
fmt: impl Fn(&T, &mut std::fmt::Formatter) -> std::fmt::Result,
|
||||||
) -> std::fmt::Result {
|
) -> std::fmt::Result {
|
||||||
let first = &mut true;
|
let first = &mut true;
|
||||||
|
@ -881,9 +875,8 @@ impl<'a> std::fmt::Display for Expr<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
match *self {
|
match *self {
|
||||||
Self::Field { target, field } => {
|
Self::Mod { path, .. } => write!(f, "@mod(\"{path}\")"),
|
||||||
write!(f, "{}.{field}", Postfix(target))
|
Self::Field { target, field } => write!(f, "{}.{field}", Postfix(target)),
|
||||||
}
|
|
||||||
Self::Directive { name, args, .. } => {
|
Self::Directive { name, args, .. } => {
|
||||||
write!(f, "@{name}(")?;
|
write!(f, "@{name}(")?;
|
||||||
fmt_list(f, ")", args, std::fmt::Display::fmt)
|
fmt_list(f, ")", args, std::fmt::Display::fmt)
|
||||||
|
@ -980,12 +973,14 @@ impl<'a> std::fmt::Display for Expr<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
struct AstInner<T: ?Sized> {
|
pub struct AstInner<T: ?Sized> {
|
||||||
ref_count: AtomicUsize,
|
ref_count: AtomicUsize,
|
||||||
mem: ArenaChunk,
|
mem: ArenaChunk,
|
||||||
exprs: *const [Expr<'static>],
|
exprs: *const [Expr<'static>],
|
||||||
path: String,
|
|
||||||
symbols: T,
|
pub path: Box<str>,
|
||||||
|
pub nlines: LineMap,
|
||||||
|
pub symbols: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AstInner<[Symbol]> {
|
impl AstInner<[Symbol]> {
|
||||||
|
@ -1006,19 +1001,25 @@ impl AstInner<[Symbol]> {
|
||||||
|
|
||||||
let layout = Self::layout(syms.len());
|
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 {
|
unsafe {
|
||||||
*(inner as *mut AstInner<()>) = AstInner {
|
let ptr = std::alloc::alloc(layout);
|
||||||
ref_count: AtomicUsize::new(1),
|
let inner: *mut Self = std::ptr::from_raw_parts_mut(ptr as *mut _, syms.len());
|
||||||
mem: ArenaChunk::default(),
|
|
||||||
exprs,
|
std::ptr::write(
|
||||||
path: path.to_owned(),
|
inner as *mut AstInner<()>,
|
||||||
symbols: (),
|
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)
|
std::ptr::addr_of_mut!((*inner).symbols)
|
||||||
.as_mut_ptr()
|
.as_mut_ptr()
|
||||||
.copy_from_nonoverlapping(syms.as_ptr(), syms.len());
|
.copy_from_nonoverlapping(syms.as_ptr(), syms.len());
|
||||||
|
|
||||||
NonNull::new_unchecked(inner)
|
NonNull::new_unchecked(inner)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1036,17 +1037,44 @@ impl Ast {
|
||||||
unsafe { &*self.inner().exprs }
|
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]> {
|
fn inner(&self) -> &AstInner<[Symbol]> {
|
||||||
unsafe { self.0.as_ref() }
|
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 {}
|
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)]
|
#[derive(Default)]
|
||||||
pub struct Arena<'a> {
|
pub struct Arena<'a> {
|
||||||
chunk: UnsafeCell<ArenaChunk>,
|
chunk: UnsafeCell<ArenaChunk>,
|
||||||
|
@ -1092,6 +1128,10 @@ impl<'a> Arena<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn alloc<T>(&self, value: T) -> &'a mut T {
|
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 layout = std::alloc::Layout::new::<T>();
|
||||||
let ptr = self.alloc_low(layout);
|
let ptr = self.alloc_low(layout);
|
||||||
unsafe { ptr.cast::<T>().write(value) };
|
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] {
|
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 layout = std::alloc::Layout::array::<T>(slice.len()).unwrap();
|
||||||
let ptr = self.alloc_low(layout);
|
let ptr = self.alloc_low(layout);
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -1109,23 +1153,6 @@ impl<'a> Arena<'a> {
|
||||||
unsafe { std::slice::from_raw_parts_mut(ptr.as_ptr() as _, slice.len()) }
|
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> {
|
fn alloc_low(&self, layout: std::alloc::Layout) -> NonNull<u8> {
|
||||||
assert!(layout.align() <= ArenaChunk::ALIGN);
|
assert!(layout.align() <= ArenaChunk::ALIGN);
|
||||||
assert!(layout.size() <= ArenaChunk::CHUNK_SIZE);
|
assert!(layout.size() <= ArenaChunk::CHUNK_SIZE);
|
||||||
|
@ -1136,10 +1163,8 @@ impl<'a> Arena<'a> {
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(prev) = ArenaChunk::reset(ArenaChunk::prev(chunk.base)) {
|
unsafe {
|
||||||
*chunk = prev;
|
std::ptr::write(chunk, ArenaChunk::new(chunk.base));
|
||||||
} else {
|
|
||||||
*chunk = ArenaChunk::new(chunk.base);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
chunk.alloc(layout).unwrap()
|
chunk.alloc(layout).unwrap()
|
||||||
|
@ -1164,18 +1189,13 @@ impl ArenaChunk {
|
||||||
const CHUNK_SIZE: usize = 1 << 16;
|
const CHUNK_SIZE: usize = 1 << 16;
|
||||||
const ALIGN: usize = std::mem::align_of::<Self>();
|
const ALIGN: usize = std::mem::align_of::<Self>();
|
||||||
const NEXT_OFFSET: usize = Self::CHUNK_SIZE - std::mem::size_of::<*mut u8>();
|
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 =
|
const LAYOUT: std::alloc::Layout =
|
||||||
unsafe { std::alloc::Layout::from_size_align_unchecked(Self::CHUNK_SIZE, Self::ALIGN) };
|
unsafe { std::alloc::Layout::from_size_align_unchecked(Self::CHUNK_SIZE, Self::ALIGN) };
|
||||||
|
|
||||||
fn new(next: *mut u8) -> Self {
|
fn new(next: *mut u8) -> Self {
|
||||||
let base = unsafe { std::alloc::alloc(Self::LAYOUT) };
|
let base = unsafe { std::alloc::alloc(Self::LAYOUT) };
|
||||||
let end = unsafe { base.add(Self::PREV_OFFSET) };
|
let end = unsafe { base.add(Self::NEXT_OFFSET) };
|
||||||
if !next.is_null() {
|
|
||||||
Self::set_prev(next, base);
|
|
||||||
}
|
|
||||||
Self::set_next(base, next);
|
Self::set_next(base, next);
|
||||||
Self::set_prev(base, std::ptr::null_mut());
|
|
||||||
Self { base, end }
|
Self { base, end }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1183,21 +1203,10 @@ impl ArenaChunk {
|
||||||
unsafe { std::ptr::write(curr.add(Self::NEXT_OFFSET) as *mut _, next) };
|
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 {
|
fn next(curr: *mut u8) -> *mut u8 {
|
||||||
unsafe { std::ptr::read(curr.add(Self::NEXT_OFFSET) as *mut _) }
|
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> {
|
fn reset(prev: *mut u8) -> Option<Self> {
|
||||||
if prev.is_null() {
|
if prev.is_null() {
|
||||||
return None;
|
return None;
|
||||||
|
@ -1218,19 +1227,17 @@ impl ArenaChunk {
|
||||||
unsafe { self.end = self.end.sub(size) };
|
unsafe { self.end = self.end.sub(size) };
|
||||||
unsafe { Some(NonNull::new_unchecked(self.end)) }
|
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 {
|
impl Drop for ArenaChunk {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
let mut current = self.base;
|
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() {
|
while !current.is_null() {
|
||||||
let next = Self::next(current);
|
let next = Self::next(current);
|
||||||
unsafe { std::alloc::dealloc(current, Self::LAYOUT) };
|
unsafe { std::alloc::dealloc(current, Self::LAYOUT) };
|
||||||
|
@ -1241,16 +1248,14 @@ impl Drop for ArenaChunk {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
fn parse(input: &'static str, output: &mut String) {
|
fn parse(input: &'static str, output: &mut String) {
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
let mut arena = super::Arena::default();
|
let arena = super::Arena::default();
|
||||||
let mut symbols = Vec::new();
|
let mut symbols = Vec::new();
|
||||||
let mut parser = super::Parser::new(&arena, &mut symbols, &super::no_loader);
|
let mut parser = super::Parser::new(&arena, &mut symbols, &super::no_loader);
|
||||||
for expr in parser.file(input, "test") {
|
for expr in parser.file(input, "test") {
|
||||||
writeln!(output, "{}", expr).unwrap();
|
writeln!(output, "{}", expr).unwrap();
|
||||||
}
|
}
|
||||||
arena.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
crate::run_tests! { parse:
|
crate::run_tests! { parse:
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
ev: Ecall
|
ev: Ecall
|
||||||
code size: 217
|
code size: 204
|
||||||
ret: 0
|
ret: 0
|
||||||
status: Ok(())
|
status: Ok(())
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
code size: 657
|
code size: 569
|
||||||
ret: 0
|
ret: 0
|
||||||
status: Ok(())
|
status: Ok(())
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
code size: 308
|
code size: 314
|
||||||
ret: 44
|
ret: 33
|
||||||
status: Ok(())
|
status: Ok(())
|
||||||
|
|
3
hblang/tests/codegen_tests_global_variables.txt
Normal file
3
hblang/tests/codegen_tests_global_variables.txt
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
code size: 305
|
||||||
|
ret: 50
|
||||||
|
status: Ok(())
|
|
@ -1,3 +1,3 @@
|
||||||
code size: 281
|
code size: 287
|
||||||
ret: 16
|
ret: 55
|
||||||
status: Ok(())
|
status: Ok(())
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
code size: 379
|
code size: 366
|
||||||
ret: 0
|
ret: 0
|
||||||
status: Ok(())
|
status: Ok(())
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
code size: 461
|
code size: 464
|
||||||
ret: 3
|
ret: 3
|
||||||
status: Ok(())
|
status: Ok(())
|
||||||
|
|
|
@ -78,3 +78,37 @@ impl From<StoreError> for crate::VmRunError {
|
||||||
Self::StoreAccessEx(value.0)
|
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) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -54,7 +54,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
|
||||||
let mut vm = unsafe {
|
let mut vm = unsafe {
|
||||||
Vm::<_, 0>::new(
|
Vm::<_, 0>::new(
|
||||||
mem::HostMemory,
|
hbvm::mem::HostMemory,
|
||||||
Address::new(mmap.as_ptr().add(stack.len()) as u64),
|
Address::new(mmap.as_ptr().add(stack.len()) as u64),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,37 +1,5 @@
|
||||||
use std::alloc::Layout;
|
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
|
const STACK_SIZE: usize = 2; // MiB
|
||||||
type Stack = [u8; 1024 * 1024 * STACK_SIZE];
|
type Stack = [u8; 1024 * 1024 * STACK_SIZE];
|
||||||
|
|
||||||
|
|
0
tests/codegen_tests_arithmetic.txt
Normal file
0
tests/codegen_tests_arithmetic.txt
Normal file
0
tests/codegen_tests_directives.txt
Normal file
0
tests/codegen_tests_directives.txt
Normal file
0
tests/codegen_tests_example.txt
Normal file
0
tests/codegen_tests_example.txt
Normal file
Loading…
Reference in a new issue