relaxing by optimizing the compiler

This commit is contained in:
Jakub Doka 2024-10-01 15:28:18 +02:00
parent 1ee8d464c6
commit d293e02f62
No known key found for this signature in database
GPG key ID: C6E9A89936B8C143
6 changed files with 229 additions and 110 deletions

View file

@ -4,6 +4,7 @@ members = ["hbbytecode", "hbvm", "hbxrt", "xtask", "hblang", "hbjit"]
[profile.release] [profile.release]
lto = true lto = true
debug = true
codegen-units = 1 codegen-units = 1
panic = "abort" panic = "abort"

View file

@ -10,8 +10,8 @@ use {
ty, Field, Func, Global, LoggedMem, OffsetIter, ParamAlloc, Reloc, Sig, Struct, SymKey, ty, Field, Func, Global, LoggedMem, OffsetIter, ParamAlloc, Reloc, Sig, Struct, SymKey,
TypedReloc, Types, TypedReloc, Types,
}, },
alloc::{borrow::ToOwned, boxed::Box, string::String, vec::Vec}, alloc::{boxed::Box, string::String, vec::Vec},
core::{fmt::Display, panic}, core::fmt::Display,
}; };
type Offset = u32; type Offset = u32;
@ -171,9 +171,9 @@ mod reg {
type Reg = u8; type Reg = u8;
#[cfg(feature = "std")] #[cfg(all(debug_assertions, feature = "std"))]
type Bt = std::backtrace::Backtrace; type Bt = std::backtrace::Backtrace;
#[cfg(not(feature = "std"))] #[cfg(not(all(debug_assertions, feature = "std")))]
type Bt = (); type Bt = ();
#[derive(Default, Debug)] #[derive(Default, Debug)]
@ -209,15 +209,15 @@ mod reg {
} }
} }
#[cfg(feature = "std")] #[cfg(all(debug_assertions, feature = "std"))]
impl Drop for Id { impl Drop for Id {
fn drop(&mut self) { fn drop(&mut self) {
let is_panicking = { let is_panicking = {
#[cfg(feature = "std")] #[cfg(all(debug_assertions, feature = "std"))]
{ {
std::thread::panicking() std::thread::panicking()
} }
#[cfg(not(feature = "std"))] #[cfg(not(all(debug_assertions, feature = "std")))]
{ {
false false
} }
@ -246,9 +246,9 @@ mod reg {
self.max_used = self.max_used.max(reg); self.max_used = self.max_used.max(reg);
Id( Id(
reg, reg,
#[cfg(feature = "std")] #[cfg(all(debug_assertions, feature = "std"))]
Some(std::backtrace::Backtrace::capture()), Some(std::backtrace::Backtrace::capture()),
#[cfg(not(feature = "std"))] #[cfg(not(all(debug_assertions, feature = "std")))]
Some(()), Some(()),
) )
} }
@ -883,9 +883,8 @@ impl Codegen {
self.assert_arg_count(expr.pos(), args.len(), cargs.len(), "inline function call"); self.assert_arg_count(expr.pos(), args.len(), cargs.len(), "inline function call");
if scope == self.ci.vars.len() { if scope == self.ci.vars.len() {
for ((arg, ty), carg) in for ((arg, ti), carg) in args.iter().zip(sig.args.range()).zip(cargs) {
args.iter().zip(sig.args.view(&self.tys.args).to_owned()).zip(cargs) let ty = self.tys.args[ti];
{
let loc = self.expr_ctx(arg, Ctx::default().with_ty(ty))?.loc; let loc = self.expr_ctx(arg, Ctx::default().with_ty(ty))?.loc;
self.ci.vars.push(Variable { id: carg.id, value: Value { ty, loc } }); self.ci.vars.push(Variable { id: carg.id, value: Value { ty, loc } });
} }
@ -903,9 +902,11 @@ impl Codegen {
self.ci.file = prev_file; self.ci.file = prev_file;
self.ci.ret = prev_ret; self.ci.ret = prev_ret;
for var in self.ci.vars.drain(scope..).collect::<Vec<_>>() { let mut vars = std::mem::take(&mut self.ci.vars);
for var in vars.drain(scope..) {
self.ci.free_loc(var.value.loc); self.ci.free_loc(var.value.loc);
} }
self.ci.vars = vars;
if let Some(last_ret) = self.ci.ret_relocs.last() if let Some(last_ret) = self.ci.ret_relocs.last()
&& last_ret.offset as usize == self.ci.code.len() - 5 && last_ret.offset as usize == self.ci.code.len() - 5
@ -1796,26 +1797,32 @@ impl Codegen {
id: self.ci.id, id: self.ci.id,
ret: self.ci.ret, ret: self.ci.ret,
task_base: self.ci.task_base, task_base: self.ci.task_base,
loops: self.ci.loops.clone(), ..self.pool.cis.pop().unwrap_or_default()
vars: self };
.ci ci.loops.extend(self.ci.loops.iter());
.vars ci.vars.extend(self.ci.vars.iter().map(|v| Variable {
.iter()
.map(|v| Variable {
id: v.id, id: v.id,
value: Value { ty: v.value.ty, loc: v.value.loc.as_ref() }, value: Value { ty: v.value.ty, loc: v.value.loc.as_ref() },
}) }));
.collect(), ci.stack_relocs.extend(self.ci.stack_relocs.iter());
stack_relocs: self.ci.stack_relocs.clone(), ci.ret_relocs.extend(self.ci.ret_relocs.iter());
ret_relocs: self.ci.ret_relocs.clone(), ci.loop_relocs.extend(self.ci.loop_relocs.iter());
loop_relocs: self.ci.loop_relocs.clone(),
..Default::default()
};
ci.regs.init(); ci.regs.init();
core::mem::swap(&mut self.ci, &mut ci); core::mem::swap(&mut self.ci, &mut ci);
let value = self.expr(expr).unwrap(); let value = self.expr(expr).unwrap();
self.ci.free_loc(value.loc); self.ci.free_loc(value.loc);
core::mem::swap(&mut self.ci, &mut ci); core::mem::swap(&mut self.ci, &mut ci);
ci.loops.clear();
ci.vars.clear();
ci.stack_relocs.clear();
ci.ret_relocs.clear();
ci.loop_relocs.clear();
ci.code.clear();
ci.relocs.clear();
self.pool.cis.push(ci);
value.ty value.ty
} }
@ -2118,9 +2125,11 @@ impl Codegen {
self.report(body.pos(), "expected all paths in the fucntion to return"); self.report(body.pos(), "expected all paths in the fucntion to return");
} }
for vars in self.ci.vars.drain(..).collect::<Vec<_>>() { let mut vars = std::mem::take(&mut self.ci.vars);
self.ci.free_loc(vars.value.loc); for var in vars.drain(..) {
self.ci.free_loc(var.value.loc);
} }
self.ci.vars = vars;
self.ci.finalize(); self.ci.finalize();
self.ci.emit(jala(ZERO, RET_ADDR, 0)); self.ci.emit(jala(ZERO, RET_ADDR, 0));
@ -2570,8 +2579,8 @@ impl Codegen {
let last_fn = self.tys.funcs.len(); let last_fn = self.tys.funcs.len();
self.tys.funcs.push(Default::default()); self.tys.funcs.push(Default::default());
self.tys.funcs[last_fn].code.append(&mut self.ci.code); self.tys.funcs[last_fn].code = std::mem::take(&mut self.ci.code);
self.tys.funcs[last_fn].relocs.append(&mut self.ci.relocs); self.tys.funcs[last_fn].relocs = std::mem::take(&mut self.ci.relocs);
if is_on_stack { if is_on_stack {
let size = let size =
@ -2603,7 +2612,11 @@ impl Codegen {
self.run_vm(); self.run_vm();
self.ct.vm.pc = prev_pc + self.ct.code.as_ptr() as usize; self.ct.vm.pc = prev_pc + self.ct.code.as_ptr() as usize;
self.tys.funcs.pop().unwrap(); let func = self.tys.funcs.pop().unwrap();
self.ci.code = func.code;
self.ci.code.clear();
self.ci.relocs = func.relocs;
self.ci.relocs.clear();
} }
self.pool.cis.push(core::mem::replace(&mut self.ci, prev_ci)); self.pool.cis.push(core::mem::replace(&mut self.ci, prev_ci));
@ -2634,10 +2647,8 @@ impl Codegen {
ty::Display::new(&self.tys, &self.files, ty) ty::Display::new(&self.tys, &self.files, ty)
} }
fn ast_display(&self, ast: &Expr) -> String { fn ast_display<'a>(&'a self, ast: &'a Expr<'a>) -> parser::Display<'a> {
let mut s = String::new(); parser::Display::new(&self.cfile().file, ast)
parser::Formatter::new(&self.cfile().file).fmt(ast, &mut s).unwrap();
s
} }
#[must_use] #[must_use]

View file

@ -1,7 +1,7 @@
use { use {
crate::{ crate::{
codegen, codegen,
parser::{self, Ast}, parser::{self, Ast, StackAlloc},
}, },
alloc::{string::String, vec::Vec}, alloc::{string::String, vec::Vec},
core::{fmt::Write, num::NonZeroUsize}, core::{fmt::Write, num::NonZeroUsize},
@ -288,21 +288,22 @@ pub fn parse_from_fs(extra_threads: usize, root: &str) -> io::Result<Vec<Ast>> {
Ok(id) Ok(id)
}; };
let execute_task = |(_, path): Task| { let execute_task = |stack: &mut _, (_, path): Task| {
let path = path.to_str().ok_or_else(|| { let path = path.to_str().ok_or_else(|| {
io::Error::new( io::Error::new(
io::ErrorKind::InvalidData, io::ErrorKind::InvalidData,
format!("path contains invalid characters: {}", display_rel_path(&path)), format!("path contains invalid characters: {}", display_rel_path(&path)),
) )
})?; })?;
Ok(Ast::new(path, std::fs::read_to_string(path)?, &|path, from| { Ok(Ast::new(path, std::fs::read_to_string(path)?, stack, &|path, from| {
loader(path, from).map_err(|e| e.to_string()) loader(path, from).map_err(|e| e.to_string())
})) }))
}; };
let thread = || { let thread = || {
let mut stack = StackAlloc::default();
while let Some(task @ (indx, ..)) = tasks.pop() { while let Some(task @ (indx, ..)) = tasks.pop() {
let res = execute_task(task); let res = execute_task(&mut stack, task);
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()));

View file

@ -18,7 +18,8 @@
map_try_insert, map_try_insert,
extract_if, extract_if,
ptr_internals, ptr_internals,
iter_intersperse iter_intersperse,
slice_from_ptr_range
)] )]
#![warn(clippy::dbg_macro)] #![warn(clippy::dbg_macro)]
#![allow(stable_features, internal_features)] #![allow(stable_features, internal_features)]
@ -152,9 +153,9 @@ mod ty {
Some(Self((pos << Self::LEN_BITS | len) as u32)) Some(Self((pos << Self::LEN_BITS | len) as u32))
} }
pub fn view(self, slice: &[Id]) -> &[Id] { //pub fn view(self, slice: &[Id]) -> &[Id] {
&slice[self.0 as usize >> Self::LEN_BITS..][..self.len()] // &slice[self.0 as usize >> Self::LEN_BITS..][..self.len()]
} //}
pub fn range(self) -> Range<usize> { pub fn range(self) -> Range<usize> {
let start = self.0 as usize >> Self::LEN_BITS; let start = self.0 as usize >> Self::LEN_BITS;
@ -658,16 +659,19 @@ impl IdentInterner {
#[derive(Default)] #[derive(Default)]
struct Types { struct Types {
syms: HashMap<SymKey, ty::Id>, syms: HashMap<SymKey, ty::Id>,
funcs: Vec<Func>, funcs: Vec<Func>,
args: Vec<ty::Id>, args: Vec<ty::Id>,
globals: Vec<Global>, globals: Vec<Global>,
structs: Vec<Struct>, structs: Vec<Struct>,
fields: Vec<Field>, fields: Vec<Field>,
fields_tmp: Vec<Field>,
field_names: IdentInterner, field_names: IdentInterner,
ptrs: Vec<Ptr>, ptrs: Vec<Ptr>,
arrays: Vec<Array>, arrays: Vec<Array>,
fields_tmp: Vec<Field>,
frontier_tmp: Vec<ty::Id>,
reachable_globals: Vec<ty::Global>,
reachable_funcs: Vec<ty::Func>,
} }
const HEADER_SIZE: usize = core::mem::size_of::<AbleOsExecutableHeader>(); const HEADER_SIZE: usize = core::mem::size_of::<AbleOsExecutableHeader>();
@ -756,12 +760,12 @@ impl Types {
} }
fn dump_reachable(&mut self, from: ty::Func, to: &mut Vec<u8>) -> AbleOsExecutableHeader { fn dump_reachable(&mut self, from: ty::Func, to: &mut Vec<u8>) -> AbleOsExecutableHeader {
let mut used_funcs = vec![]; debug_assert!(self.frontier_tmp.is_empty());
let mut used_globals = vec![]; debug_assert!(self.reachable_funcs.is_empty());
debug_assert!(self.reachable_globals.is_empty());
let mut frontier = vec![ty::Kind::Func(from).compress()]; self.frontier_tmp.push(ty::Kind::Func(from).compress());
while let Some(itm) = self.frontier_tmp.pop() {
while let Some(itm) = frontier.pop() {
match itm.expand() { match itm.expand() {
ty::Kind::Func(func) => { ty::Kind::Func(func) => {
let fuc = &mut self.funcs[func as usize]; let fuc = &mut self.funcs[func as usize];
@ -769,8 +773,8 @@ impl Types {
continue; continue;
} }
fuc.offset = 0; fuc.offset = 0;
used_funcs.push(func); self.reachable_funcs.push(func);
frontier.extend(fuc.relocs.iter().map(|r| r.target)); self.frontier_tmp.extend(fuc.relocs.iter().map(|r| r.target));
} }
ty::Kind::Global(glob) => { ty::Kind::Global(glob) => {
let glb = &mut self.globals[glob as usize]; let glb = &mut self.globals[glob as usize];
@ -778,13 +782,13 @@ impl Types {
continue; continue;
} }
glb.offset = 0; glb.offset = 0;
used_globals.push(glob); self.reachable_globals.push(glob);
} }
_ => unreachable!(), _ => unreachable!(),
} }
} }
for &func in &used_funcs { for &func in &self.reachable_funcs {
let fuc = &mut self.funcs[func as usize]; let fuc = &mut self.funcs[func as usize];
fuc.offset = to.len() as _; fuc.offset = to.len() as _;
to.extend(&fuc.code); to.extend(&fuc.code);
@ -792,7 +796,7 @@ impl Types {
let code_length = to.len(); let code_length = to.len();
for &global in &used_globals { for global in self.reachable_globals.drain(..) {
let global = &mut self.globals[global as usize]; let global = &mut self.globals[global as usize];
global.offset = to.len() as _; global.offset = to.len() as _;
to.extend(&global.data); to.extend(&global.data);
@ -800,7 +804,7 @@ impl Types {
let data_length = to.len() - code_length; let data_length = to.len() - code_length;
for func in used_funcs { for func in self.reachable_funcs.drain(..) {
let fuc = &self.funcs[func as usize]; let fuc = &self.funcs[func as usize];
for rel in &fuc.relocs { for rel in &fuc.relocs {
let offset = match rel.target.expand() { let offset = match rel.target.expand() {
@ -1167,9 +1171,10 @@ fn test_parse_files(ident: &'static str, input: &'static str) -> Vec<parser::Ast
.ok_or("Not Found".to_string()) .ok_or("Not Found".to_string())
}; };
let mut stack = parser::StackAlloc::default();
module_map module_map
.iter() .iter()
.map(|&(path, content)| parser::Ast::new(path, content.to_owned(), &loader)) .map(|&(path, content)| parser::Ast::new(path, content.to_owned(), &mut stack, &loader))
.collect() .collect()
} }

View file

@ -6,11 +6,13 @@ use {
alloc::{boxed::Box, string::String, vec::Vec}, alloc::{boxed::Box, string::String, vec::Vec},
core::{ core::{
cell::UnsafeCell, cell::UnsafeCell,
fmt::{self, Write}, fmt::{self},
ops::{Deref, Not}, marker::PhantomData,
ops::Deref,
ptr::NonNull, ptr::NonNull,
sync::atomic::AtomicUsize, sync::atomic::AtomicUsize,
}, },
std::intrinsics::unlikely,
}; };
pub type Pos = u32; pub type Pos = u32;
@ -58,10 +60,11 @@ struct ScopeIdent {
pub struct Parser<'a, 'b> { pub struct Parser<'a, 'b> {
path: &'b str, path: &'b str,
loader: Loader<'b>, loader: Loader<'b>,
lexer: Lexer<'b>, lexer: Lexer<'a>,
arena: &'b Arena<'a>, arena: &'b Arena<'a>,
token: Token, token: Token,
symbols: &'b mut Symbols, symbols: &'b mut Symbols,
stack: &'b mut StackAlloc,
ns_bound: usize, ns_bound: usize,
trailing_sep: bool, trailing_sep: bool,
packed: bool, packed: bool,
@ -70,7 +73,12 @@ pub struct Parser<'a, 'b> {
} }
impl<'a, 'b> Parser<'a, 'b> { impl<'a, 'b> Parser<'a, 'b> {
pub fn new(arena: &'b Arena<'a>, symbols: &'b mut Symbols, loader: Loader<'b>) -> Self { pub fn new(
arena: &'b Arena<'a>,
symbols: &'b mut Symbols,
stack: &'b mut StackAlloc,
loader: Loader<'b>,
) -> Self {
let mut lexer = Lexer::new(""); let mut lexer = Lexer::new("");
Self { Self {
loader, loader,
@ -79,6 +87,7 @@ impl<'a, 'b> Parser<'a, 'b> {
path: "", path: "",
arena, arena,
symbols, symbols,
stack,
ns_bound: 0, ns_bound: 0,
trailing_sep: false, trailing_sep: false,
packed: false, packed: false,
@ -87,7 +96,7 @@ impl<'a, 'b> Parser<'a, 'b> {
} }
} }
pub fn file(&mut self, input: &'b str, path: &'b str) -> &'a [Expr<'a>] { pub fn file(&mut self, input: &'a str, path: &'b str) -> &'a [Expr<'a>] {
self.path = path; self.path = path;
self.lexer = Lexer::new(input); self.lexer = Lexer::new(input);
self.token = self.lexer.next(); self.token = self.lexer.next();
@ -247,8 +256,8 @@ impl<'a, 'b> Parser<'a, 'b> {
(id.ident, bl) (id.ident, bl)
} }
fn move_str(&mut self, range: Token) -> &'a str { fn tok_str(&mut self, range: Token) -> &'a str {
self.arena.alloc_str(self.lexer.slice(range.range())) self.lexer.slice(range.range())
} }
fn unit_expr(&mut self) -> Expr<'a> { fn unit_expr(&mut self) -> Expr<'a> {
@ -278,7 +287,7 @@ impl<'a, 'b> Parser<'a, 'b> {
} }
T::Directive => E::Directive { T::Directive => E::Directive {
pos: pos - 1, // need to undo the directive shift pos: pos - 1, // need to undo the directive shift
name: self.move_str(token), name: self.tok_str(token),
args: { args: {
self.expect_advance(T::LParen); self.expect_advance(T::LParen);
self.collect_list(T::Comma, T::RParen, Self::expr) self.collect_list(T::Comma, T::RParen, Self::expr)
@ -287,7 +296,7 @@ impl<'a, 'b> Parser<'a, 'b> {
T::True => E::Bool { pos, value: true }, T::True => E::Bool { pos, value: true },
T::False => E::Bool { pos, value: false }, T::False => E::Bool { pos, value: false },
T::Idk => E::Idk { pos }, T::Idk => E::Idk { pos },
T::DQuote => E::String { pos, literal: self.move_str(token) }, T::DQuote => E::String { pos, literal: self.tok_str(token) },
T::Packed => { T::Packed => {
self.packed = true; self.packed = true;
let expr = self.unit_expr(); let expr = self.unit_expr();
@ -308,13 +317,13 @@ impl<'a, 'b> Parser<'a, 'b> {
self.collect_list(T::Comma, T::RBrace, |s| { self.collect_list(T::Comma, T::RBrace, |s| {
let tok = s.token; let tok = s.token;
if s.advance_if(T::Comment) { if s.advance_if(T::Comment) {
CommentOr::Comment { literal: s.move_str(tok), pos: tok.start } CommentOr::Comment { literal: s.tok_str(tok), pos: tok.start }
} else { } else {
let name = s.expect_advance(T::Ident); let name = s.expect_advance(T::Ident);
s.expect_advance(T::Colon); s.expect_advance(T::Colon);
CommentOr::Or(StructField { CommentOr::Or(StructField {
pos: name.start, pos: name.start,
name: s.move_str(name), name: s.tok_str(name),
ty: s.expr(), ty: s.expr(),
}) })
} }
@ -338,7 +347,7 @@ impl<'a, 'b> Parser<'a, 'b> {
}, },
T::Ident | T::CtIdent => { T::Ident | T::CtIdent => {
let (id, is_first) = self.resolve_ident(token); let (id, is_first) = self.resolve_ident(token);
let name = self.move_str(token); let name = self.tok_str(token);
E::Ident { pos, is_ct: token.kind == T::CtIdent, name, id, is_first } E::Ident { pos, is_ct: token.kind == T::CtIdent, name, id, is_first }
} }
T::If => E::If { T::If => E::If {
@ -369,7 +378,7 @@ impl<'a, 'b> Parser<'a, 'b> {
s.expect_advance(T::Colon); s.expect_advance(T::Colon);
Arg { Arg {
pos: name.start, pos: name.start,
name: s.move_str(name), name: s.tok_str(name),
is_ct: name.kind == T::CtIdent, is_ct: name.kind == T::CtIdent,
id, id,
ty: s.expr(), ty: s.expr(),
@ -426,7 +435,7 @@ impl<'a, 'b> Parser<'a, 'b> {
self.expect_advance(T::RParen); self.expect_advance(T::RParen);
expr expr
} }
T::Comment => Expr::Comment { pos, literal: self.move_str(token) }, T::Comment => Expr::Comment { pos, literal: self.tok_str(token) },
tok => self.report(token.start, format_args!("unexpected token: {tok:?}")), tok => self.report(token.start, format_args!("unexpected token: {tok:?}")),
}; };
@ -457,7 +466,7 @@ impl<'a, 'b> Parser<'a, 'b> {
pos: token.start, pos: token.start,
name: { name: {
let token = self.expect_advance(T::Ident); let token = self.expect_advance(T::Ident);
self.move_str(token) self.tok_str(token)
}, },
}, },
_ => break, _ => break,
@ -486,7 +495,7 @@ impl<'a, 'b> Parser<'a, 'b> {
ty: ty.map(|ty| self.arena.alloc(ty)), ty: ty.map(|ty| self.arena.alloc(ty)),
fields: self.collect_list(TokenKind::Comma, TokenKind::RBrace, |s| { fields: self.collect_list(TokenKind::Comma, TokenKind::RBrace, |s| {
let name_tok = s.advance_ident(); let name_tok = s.advance_ident();
let name = s.move_str(name_tok); let name = s.tok_str(name_tok);
CtorField { CtorField {
pos: name_tok.start, pos: name_tok.start,
name, name,
@ -538,19 +547,13 @@ impl<'a, 'b> Parser<'a, 'b> {
end: TokenKind, end: TokenKind,
mut f: impl FnMut(&mut Self) -> T, mut f: impl FnMut(&mut Self) -> T,
) -> &'a [T] { ) -> &'a [T] {
self.collect(|s| { let mut view = self.stack.view();
s.advance_if(end).not().then(|| { while !self.advance_if(end) {
let val = f(s); let val = f(self);
s.trailing_sep = s.advance_if(delim); self.trailing_sep = self.advance_if(delim);
val unsafe { self.stack.push(&mut view, val) };
})
})
} }
self.arena.alloc_slice(unsafe { self.stack.finalize(view) })
fn collect<T: Copy>(&mut self, mut f: impl FnMut(&mut Self) -> Option<T>) -> &'a [T] {
// TODO: avoid this allocation
let vec = core::iter::from_fn(|| f(self)).collect::<Vec<_>>();
self.arena.alloc_slice(&vec)
} }
fn advance_if(&mut self, kind: TokenKind) -> bool { fn advance_if(&mut self, kind: TokenKind) -> bool {
@ -931,6 +934,22 @@ impl<'a, T: Copy> CommentOr<'a, T> {
} }
} }
pub struct Display<'a> {
source: &'a str,
expr: &'a Expr<'a>,
}
impl<'a> Display<'a> {
pub fn new(source: &'a str, expr: &'a Expr<'a>) -> Self {
Self { source, expr }
}
}
impl core::fmt::Display for Display<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Formatter::new(self.source).fmt(self.expr, f)
}
}
pub struct Formatter<'a> { pub struct Formatter<'a> {
source: &'a str, source: &'a str,
depth: usize, depth: usize,
@ -942,14 +961,14 @@ impl<'a> Formatter<'a> {
Self { source, depth: 0, disp_buff: Default::default() } Self { source, depth: 0, disp_buff: Default::default() }
} }
fn fmt_list<T: Poser>( fn fmt_list<T: Poser, F: core::fmt::Write>(
&mut self, &mut self,
f: &mut String, f: &mut F,
trailing: bool, trailing: bool,
end: &str, end: &str,
sep: &str, sep: &str,
list: &[T], list: &[T],
fmt: impl Fn(&mut Self, &T, &mut String) -> fmt::Result, fmt: impl Fn(&mut Self, &T, &mut F) -> fmt::Result,
) -> fmt::Result { ) -> fmt::Result {
self.fmt_list_low(f, trailing, end, sep, list, |s, v, f| { self.fmt_list_low(f, trailing, end, sep, list, |s, v, f| {
fmt(s, v, f)?; fmt(s, v, f)?;
@ -957,14 +976,14 @@ impl<'a> Formatter<'a> {
}) })
} }
fn fmt_list_low<T: Poser>( fn fmt_list_low<T: Poser, F: core::fmt::Write>(
&mut self, &mut self,
f: &mut String, f: &mut F,
trailing: bool, trailing: bool,
end: &str, end: &str,
sep: &str, sep: &str,
list: &[T], list: &[T],
fmt: impl Fn(&mut Self, &T, &mut String) -> Result<bool, fmt::Error>, fmt: impl Fn(&mut Self, &T, &mut F) -> Result<bool, fmt::Error>,
) -> fmt::Result { ) -> fmt::Result {
if !trailing { if !trailing {
let mut first = true; let mut first = true;
@ -1013,10 +1032,10 @@ impl<'a> Formatter<'a> {
res res
} }
fn fmt_paren( fn fmt_paren<F: core::fmt::Write>(
&mut self, &mut self,
expr: &Expr, expr: &Expr,
f: &mut String, f: &mut F,
cond: impl FnOnce(&Expr) -> bool, cond: impl FnOnce(&Expr) -> bool,
) -> fmt::Result { ) -> fmt::Result {
if cond(expr) { if cond(expr) {
@ -1028,7 +1047,7 @@ impl<'a> Formatter<'a> {
} }
} }
pub fn fmt(&mut self, expr: &Expr, f: &mut String) -> fmt::Result { pub fn fmt<F: core::fmt::Write>(&mut self, expr: &Expr, f: &mut F) -> fmt::Result {
macro_rules! impl_parenter { macro_rules! impl_parenter {
($($name:ident => $pat:pat,)*) => { ($($name:ident => $pat:pat,)*) => {
$( $(
@ -1259,11 +1278,13 @@ impl AstInner<[Symbol]> {
.0 .0
} }
fn new(content: String, path: &str, loader: Loader) -> NonNull<Self> { fn new(file: Box<str>, path: &str, stack: &mut StackAlloc, loader: Loader) -> NonNull<Self> {
let arena = Arena::default(); let arena = Arena::default();
let mut syms = Vec::new(); let mut syms = Vec::new();
let mut parser = Parser::new(&arena, &mut syms, loader); let mut parser = Parser::new(&arena, &mut syms, stack, loader);
let exprs = parser.file(&content, path) as *const [Expr<'static>]; let exprs =
parser.file(unsafe { &*(&*file as *const _) }, path) as *const [Expr<'static>];
drop(parser);
syms.sort_unstable_by_key(|s| s.name); syms.sort_unstable_by_key(|s| s.name);
@ -1278,7 +1299,7 @@ impl AstInner<[Symbol]> {
mem: arena.chunk.into_inner(), mem: arena.chunk.into_inner(),
exprs, exprs,
path: path.into(), path: path.into(),
file: content.into(), file,
symbols: (), symbols: (),
}); });
core::ptr::addr_of_mut!((*inner).symbols) core::ptr::addr_of_mut!((*inner).symbols)
@ -1320,8 +1341,8 @@ pub fn report_to(
pub struct Ast(NonNull<AstInner<[Symbol]>>); pub struct Ast(NonNull<AstInner<[Symbol]>>);
impl Ast { impl Ast {
pub fn new(path: &str, content: String, loader: Loader) -> Self { pub fn new(path: &str, content: String, stack: &mut StackAlloc, loader: Loader) -> Self {
Self(AstInner::new(content, path, loader)) Self(AstInner::new(content.into(), path, stack, loader))
} }
pub fn exprs(&self) -> &[Expr] { pub fn exprs(&self) -> &[Expr] {
@ -1346,7 +1367,7 @@ impl Ast {
impl Default for Ast { impl Default for Ast {
fn default() -> Self { fn default() -> Self {
Self(AstInner::new(String::new(), "", &no_loader)) Self(AstInner::new("".into(), "", &mut StackAlloc::default(), &no_loader))
} }
} }
@ -1392,7 +1413,10 @@ impl Drop for Ast {
fn drop(&mut self) { fn drop(&mut self) {
let inner = unsafe { self.0.as_ref() }; let inner = unsafe { self.0.as_ref() };
if inner.ref_count.fetch_sub(1, core::sync::atomic::Ordering::Relaxed) == 1 { if inner.ref_count.fetch_sub(1, core::sync::atomic::Ordering::Relaxed) == 1 {
let layout = AstInner::layout(inner.symbols.len()); let inner = unsafe { self.0.as_mut() };
let len = inner.symbols.len();
unsafe { core::ptr::drop_in_place(inner) };
let layout = AstInner::layout(len);
unsafe { unsafe {
alloc::alloc::dealloc(self.0.as_ptr() as _, layout); alloc::alloc::dealloc(self.0.as_ptr() as _, layout);
} }
@ -1408,6 +1432,84 @@ impl Deref for Ast {
} }
} }
pub struct StackAllocView<T> {
prev: usize,
base: usize,
_ph: PhantomData<T>,
}
pub struct StackAlloc {
data: *mut u8,
len: usize,
cap: usize,
}
impl StackAlloc {
const MAX_ALIGN: usize = 16;
fn view<T: Copy>(&mut self) -> StackAllocView<T> {
let prev = self.len;
let align = core::mem::align_of::<T>();
assert!(align <= Self::MAX_ALIGN);
self.len = (self.len + align - 1) & !(align - 1);
StackAllocView { base: self.len, prev, _ph: PhantomData }
}
unsafe fn push<T: Copy>(&mut self, _view: &mut StackAllocView<T>, value: T) {
if unlikely(self.len + core::mem::size_of::<T>() > self.cap) {
let next_cap = self.cap.max(16 * 32).max(std::mem::size_of::<T>()) * 2;
if self.cap == 0 {
let layout =
core::alloc::Layout::from_size_align_unchecked(next_cap, Self::MAX_ALIGN);
self.data = alloc::alloc::alloc(layout);
} else {
let old_layout =
core::alloc::Layout::from_size_align_unchecked(self.cap, Self::MAX_ALIGN);
self.data = alloc::alloc::realloc(self.data, old_layout, next_cap);
}
self.cap = next_cap;
}
let dst = self.data.add(self.len) as *mut T;
debug_assert!(
dst.is_aligned(),
"{:?} {:?} {} {} {}",
dst,
std::mem::size_of::<T>(),
std::mem::align_of::<T>(),
self.len,
self.cap
);
self.len += core::mem::size_of::<T>();
core::ptr::write(dst, value);
}
unsafe fn finalize<T: Copy>(&mut self, view: StackAllocView<T>) -> &[T] {
if unlikely(self.cap == 0) {
return &[];
}
let slice = core::slice::from_ptr_range(
self.data.add(view.base) as *const T..self.data.add(self.len) as *const T,
);
self.len = view.prev;
slice
}
}
impl Default for StackAlloc {
fn default() -> Self {
Self { data: core::ptr::null_mut(), len: 0, cap: 0 }
}
}
impl Drop for StackAlloc {
fn drop(&mut self) {
let layout =
unsafe { core::alloc::Layout::from_size_align_unchecked(self.cap, Self::MAX_ALIGN) };
unsafe { alloc::alloc::dealloc(self.data, layout) };
}
}
#[derive(Default)] #[derive(Default)]
pub struct Arena<'a> { pub struct Arena<'a> {
chunk: UnsafeCell<ArenaChunk>, chunk: UnsafeCell<ArenaChunk>,
@ -1529,10 +1631,11 @@ impl Drop for ArenaChunk {
#[cfg(test)] #[cfg(test)]
pub mod test { pub mod test {
use {alloc::borrow::ToOwned, std::string::String}; use {crate::parser::StackAlloc, alloc::borrow::ToOwned, std::string::String};
pub fn format(ident: &str, input: &str) { pub fn format(ident: &str, input: &str) {
let ast = super::Ast::new(ident, input.to_owned(), &|_, _| Ok(0)); let ast =
super::Ast::new(ident, input.to_owned(), &mut StackAlloc::default(), &|_, _| Ok(0));
let mut output = String::new(); let mut output = String::new();
crate::fs::format_to(&ast, input, &mut output).unwrap(); crate::fs::format_to(&ast, input, &mut output).unwrap();

View file

@ -2107,10 +2107,8 @@ impl Codegen {
ty::Display::new(&self.tys, &self.files, ty) ty::Display::new(&self.tys, &self.files, ty)
} }
fn ast_display(&self, ast: &Expr) -> String { fn ast_display<'a>(&'a self, ast: &'a Expr<'a>) -> parser::Display<'a> {
let mut s = String::new(); parser::Display::new(&self.cfile().file, ast)
parser::Formatter::new(&self.cfile().file).fmt(ast, &mut s).unwrap();
s
} }
#[must_use] #[must_use]