binary no longer contains comptime code and inoptimized impl is grately simplified

This commit is contained in:
mlokr 2024-09-13 18:22:27 +02:00
parent 39c4526797
commit fbdabd8314
No known key found for this signature in database
GPG key ID: DEA147DDEE644993
4 changed files with 523 additions and 1292 deletions

File diff suppressed because it is too large Load diff

View file

@ -17,12 +17,13 @@
extract_if, extract_if,
ptr_internals ptr_internals
)] )]
#![allow(internal_features, clippy::format_collect, dead_code)] #![allow(internal_features, clippy::format_collect)]
use { use {
self::{ self::{
ident::Ident, ident::Ident,
parser::{ExprRef, FileId}, parser::{Expr, ExprRef, FileId},
son::reg,
ty::ArrayLen, ty::ArrayLen,
}, },
parser::Ast, parser::Ast,
@ -53,6 +54,108 @@ pub mod son;
mod instrs; mod instrs;
mod lexer; mod lexer;
mod task {
use super::Offset;
pub fn unpack(offset: Offset) -> Result<Offset, usize> {
if offset >> 31 != 0 {
Err((offset & !(1 << 31)) as usize)
} else {
Ok(offset)
}
}
pub fn is_done(offset: Offset) -> bool {
unpack(offset).is_ok()
}
pub fn id(index: usize) -> Offset {
1 << 31 | index as u32
}
}
mod ident {
pub type Ident = u32;
const LEN_BITS: u32 = 6;
pub fn len(ident: u32) -> u32 {
ident & ((1 << LEN_BITS) - 1)
}
pub fn is_null(ident: u32) -> bool {
(ident >> LEN_BITS) == 0
}
pub fn pos(ident: u32) -> u32 {
(ident >> LEN_BITS).saturating_sub(1)
}
pub fn new(pos: u32, len: u32) -> u32 {
debug_assert!(len < (1 << LEN_BITS));
((pos + 1) << LEN_BITS) | len
}
pub fn range(ident: u32) -> std::ops::Range<usize> {
let (len, pos) = (len(ident) as usize, pos(ident) as usize);
pos..pos + len
}
}
mod log {
#![allow(unused_macros)]
#[derive(PartialOrd, PartialEq, Ord, Eq, Debug)]
pub enum Level {
Err,
Wrn,
Inf,
Dbg,
Trc,
}
pub const LOG_LEVEL: Level = match option_env!("LOG_LEVEL") {
Some(val) => match val.as_bytes()[0] {
b'e' => Level::Err,
b'w' => Level::Wrn,
b'i' => Level::Inf,
b'd' => Level::Dbg,
b't' => Level::Trc,
_ => panic!("Invalid log level."),
},
None => {
if cfg!(debug_assertions) {
Level::Dbg
} else {
Level::Err
}
}
};
macro_rules! log {
($level:expr, $fmt:literal $($expr:tt)*) => {
if $level <= $crate::log::LOG_LEVEL {
eprintln!("{:?}: {}", $level, format_args!($fmt $($expr)*));
}
};
($level:expr, $($arg:expr),+) => {
if $level <= $crate::log::LOG_LEVEL {
$(eprintln!("[{}:{}:{}][{:?}]: {} = {:?}", line!(), column!(), file!(), $level, stringify!($arg), $arg);)*
}
};
}
macro_rules! err { ($($arg:tt)*) => { $crate::log::log!($crate::log::Level::Err, $($arg)*) }; }
macro_rules! wrn { ($($arg:tt)*) => { $crate::log::log!($crate::log::Level::Wrn, $($arg)*) }; }
macro_rules! inf { ($($arg:tt)*) => { $crate::log::log!($crate::log::Level::Inf, $($arg)*) }; }
macro_rules! dbg { ($($arg:tt)*) => { $crate::log::log!($crate::log::Level::Dbg, $($arg)*) }; }
macro_rules! trc { ($($arg:tt)*) => { $crate::log::log!($crate::log::Level::Trc, $($arg)*) }; }
#[allow(unused_imports)]
pub(crate) use {dbg, err, inf, log, trc, wrn};
}
mod ty { mod ty {
use { use {
crate::{ crate::{
@ -70,7 +173,6 @@ mod ty {
pub type Func = u32; pub type Func = u32;
pub type Global = u32; pub type Global = u32;
pub type Module = u32; pub type Module = u32;
pub type Param = u32;
pub type Slice = u32; pub type Slice = u32;
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
@ -102,10 +204,6 @@ mod ty {
self.0 as usize & Self::LEN_MASK self.0 as usize & Self::LEN_MASK
} }
pub fn is_empty(self) -> bool {
self.0 == 0
}
pub fn empty() -> Self { pub fn empty() -> Self {
Self(0) Self(0)
} }
@ -435,8 +533,8 @@ impl Default for Global {
ty: Default::default(), ty: Default::default(),
offset: u32::MAX, offset: u32::MAX,
data: Default::default(), data: Default::default(),
file: 0, file: u32::MAX,
name: 0, name: u32::MAX,
} }
} }
} }
@ -511,7 +609,99 @@ struct Types {
arrays: Vec<Array>, arrays: Vec<Array>,
} }
fn emit(out: &mut Vec<u8>, (len, instr): (usize, [u8; instrs::MAX_SIZE])) {
out.extend_from_slice(&instr[..len]);
}
impl Types { impl Types {
fn assemble(&mut self, to: &mut Vec<u8>) {
emit(to, instrs::jal(reg::RET_ADDR, reg::ZERO, 0));
emit(to, instrs::tx());
self.dump_reachable(0, to);
Reloc::new(0, 3, 4).apply_jump(to, self.funcs[0].offset, 0);
}
fn dump_reachable(&mut self, from: ty::Func, to: &mut Vec<u8>) {
let mut frontier = vec![ty::Kind::Func(from).compress()];
while let Some(itm) = frontier.pop() {
match itm.expand() {
ty::Kind::Func(func) => {
let fuc = &mut self.funcs[func as usize];
if task::is_done(fuc.offset) {
continue;
}
fuc.offset = to.len() as _;
to.extend(&fuc.code);
frontier.extend(fuc.relocs.iter().map(|r| r.target));
}
ty::Kind::Global(glob) => {
let glb = &mut self.globals[glob as usize];
if task::is_done(glb.offset) {
continue;
}
glb.offset = to.len() as _;
to.extend(&glb.data);
}
_ => unreachable!(),
}
}
for fuc in &self.funcs {
if !task::is_done(fuc.offset) {
continue;
}
for rel in &fuc.relocs {
let offset = match rel.target.expand() {
ty::Kind::Func(fun) => self.funcs[fun as usize].offset,
ty::Kind::Global(glo) => self.globals[glo as usize].offset,
_ => unreachable!(),
};
rel.reloc.apply_jump(to, offset, fuc.offset);
}
}
}
pub fn disasm(
&self,
mut sluce: &[u8],
files: &[parser::Ast],
output: &mut impl std::io::Write,
eca_handler: impl FnMut(&mut &[u8]),
) -> std::io::Result<()> {
use crate::DisasmItem;
let functions = self
.funcs
.iter()
.filter(|f| task::is_done(f.offset))
.map(|f| {
let name = if f.file != u32::MAX {
let file = &files[f.file as usize];
let Expr::BinOp { left: &Expr::Ident { name, .. }, .. } =
f.expr.get(file).unwrap()
else {
unreachable!()
};
name
} else {
"target_fn"
};
(f.offset, (name, f.code.len() as u32, DisasmItem::Func))
})
.chain(self.globals.iter().filter(|g| task::is_done(g.offset)).map(|g| {
let name = if g.file == u32::MAX {
std::str::from_utf8(&g.data).unwrap()
} else {
let file = &files[g.file as usize];
file.ident_str(g.name)
};
(g.offset, (name, g.data.len() as Size, DisasmItem::Global))
}))
.collect::<BTreeMap<_, _>>();
crate::disasm(&mut sluce, &functions, output, eca_handler)
}
fn parama(&self, ret: impl Into<ty::Id>) -> ParamAlloc { fn parama(&self, ret: impl Into<ty::Id>) -> ParamAlloc {
ParamAlloc(2 + (9..=16).contains(&self.size_of(ret.into())) as u8..12) ParamAlloc(2 + (9..=16).contains(&self.size_of(ret.into())) as u8..12)
} }
@ -622,88 +812,6 @@ impl Types {
} }
} }
mod ident {
pub type Ident = u32;
const LEN_BITS: u32 = 6;
pub fn len(ident: u32) -> u32 {
ident & ((1 << LEN_BITS) - 1)
}
pub fn is_null(ident: u32) -> bool {
(ident >> LEN_BITS) == 0
}
pub fn pos(ident: u32) -> u32 {
(ident >> LEN_BITS).saturating_sub(1)
}
pub fn new(pos: u32, len: u32) -> u32 {
debug_assert!(len < (1 << LEN_BITS));
((pos + 1) << LEN_BITS) | len
}
pub fn range(ident: u32) -> std::ops::Range<usize> {
let (len, pos) = (len(ident) as usize, pos(ident) as usize);
pos..pos + len
}
}
mod log {
#![allow(unused_macros)]
#[derive(PartialOrd, PartialEq, Ord, Eq, Debug)]
pub enum Level {
Err,
Wrn,
Inf,
Dbg,
Trc,
}
pub const LOG_LEVEL: Level = match option_env!("LOG_LEVEL") {
Some(val) => match val.as_bytes()[0] {
b'e' => Level::Err,
b'w' => Level::Wrn,
b'i' => Level::Inf,
b'd' => Level::Dbg,
b't' => Level::Trc,
_ => panic!("Invalid log level."),
},
None => {
if cfg!(debug_assertions) {
Level::Dbg
} else {
Level::Err
}
}
};
macro_rules! log {
($level:expr, $fmt:literal $($expr:tt)*) => {
if $level <= $crate::log::LOG_LEVEL {
eprintln!("{:?}: {}", $level, format_args!($fmt $($expr)*));
}
};
($level:expr, $($arg:expr),+) => {
if $level <= $crate::log::LOG_LEVEL {
$(eprintln!("[{}:{}:{}][{:?}]: {} = {:?}", line!(), column!(), file!(), $level, stringify!($arg), $arg);)*
}
};
}
macro_rules! err { ($($arg:tt)*) => { $crate::log::log!($crate::log::Level::Err, $($arg)*) }; }
macro_rules! wrn { ($($arg:tt)*) => { $crate::log::log!($crate::log::Level::Wrn, $($arg)*) }; }
macro_rules! inf { ($($arg:tt)*) => { $crate::log::log!($crate::log::Level::Inf, $($arg)*) }; }
macro_rules! dbg { ($($arg:tt)*) => { $crate::log::log!($crate::log::Level::Dbg, $($arg)*) }; }
macro_rules! trc { ($($arg:tt)*) => { $crate::log::log!($crate::log::Level::Trc, $($arg)*) }; }
#[allow(unused_imports)]
pub(crate) use {dbg, err, inf, log, trc, wrn};
}
#[inline] #[inline]
unsafe fn encode<T>(instr: T) -> (usize, [u8; instrs::MAX_SIZE]) { unsafe fn encode<T>(instr: T) -> (usize, [u8; instrs::MAX_SIZE]) {
let mut buf = [0; instrs::MAX_SIZE]; let mut buf = [0; instrs::MAX_SIZE];
@ -1463,7 +1571,9 @@ pub fn run_compiler(
if options.dump_asm { if options.dump_asm {
codegen.disasm(out)?; codegen.disasm(out)?;
} else { } else {
codegen.dump(out)?; let mut buf = Vec::new();
codegen.assemble(&mut buf);
out.write_all(&buf)?;
} }
} }

View file

@ -1,6 +1,5 @@
use { use {
crate::{ crate::{
codegen,
ident::{self, Ident}, ident::{self, Ident},
lexer::{self, Lexer, Token, TokenKind}, lexer::{self, Lexer, Token, TokenKind},
}, },
@ -224,7 +223,7 @@ impl<'a, 'b> Parser<'a, 'b> {
let is_ct = token.kind == TokenKind::CtIdent; let is_ct = token.kind == TokenKind::CtIdent;
let name = self.lexer.slice(token.range()); let name = self.lexer.slice(token.range());
if let Some(builtin) = codegen::ty::from_str(name) { if let Some(builtin) = crate::ty::from_str(name) {
return (builtin, 0); return (builtin, 0);
} }

View file

@ -10,12 +10,12 @@ use {
idfl::{self}, idfl::{self},
Expr, ExprRef, FileId, Pos, Expr, ExprRef, FileId, Pos,
}, },
ty, Field, Func, Reloc, Sig, Struct, SymKey, TypedReloc, Types, task, ty, Field, Func, Reloc, Sig, Struct, SymKey, TypedReloc, Types,
}, },
core::fmt, core::fmt,
std::{ std::{
cell::RefCell, cell::RefCell,
collections::{hash_map, BTreeMap}, collections::hash_map,
fmt::{Debug, Display, Write}, fmt::{Debug, Display, Write},
hash::{Hash as _, Hasher}, hash::{Hash as _, Hasher},
mem::{self, MaybeUninit}, mem::{self, MaybeUninit},
@ -331,7 +331,7 @@ impl BitSet {
type Nid = u16; type Nid = u16;
mod reg { pub mod reg {
pub const STACK_PTR: Reg = 254; pub const STACK_PTR: Reg = 254;
pub const ZERO: Reg = 0; pub const ZERO: Reg = 0;
@ -1327,7 +1327,7 @@ impl ItemCtx {
} }
fn emit(&mut self, instr: (usize, [u8; instrs::MAX_SIZE])) { fn emit(&mut self, instr: (usize, [u8; instrs::MAX_SIZE])) {
emit(&mut self.code, instr); crate::emit(&mut self.code, instr);
} }
fn free_loc(&mut self, loc: impl Into<Option<Loc>>) { fn free_loc(&mut self, loc: impl Into<Option<Loc>>) {
@ -1337,31 +1337,11 @@ impl ItemCtx {
} }
} }
fn emit(out: &mut Vec<u8>, (len, instr): (usize, [u8; instrs::MAX_SIZE])) {
out.extend_from_slice(&instr[..len]);
}
fn write_reloc(doce: &mut [u8], offset: usize, value: i64, size: u16) { fn write_reloc(doce: &mut [u8], offset: usize, value: i64, size: u16) {
let value = value.to_ne_bytes(); let value = value.to_ne_bytes();
doce[offset..offset + size as usize].copy_from_slice(&value[..size as usize]); doce[offset..offset + size as usize].copy_from_slice(&value[..size as usize]);
} }
mod task {
use super::Offset;
pub fn unpack(offset: Offset) -> Result<Offset, usize> {
if offset >> 31 != 0 {
Err((offset & !(1 << 31)) as usize)
} else {
Ok(offset)
}
}
pub fn id(index: usize) -> Offset {
1 << 31 | index as u32
}
}
struct FTask { struct FTask {
file: FileId, file: FileId,
id: ty::Func, id: ty::Func,
@ -1417,83 +1397,6 @@ impl Codegen {
self.complete_call_graph_low(); self.complete_call_graph_low();
} }
fn assemble(&mut self, to: &mut Vec<u8>) {
emit(to, instrs::jal(reg::RET_ADDR, reg::ZERO, 0));
emit(to, instrs::tx());
self.dump_reachable(0, to);
Reloc::new(0, 3, 4).apply_jump(to, self.tys.funcs[0].offset, 0);
}
fn dump_reachable(&mut self, from: ty::Func, to: &mut Vec<u8>) {
let mut frontier = vec![ty::Kind::Func(from).compress()];
while let Some(itm) = frontier.pop() {
match itm.expand() {
ty::Kind::Func(func) => {
let fuc = &mut self.tys.funcs[func as usize];
if task::unpack(fuc.offset).is_ok() {
continue;
}
fuc.offset = to.len() as _;
to.extend(&fuc.code);
frontier.extend(fuc.relocs.iter().map(|r| r.target));
}
ty::Kind::Global(glob) => {
let glb = &mut self.tys.globals[glob as usize];
if task::unpack(glb.offset).is_ok() {
continue;
}
glb.offset = to.len() as _;
to.extend(&glb.data);
}
_ => unreachable!(),
}
}
for fuc in &self.tys.funcs {
if task::unpack(fuc.offset).is_err() {
continue;
}
for rel in &fuc.relocs {
let offset = match rel.target.expand() {
ty::Kind::Func(fun) => self.tys.funcs[fun as usize].offset,
ty::Kind::Global(glo) => self.tys.globals[glo as usize].offset,
_ => unreachable!(),
};
rel.reloc.apply_jump(to, offset, fuc.offset);
}
}
}
pub fn disasm(
&mut self,
mut sluce: &[u8],
output: &mut impl std::io::Write,
) -> std::io::Result<()> {
use crate::DisasmItem;
let functions = self
.tys
.funcs
.iter()
.filter(|f| task::unpack(f.offset).is_ok())
.map(|f| {
let file = &self.files[f.file as usize];
let Expr::BinOp { left: &Expr::Ident { name, .. }, .. } = f.expr.get(file).unwrap()
else {
unreachable!()
};
(f.offset, (name, f.code.len() as u32, DisasmItem::Func))
})
.chain(self.tys.globals.iter().filter(|g| task::unpack(g.offset).is_ok()).map(|g| {
let file = &self.files[g.file as usize];
(g.offset, (file.ident_str(g.name), self.tys.size_of(g.ty), DisasmItem::Global))
}))
.collect::<BTreeMap<_, _>>();
crate::disasm(&mut sluce, &functions, output, |_| {})
}
fn make_func_reachable(&mut self, func: ty::Func) { fn make_func_reachable(&mut self, func: ty::Func) {
let fuc = &mut self.tys.funcs[func as usize]; let fuc = &mut self.tys.funcs[func as usize];
if fuc.offset == u32::MAX { if fuc.offset == u32::MAX {
@ -2556,7 +2459,10 @@ impl Codegen {
1..=8 => { 1..=8 => {
let reg = params.next(); let reg = params.next();
if i == index as usize - 1 { if i == index as usize - 1 {
emit(&mut self.ci.code, instrs::cp(node_loc!(self, expr).reg, reg)); crate::emit(
&mut self.ci.code,
instrs::cp(node_loc!(self, expr).reg, reg),
);
} }
} }
s => todo!("{s}"), s => todo!("{s}"),
@ -3142,10 +3048,10 @@ mod tests {
} }
let mut out = Vec::new(); let mut out = Vec::new();
codegen.assemble(&mut out); codegen.tys.assemble(&mut out);
let mut buf = Vec::<u8>::new(); let mut buf = Vec::<u8>::new();
let err = codegen.disasm(&out, &mut buf); let err = codegen.tys.disasm(&out, &codegen.files, &mut buf, |_| {});
output.push_str(String::from_utf8(buf).unwrap().as_str()); output.push_str(String::from_utf8(buf).unwrap().as_str());
if let Err(e) = err { if let Err(e) = err {
writeln!(output, "!!! asm is invalid: {e}").unwrap(); writeln!(output, "!!! asm is invalid: {e}").unwrap();