forked from AbleOS/holey-bytes
making a little utility for computing struct layouts
This commit is contained in:
parent
c3f9e535d3
commit
a51b23187d
|
@ -85,11 +85,7 @@ fn gen_instrs(generated: &mut String) -> Result<(), Box<dyn std::error::Error>>
|
|||
}
|
||||
|
||||
'_name_list: {
|
||||
writeln!(generated, "pub const NAMES: [&str; {}] = [", instructions().count())?;
|
||||
for [_, name, _, _] in instructions() {
|
||||
writeln!(generated, " \"{}\",", name.to_lowercase())?;
|
||||
}
|
||||
writeln!(generated, "];")?;
|
||||
writeln!(generated, "pub const COUNT: u8 = {};", instructions().count())?;
|
||||
}
|
||||
|
||||
let instr = "Instr";
|
||||
|
|
|
@ -34,7 +34,7 @@ impl TryFrom<u8> for Instr {
|
|||
Err(value)
|
||||
}
|
||||
|
||||
if value < NAMES.len() as u8 {
|
||||
if value < COUNT {
|
||||
unsafe { Ok(core::mem::transmute::<u8, Instr>(value)) }
|
||||
} else {
|
||||
failed(value)
|
||||
|
@ -99,10 +99,7 @@ pub fn disasm(
|
|||
};
|
||||
|
||||
fn instr_from_byte(b: u8) -> std::io::Result<Instr> {
|
||||
if b as usize >= instrs::NAMES.len() {
|
||||
return Err(std::io::ErrorKind::InvalidData.into());
|
||||
}
|
||||
Ok(unsafe { std::mem::transmute::<u8, Instr>(b) })
|
||||
b.try_into().map_err(|_| std::io::ErrorKind::InvalidData.into())
|
||||
}
|
||||
|
||||
let mut labels = HashMap::<u32, u32>::default();
|
||||
|
|
|
@ -160,12 +160,15 @@ Ty2 := struct {
|
|||
c: int,
|
||||
}
|
||||
|
||||
useless := struct {}
|
||||
|
||||
main := fn(): int {
|
||||
// `packed` structs have no padding (all fields are alighred to 1)
|
||||
if @sizeof(packed struct {a: u8, b: u16}) != 3 {
|
||||
return 9001
|
||||
}
|
||||
|
||||
finst := Ty2.{ty: Ty.{a: 4, b: 1}, c: 3}
|
||||
finst := Ty2.{ty: .{a: 4, b: 1}, c: 3}
|
||||
inst := odher_pass(finst)
|
||||
if inst.c == 3 {
|
||||
return pass(&inst.ty)
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
--fmt - format all source files
|
||||
--fmt-current - format mentioned file
|
||||
--fmt-stdout - dont write the formatted file but print it
|
||||
--fmt-stdout - dont write the formatted file but print it
|
||||
--dump-asm - output assembly instead of raw code, (the assembly is more for debugging the compiler)
|
||||
--threads <1...> - number of threads compiler can use [default: 1]
|
||||
|
|
|
@ -8,8 +8,8 @@ use {
|
|||
parser::{
|
||||
self, find_symbol, idfl, CommentOr, CtorField, Expr, ExprRef, FileId, Pos, StructField,
|
||||
},
|
||||
ty, Field, Func, Global, LoggedMem, ParamAlloc, Reloc, Sig, Struct, SymKey, TypedReloc,
|
||||
Types,
|
||||
ty, Field, Func, Global, LoggedMem, OffsetIter, ParamAlloc, Reloc, Sig, Struct, SymKey,
|
||||
TypedReloc, Types,
|
||||
},
|
||||
core::panic,
|
||||
std::fmt::Display,
|
||||
|
@ -483,13 +483,6 @@ impl ItemCtx {
|
|||
}
|
||||
|
||||
fn emit(&mut self, (len, instr): (usize, [u8; instrs::MAX_SIZE])) {
|
||||
let name = instrs::NAMES[instr[0] as usize];
|
||||
log::trc!(
|
||||
"{:08x}: {}: {}",
|
||||
self.code.len(),
|
||||
name,
|
||||
instr.iter().take(len).skip(1).map(|b| format!("{:02x}", b)).collect::<String>()
|
||||
);
|
||||
self.code.extend_from_slice(&instr[..len]);
|
||||
}
|
||||
|
||||
|
@ -729,7 +722,7 @@ impl Codegen {
|
|||
.filter_map(CommentOr::or)
|
||||
.map(|sf| Field { name: sf.name.into(), ty: self.ty(&sf.ty) })
|
||||
.collect();
|
||||
self.tys.structs.push(Struct { fields, explicit_alignment });
|
||||
self.tys.structs.push(Struct { name: 0, file: 0, fields, explicit_alignment });
|
||||
self.tys.structs.len() as u32 - 1
|
||||
}
|
||||
|
||||
|
@ -1122,15 +1115,13 @@ impl Codegen {
|
|||
|
||||
match ty.expand() {
|
||||
ty::Kind::Struct(stru) => {
|
||||
let mut offset = 0;
|
||||
let sfields = self.tys.structs[stru as usize].fields.clone();
|
||||
for (sfield, field) in sfields.iter().zip(fields) {
|
||||
let mut oiter = OffsetIter::new(stru);
|
||||
for field in fields {
|
||||
let (ty, offset) = oiter.next_ty(&self.tys).unwrap();
|
||||
let loc = loc.as_ref().offset(offset);
|
||||
let ctx = Ctx::default().with_loc(loc).with_ty(sfield.ty);
|
||||
let ctx = Ctx::default().with_loc(loc).with_ty(ty);
|
||||
let value = self.expr_ctx(field, ctx)?;
|
||||
self.ci.free_loc(value.loc);
|
||||
offset += self.tys.size_of(sfield.ty);
|
||||
offset = Types::align_up(offset, self.tys.align_of(sfield.ty));
|
||||
}
|
||||
}
|
||||
ty::Kind::Slice(arr) => {
|
||||
|
@ -1944,21 +1935,14 @@ impl Codegen {
|
|||
.loc
|
||||
.or_else(|| right.take_owned())
|
||||
.unwrap_or_else(|| Loc::stack(self.ci.stack.allocate(self.tys.size_of(ty))));
|
||||
let mut offset = 0;
|
||||
for &Field { ty, .. } in self.tys.structs[stuct as usize].fields.clone().iter() {
|
||||
offset = Types::align_up(
|
||||
offset,
|
||||
self.tys.structs[stuct as usize]
|
||||
.explicit_alignment
|
||||
.unwrap_or(self.tys.align_of(ty)),
|
||||
);
|
||||
let size = self.tys.size_of(ty);
|
||||
|
||||
let mut oiter = OffsetIter::new(stuct);
|
||||
while let Some((ty, offset)) = oiter.next_ty(&self.tys) {
|
||||
let ctx = Ctx::from(Value { ty, loc: loc.as_ref().offset(offset) });
|
||||
let left = left.as_ref().offset(offset);
|
||||
let right = right.as_ref().offset(offset);
|
||||
let value = self.struct_op(op, ty, ctx, left, right)?;
|
||||
self.ci.free_loc(value.loc);
|
||||
offset += size;
|
||||
}
|
||||
|
||||
self.ci.free_loc(left);
|
||||
|
@ -2317,9 +2301,10 @@ impl Codegen {
|
|||
Reloc::pack_srel(stack, off)
|
||||
}
|
||||
|
||||
// TODO: sometimes its better to do this in bulk
|
||||
fn ty(&mut self, expr: &Expr) -> ty::Id {
|
||||
ty::Id::from(self.eval_const(expr, ty::TYPE))
|
||||
self.tys
|
||||
.ty(self.ci.file, expr, &self.files)
|
||||
.unwrap_or_else(|| ty::Id::from(self.eval_const(expr, ty::TYPE)))
|
||||
}
|
||||
|
||||
fn read_trap(addr: u64) -> Option<&'static trap::Trap> {
|
||||
|
|
|
@ -272,6 +272,7 @@ impl TokenKind {
|
|||
Self::Div => a.wrapping_div(b),
|
||||
Self::Shl => a.wrapping_shl(b as _),
|
||||
Self::Eq => (a == b) as i64,
|
||||
Self::Ne => (a != b) as i64,
|
||||
Self::Band => a & b,
|
||||
s => todo!("{s}"),
|
||||
}
|
||||
|
|
|
@ -19,12 +19,13 @@
|
|||
extract_if,
|
||||
ptr_internals
|
||||
)]
|
||||
#![allow(stable_features, internal_features, clippy::format_collect)]
|
||||
#![allow(stable_features, internal_features)]
|
||||
|
||||
use {
|
||||
self::{
|
||||
ident::Ident,
|
||||
parser::{Expr, ExprRef, FileId},
|
||||
lexer::TokenKind,
|
||||
parser::{CommentOr, Expr, ExprRef, FileId},
|
||||
son::reg,
|
||||
ty::ArrayLen,
|
||||
},
|
||||
|
@ -32,6 +33,7 @@ use {
|
|||
parser::Ast,
|
||||
std::{
|
||||
collections::{hash_map, BTreeMap, VecDeque},
|
||||
fmt::Display,
|
||||
io,
|
||||
ops::Range,
|
||||
path::{Path, PathBuf},
|
||||
|
@ -58,6 +60,7 @@ pub mod parser;
|
|||
pub mod son;
|
||||
|
||||
mod lexer;
|
||||
mod vc;
|
||||
|
||||
mod task {
|
||||
use super::Offset;
|
||||
|
@ -164,8 +167,9 @@ mod log {
|
|||
mod ty {
|
||||
use {
|
||||
crate::{
|
||||
ident,
|
||||
lexer::TokenKind,
|
||||
parser::{self, Expr},
|
||||
parser::{self},
|
||||
},
|
||||
std::{num::NonZeroU32, ops::Range},
|
||||
};
|
||||
|
@ -427,34 +431,22 @@ mod ty {
|
|||
TK::Ptr(ty) => {
|
||||
write!(f, "^{}", self.rety(self.tys.ptrs[ty as usize].base))
|
||||
}
|
||||
_ if let Some((key, _)) = self
|
||||
.tys
|
||||
.syms
|
||||
.iter()
|
||||
.find(|(sym, &ty)| sym.file < self.files.len() as u32 && ty == self.ty)
|
||||
&& let Some(name) = self.files[key.file as usize].exprs().iter().find_map(
|
||||
|expr| match expr {
|
||||
Expr::BinOp {
|
||||
left: &Expr::Ident { name, id, .. },
|
||||
op: TokenKind::Decl,
|
||||
..
|
||||
} if id == key.ident => Some(name),
|
||||
_ => None,
|
||||
},
|
||||
) =>
|
||||
{
|
||||
write!(f, "{name}")
|
||||
}
|
||||
TK::Struct(idx) => {
|
||||
let record = &self.tys.structs[idx as usize];
|
||||
write!(f, "[{idx}]{{")?;
|
||||
for (i, &super::Field { ref name, ty }) in record.fields.iter().enumerate() {
|
||||
if i != 0 {
|
||||
write!(f, ", ")?;
|
||||
if ident::is_null(record.name) {
|
||||
write!(f, "[{idx}]{{")?;
|
||||
for (i, &super::Field { ref name, ty }) in record.fields.iter().enumerate()
|
||||
{
|
||||
if i != 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
write!(f, "{name}: {}", self.rety(ty))?;
|
||||
}
|
||||
write!(f, "{name}: {}", self.rety(ty))?;
|
||||
write!(f, "}}")
|
||||
} else {
|
||||
let file = &self.files[record.file as usize];
|
||||
write!(f, "{}", file.ident_str(record.name))
|
||||
}
|
||||
write!(f, "}}")
|
||||
}
|
||||
TK::Func(idx) => write!(f, "fn{idx}"),
|
||||
TK::Global(idx) => write!(f, "global{idx}"),
|
||||
|
@ -585,6 +577,8 @@ struct Field {
|
|||
}
|
||||
|
||||
struct Struct {
|
||||
name: Ident,
|
||||
file: FileId,
|
||||
explicit_alignment: Option<u32>,
|
||||
fields: Rc<[Field]>,
|
||||
}
|
||||
|
@ -639,6 +633,53 @@ struct Types {
|
|||
const HEADER_SIZE: usize = std::mem::size_of::<AbleOsExecutableHeader>();
|
||||
|
||||
impl Types {
|
||||
/// returns none if comptime eval is required
|
||||
fn ty(&mut self, file: FileId, expr: &Expr, files: &[parser::Ast]) -> Option<ty::Id> {
|
||||
Some(match *expr {
|
||||
Expr::UnOp { op: TokenKind::Xor, val, .. } => {
|
||||
let base = self.ty(file, val, files)?;
|
||||
self.make_ptr(base)
|
||||
}
|
||||
Expr::Ident { id, .. } if ident::is_null(id) => id.into(),
|
||||
Expr::Ident { id, .. } => {
|
||||
let f = &files[file as usize];
|
||||
let (Expr::BinOp { right, .. }, name) = f.find_decl(Ok(id))? else {
|
||||
unreachable!()
|
||||
};
|
||||
let ty = self.ty(file, right, files)?;
|
||||
if let ty::Kind::Struct(s) = ty.expand() {
|
||||
self.structs[s as usize].name = name;
|
||||
}
|
||||
ty
|
||||
}
|
||||
Expr::Struct { pos, fields, packed, .. } => {
|
||||
let sym = SymKey { file, ident: pos };
|
||||
if let Some(&ty) = self.syms.get(&sym) {
|
||||
return Some(ty);
|
||||
}
|
||||
|
||||
let fields = fields
|
||||
.iter()
|
||||
.filter_map(CommentOr::or)
|
||||
.map(|sf| {
|
||||
Some(Field { name: sf.name.into(), ty: self.ty(file, &sf.ty, files)? })
|
||||
})
|
||||
.collect::<Option<_>>()?;
|
||||
self.structs.push(Struct {
|
||||
name: 0,
|
||||
file,
|
||||
fields,
|
||||
explicit_alignment: packed.then_some(1),
|
||||
});
|
||||
|
||||
let ty = ty::Kind::Struct(self.structs.len() as u32 - 1).compress();
|
||||
self.syms.insert(sym, ty);
|
||||
ty
|
||||
}
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
fn assemble(&mut self, to: &mut Vec<u8>) {
|
||||
to.extend([0u8; HEADER_SIZE]);
|
||||
|
||||
|
@ -762,14 +803,10 @@ impl Types {
|
|||
}
|
||||
|
||||
fn offset_of(&self, idx: ty::Struct, field: &str) -> Option<(Offset, ty::Id)> {
|
||||
let record = &self.structs[idx as usize];
|
||||
let until = record.fields.iter().position(|f| f.name.as_ref() == field)?;
|
||||
let mut offset = 0;
|
||||
for &Field { ty, .. } in &record.fields[..until] {
|
||||
offset = Self::align_up(offset, record.explicit_alignment.unwrap_or(self.align_of(ty)));
|
||||
offset += self.size_of(ty);
|
||||
}
|
||||
Some((offset, record.fields[until].ty))
|
||||
OffsetIter::new(idx)
|
||||
.into_iter(self)
|
||||
.find(|(f, _)| f.name.as_ref() == field)
|
||||
.map(|(f, off)| (off, f.ty))
|
||||
}
|
||||
|
||||
fn make_ptr(&mut self, base: ty::Id) -> ty::Id {
|
||||
|
@ -812,10 +849,6 @@ impl Types {
|
|||
.inner()
|
||||
}
|
||||
|
||||
fn align_up(value: Size, align: Size) -> Size {
|
||||
(value + align - 1) & !(align - 1)
|
||||
}
|
||||
|
||||
fn size_of(&self, ty: ty::Id) -> Size {
|
||||
match ty.expand() {
|
||||
ty::Kind::Ptr(_) => 8,
|
||||
|
@ -834,14 +867,9 @@ impl Types {
|
|||
}
|
||||
}
|
||||
ty::Kind::Struct(stru) => {
|
||||
let mut offset = 0u32;
|
||||
let record = &self.structs[stru as usize];
|
||||
for &Field { ty, .. } in record.fields.iter() {
|
||||
let align = record.explicit_alignment.unwrap_or(self.align_of(ty));
|
||||
offset = Self::align_up(offset, align);
|
||||
offset += self.size_of(ty);
|
||||
}
|
||||
offset
|
||||
let mut oiter = OffsetIter::new(stru);
|
||||
while oiter.next(self).is_some() {}
|
||||
oiter.offset
|
||||
}
|
||||
ty => unimplemented!("size_of: {:?}", ty),
|
||||
}
|
||||
|
@ -856,7 +884,7 @@ impl Types {
|
|||
.iter()
|
||||
.map(|&Field { ty, .. }| self.align_of(ty))
|
||||
.max()
|
||||
.unwrap()
|
||||
.unwrap_or(1)
|
||||
})
|
||||
}
|
||||
ty::Kind::Slice(arr) => {
|
||||
|
@ -878,6 +906,43 @@ impl Types {
|
|||
}
|
||||
}
|
||||
|
||||
struct OffsetIter {
|
||||
strct: ty::Struct,
|
||||
offset: Offset,
|
||||
index: usize,
|
||||
}
|
||||
|
||||
fn align_up(value: Size, align: Size) -> Size {
|
||||
(value + align - 1) & !(align - 1)
|
||||
}
|
||||
|
||||
impl OffsetIter {
|
||||
fn new(strct: ty::Struct) -> Self {
|
||||
Self { strct, offset: 0, index: 0 }
|
||||
}
|
||||
|
||||
fn next<'a>(&mut self, tys: &'a Types) -> Option<(&'a Field, Offset)> {
|
||||
let stru = &tys.structs[self.strct as usize];
|
||||
let field = stru.fields.get(self.index)?;
|
||||
self.index += 1;
|
||||
let align = stru.explicit_alignment.unwrap_or_else(|| tys.align_of(field.ty));
|
||||
|
||||
self.offset = align_up(self.offset, align);
|
||||
let off = self.offset;
|
||||
self.offset += tys.size_of(field.ty);
|
||||
Some((field, off))
|
||||
}
|
||||
|
||||
fn next_ty(&mut self, tys: &Types) -> Option<(ty::Id, Offset)> {
|
||||
let (field, off) = self.next(tys)?;
|
||||
Some((field.ty, off))
|
||||
}
|
||||
|
||||
fn into_iter(mut self, tys: &Types) -> impl Iterator<Item = (&Field, Offset)> {
|
||||
std::iter::from_fn(move || self.next(tys))
|
||||
}
|
||||
}
|
||||
|
||||
struct TaskQueue<T> {
|
||||
inner: Mutex<TaskQueueInner<T>>,
|
||||
}
|
||||
|
@ -1277,7 +1342,7 @@ fn test_run_vm(out: &[u8], output: &mut String) {
|
|||
#[derive(Default)]
|
||||
pub struct Options {
|
||||
pub fmt: bool,
|
||||
pub fmt_current: bool,
|
||||
pub fmt_stdout: bool,
|
||||
pub dump_asm: bool,
|
||||
pub extra_threads: usize,
|
||||
}
|
||||
|
@ -1328,7 +1393,7 @@ pub fn run_compiler(
|
|||
for parsed in parsed {
|
||||
format_ast(parsed)?;
|
||||
}
|
||||
} else if options.fmt_current {
|
||||
} else if options.fmt_stdout {
|
||||
let ast = parsed.into_iter().next().unwrap();
|
||||
let source = std::fs::read_to_string(&*ast.path)?;
|
||||
format_to(&ast, &source, out)?;
|
||||
|
@ -1352,6 +1417,42 @@ pub fn run_compiler(
|
|||
#[derive(Default)]
|
||||
pub struct LoggedMem {
|
||||
pub mem: hbvm::mem::HostMemory,
|
||||
op_buf: Vec<hbbytecode::Oper>,
|
||||
disp_buf: String,
|
||||
prev_instr: Option<hbbytecode::Instr>,
|
||||
}
|
||||
|
||||
impl LoggedMem {
|
||||
unsafe fn display_instr<T>(&mut self, instr: hbbytecode::Instr, addr: hbvm::mem::Address) {
|
||||
let novm: *const hbvm::Vm<Self, 0> = std::ptr::null();
|
||||
let offset = std::ptr::addr_of!((*novm).memory) as usize;
|
||||
let regs = unsafe {
|
||||
&*std::ptr::addr_of!(
|
||||
(*(((self as *mut _ as *mut u8).sub(offset)) as *const hbvm::Vm<Self, 0>))
|
||||
.registers
|
||||
)
|
||||
};
|
||||
|
||||
let mut bytes = core::slice::from_raw_parts(
|
||||
(addr.get() - 1) as *const u8,
|
||||
std::mem::size_of::<T>() + 1,
|
||||
);
|
||||
use std::fmt::Write;
|
||||
hbbytecode::parse_args(&mut bytes, instr, &mut self.op_buf).unwrap();
|
||||
debug_assert!(bytes.is_empty());
|
||||
self.disp_buf.clear();
|
||||
write!(self.disp_buf, "{:<10}", format!("{instr:?}")).unwrap();
|
||||
for (i, op) in self.op_buf.drain(..).enumerate() {
|
||||
if i != 0 {
|
||||
write!(self.disp_buf, ", ").unwrap();
|
||||
}
|
||||
write!(self.disp_buf, "{op:?}").unwrap();
|
||||
if let hbbytecode::Oper::R(r) = op {
|
||||
write!(self.disp_buf, "({})", regs[r as usize].0).unwrap()
|
||||
}
|
||||
}
|
||||
log::trc!("read-typed: {:x}: {}", addr.get(), self.disp_buf);
|
||||
}
|
||||
}
|
||||
|
||||
impl hbvm::mem::Memory for LoggedMem {
|
||||
|
@ -1362,13 +1463,9 @@ impl hbvm::mem::Memory for LoggedMem {
|
|||
count: usize,
|
||||
) -> Result<(), hbvm::mem::LoadError> {
|
||||
log::trc!(
|
||||
"load: {:x} {:?}",
|
||||
"load: {:x} {}",
|
||||
addr.get(),
|
||||
core::slice::from_raw_parts(addr.get() as *const u8, count)
|
||||
.iter()
|
||||
.rev()
|
||||
.map(|&b| format!("{b:02x}"))
|
||||
.collect::<String>()
|
||||
AsHex(core::slice::from_raw_parts(addr.get() as *const u8, count))
|
||||
);
|
||||
self.mem.load(addr, target, count)
|
||||
}
|
||||
|
@ -1379,39 +1476,38 @@ impl hbvm::mem::Memory for LoggedMem {
|
|||
source: *const u8,
|
||||
count: usize,
|
||||
) -> Result<(), hbvm::mem::StoreError> {
|
||||
log::trc!(
|
||||
"store: {:x} {:?}",
|
||||
addr.get(),
|
||||
core::slice::from_raw_parts(source, count)
|
||||
.iter()
|
||||
.rev()
|
||||
.map(|&b| format!("{b:02x}"))
|
||||
.collect::<String>()
|
||||
);
|
||||
log::trc!("store: {:x} {}", addr.get(), AsHex(core::slice::from_raw_parts(source, count)));
|
||||
self.mem.store(addr, source, count)
|
||||
}
|
||||
|
||||
unsafe fn prog_read<T: Copy>(&mut self, addr: hbvm::mem::Address) -> T {
|
||||
log::trc!(
|
||||
"read-typed: {:x} {} {:?}",
|
||||
addr.get(),
|
||||
std::any::type_name::<T>(),
|
||||
if core::mem::size_of::<T>() == 1
|
||||
&& let Some(nm) =
|
||||
instrs::NAMES.get(std::ptr::read(addr.get() as *const u8) as usize)
|
||||
{
|
||||
nm.to_string()
|
||||
unsafe fn prog_read<T: Copy + 'static>(&mut self, addr: hbvm::mem::Address) -> T {
|
||||
if log::LOG_LEVEL == log::Level::Trc {
|
||||
if std::any::TypeId::of::<u8>() == std::any::TypeId::of::<T>() {
|
||||
if let Some(instr) = self.prev_instr {
|
||||
self.display_instr::<()>(instr, addr);
|
||||
}
|
||||
self.prev_instr = hbbytecode::Instr::try_from(*(addr.get() as *const u8)).ok();
|
||||
} else {
|
||||
core::slice::from_raw_parts(addr.get() as *const u8, core::mem::size_of::<T>())
|
||||
.iter()
|
||||
.map(|&b| format!("{:02x}", b))
|
||||
.collect::<String>()
|
||||
let instr = self.prev_instr.take().unwrap();
|
||||
self.display_instr::<T>(instr, addr);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
self.mem.prog_read(addr)
|
||||
}
|
||||
}
|
||||
|
||||
struct AsHex<'a>(&'a [u8]);
|
||||
|
||||
impl Display for AsHex<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
for &b in self.0 {
|
||||
write!(f, "{b:02x}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::sync::Arc;
|
||||
|
|
|
@ -14,7 +14,7 @@ fn main() -> std::io::Result<()> {
|
|||
args.iter().filter(|a| !a.starts_with('-')).nth(1).copied().unwrap_or("main.hb"),
|
||||
hblang::Options {
|
||||
fmt: args.contains(&"--fmt"),
|
||||
fmt_current: args.contains(&"--fmt-stdout"),
|
||||
fmt_stdout: args.contains(&"--fmt-stdout"),
|
||||
dump_asm: args.contains(&"--dump-asm"),
|
||||
extra_threads: args
|
||||
.iter()
|
||||
|
|
File diff suppressed because it is too large
Load diff
286
hblang/src/vc.rs
Normal file
286
hblang/src/vc.rs
Normal file
|
@ -0,0 +1,286 @@
|
|||
use std::{
|
||||
fmt::Debug,
|
||||
mem::MaybeUninit,
|
||||
ops::{Deref, DerefMut, Not},
|
||||
ptr::Unique,
|
||||
};
|
||||
|
||||
type Nid = u16;
|
||||
|
||||
const VC_SIZE: usize = 16;
|
||||
const INLINE_ELEMS: usize = VC_SIZE / 2 - 1;
|
||||
|
||||
pub union Vc {
|
||||
inline: InlineVc,
|
||||
alloced: AllocedVc,
|
||||
}
|
||||
|
||||
impl Default for Vc {
|
||||
fn default() -> Self {
|
||||
Vc { inline: InlineVc { elems: MaybeUninit::uninit(), cap: Default::default() } }
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Vc {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.as_slice().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Vc {
|
||||
fn is_inline(&self) -> bool {
|
||||
unsafe { self.inline.cap <= INLINE_ELEMS as Nid }
|
||||
}
|
||||
|
||||
fn layout(&self) -> Option<std::alloc::Layout> {
|
||||
unsafe {
|
||||
self.is_inline()
|
||||
.not()
|
||||
.then(|| std::alloc::Layout::array::<Nid>(self.alloced.cap as _).unwrap_unchecked())
|
||||
}
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
unsafe {
|
||||
if self.is_inline() {
|
||||
self.inline.cap as _
|
||||
} else {
|
||||
self.alloced.len as _
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn len_mut(&mut self) -> &mut Nid {
|
||||
unsafe {
|
||||
if self.is_inline() {
|
||||
&mut self.inline.cap
|
||||
} else {
|
||||
&mut self.alloced.len
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn as_ptr(&self) -> *const Nid {
|
||||
unsafe {
|
||||
match self.is_inline() {
|
||||
true => self.inline.elems.as_ptr().cast(),
|
||||
false => self.alloced.base.as_ptr(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn as_mut_ptr(&mut self) -> *mut Nid {
|
||||
unsafe {
|
||||
match self.is_inline() {
|
||||
true => self.inline.elems.as_mut_ptr().cast(),
|
||||
false => self.alloced.base.as_ptr(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_slice(&self) -> &[Nid] {
|
||||
unsafe { std::slice::from_raw_parts(self.as_ptr(), self.len()) }
|
||||
}
|
||||
|
||||
fn as_slice_mut(&mut self) -> &mut [Nid] {
|
||||
unsafe { std::slice::from_raw_parts_mut(self.as_mut_ptr(), self.len()) }
|
||||
}
|
||||
|
||||
pub fn push(&mut self, value: Nid) {
|
||||
if let Some(layout) = self.layout()
|
||||
&& unsafe { self.alloced.len == self.alloced.cap }
|
||||
{
|
||||
unsafe {
|
||||
self.alloced.cap *= 2;
|
||||
self.alloced.base = Unique::new_unchecked(
|
||||
std::alloc::realloc(
|
||||
self.alloced.base.as_ptr().cast(),
|
||||
layout,
|
||||
self.alloced.cap as usize * std::mem::size_of::<Nid>(),
|
||||
)
|
||||
.cast(),
|
||||
);
|
||||
}
|
||||
} else if self.len() == INLINE_ELEMS {
|
||||
unsafe {
|
||||
let mut allcd =
|
||||
Self::alloc((self.inline.cap + 1).next_power_of_two() as _, self.len());
|
||||
std::ptr::copy_nonoverlapping(self.as_ptr(), allcd.as_mut_ptr(), self.len());
|
||||
*self = allcd;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
*self.len_mut() += 1;
|
||||
self.as_mut_ptr().add(self.len() - 1).write(value);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn alloc(cap: usize, len: usize) -> Self {
|
||||
debug_assert!(cap > INLINE_ELEMS);
|
||||
let layout = unsafe { std::alloc::Layout::array::<Nid>(cap).unwrap_unchecked() };
|
||||
let alloc = unsafe { std::alloc::alloc(layout) };
|
||||
unsafe {
|
||||
Vc {
|
||||
alloced: AllocedVc {
|
||||
base: Unique::new_unchecked(alloc.cast()),
|
||||
len: len as _,
|
||||
cap: cap as _,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn swap_remove(&mut self, index: usize) {
|
||||
let len = self.len() - 1;
|
||||
self.as_slice_mut().swap(index, len);
|
||||
*self.len_mut() -= 1;
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, index: usize) {
|
||||
self.as_slice_mut().copy_within(index + 1.., index);
|
||||
*self.len_mut() -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Vc {
|
||||
fn drop(&mut self) {
|
||||
if let Some(layout) = self.layout() {
|
||||
unsafe {
|
||||
std::alloc::dealloc(self.alloced.base.as_ptr().cast(), layout);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Vc {
|
||||
fn clone(&self) -> Self {
|
||||
self.as_slice().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for Vc {
|
||||
type IntoIter = VcIntoIter;
|
||||
type Item = Nid;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
VcIntoIter { start: 0, end: self.len(), vc: self }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VcIntoIter {
|
||||
start: usize,
|
||||
end: usize,
|
||||
vc: Vc,
|
||||
}
|
||||
|
||||
impl Iterator for VcIntoIter {
|
||||
type Item = Nid;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.start == self.end {
|
||||
return None;
|
||||
}
|
||||
|
||||
let ret = unsafe { std::ptr::read(self.vc.as_slice().get_unchecked(self.start)) };
|
||||
self.start += 1;
|
||||
Some(ret)
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let len = self.end - self.start;
|
||||
(len, Some(len))
|
||||
}
|
||||
}
|
||||
|
||||
impl DoubleEndedIterator for VcIntoIter {
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
if self.start == self.end {
|
||||
return None;
|
||||
}
|
||||
|
||||
self.end -= 1;
|
||||
Some(unsafe { std::ptr::read(self.vc.as_slice().get_unchecked(self.end)) })
|
||||
}
|
||||
}
|
||||
|
||||
impl ExactSizeIterator for VcIntoIter {}
|
||||
|
||||
impl<const SIZE: usize> From<[Nid; SIZE]> for Vc {
|
||||
fn from(value: [Nid; SIZE]) -> Self {
|
||||
value.as_slice().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a [Nid]> for Vc {
|
||||
fn from(value: &'a [Nid]) -> Self {
|
||||
if value.len() <= INLINE_ELEMS {
|
||||
let mut dflt = Self::default();
|
||||
unsafe {
|
||||
std::ptr::copy_nonoverlapping(value.as_ptr(), dflt.as_mut_ptr(), value.len())
|
||||
};
|
||||
dflt.inline.cap = value.len() as _;
|
||||
dflt
|
||||
} else {
|
||||
let mut allcd = unsafe { Self::alloc(value.len(), value.len()) };
|
||||
unsafe {
|
||||
std::ptr::copy_nonoverlapping(value.as_ptr(), allcd.as_mut_ptr(), value.len())
|
||||
};
|
||||
allcd
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Vc {
|
||||
type Target = [Nid];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Vc {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.as_slice_mut()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(C)]
|
||||
struct InlineVc {
|
||||
cap: Nid,
|
||||
elems: MaybeUninit<[Nid; INLINE_ELEMS]>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(C)]
|
||||
struct AllocedVc {
|
||||
cap: Nid,
|
||||
len: Nid,
|
||||
base: Unique<Nid>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct BitSet {
|
||||
data: Vec<usize>,
|
||||
}
|
||||
|
||||
impl BitSet {
|
||||
const ELEM_SIZE: usize = std::mem::size_of::<usize>() * 8;
|
||||
|
||||
pub fn clear(&mut self, bit_size: usize) {
|
||||
let new_len = (bit_size + Self::ELEM_SIZE - 1) / Self::ELEM_SIZE;
|
||||
self.data.clear();
|
||||
self.data.resize(new_len, 0);
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub fn set(&mut self, idx: Nid) -> bool {
|
||||
let idx = idx as usize;
|
||||
let data_idx = idx / Self::ELEM_SIZE;
|
||||
let sub_idx = idx % Self::ELEM_SIZE;
|
||||
let prev = self.data[data_idx] & (1 << sub_idx);
|
||||
self.data[data_idx] |= 1 << sub_idx;
|
||||
prev == 0
|
||||
}
|
||||
}
|
|
@ -6,8 +6,8 @@ drop:
|
|||
ADDI64 r254, r254, 16d
|
||||
JALA r0, r31, 0a
|
||||
main:
|
||||
ADDI64 r254, r254, -48d
|
||||
ST r31, r254, 8a, 40h
|
||||
ADDI64 r254, r254, -40d
|
||||
ST r31, r254, 8a, 32h
|
||||
LI64 r32, 1d
|
||||
ST r32, r254, 0a, 8h
|
||||
ADDI64 r32, r254, 0d
|
||||
|
@ -15,12 +15,11 @@ main:
|
|||
JAL r31, r0, :modify
|
||||
LD r2, r254, 0a, 8h
|
||||
JAL r31, r0, :drop
|
||||
LI64 r33, 0d
|
||||
CP r34, r32
|
||||
LD r35, r34, 0a, 8h
|
||||
ADDI64 r1, r35, -2d
|
||||
LD r31, r254, 8a, 40h
|
||||
ADDI64 r254, r254, 48d
|
||||
CP r33, r32
|
||||
LD r34, r33, 0a, 8h
|
||||
ADDI64 r1, r34, -2d
|
||||
LD r31, r254, 8a, 32h
|
||||
ADDI64 r254, r254, 40d
|
||||
JALA r0, r31, 0a
|
||||
modify:
|
||||
ADDI64 r254, r254, -32d
|
||||
|
@ -32,6 +31,6 @@ modify:
|
|||
LD r31, r254, 0a, 32h
|
||||
ADDI64 r254, r254, 32d
|
||||
JALA r0, r31, 0a
|
||||
code size: 318
|
||||
code size: 308
|
||||
ret: 0
|
||||
status: Ok(())
|
||||
|
|
|
@ -1,23 +1,28 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -72d
|
||||
ST r31, r254, 48a, 24h
|
||||
LI64 r32, 4d
|
||||
ST r32, r254, 0a, 8h
|
||||
LI64 r32, 1d
|
||||
ST r32, r254, 8a, 8h
|
||||
LI64 r32, 3d
|
||||
ST r32, r254, 16a, 8h
|
||||
LI64 r33, 3d
|
||||
JEQ r32, r33, :0
|
||||
LI64 r1, 9001d
|
||||
JMP :1
|
||||
0: LI64 r33, 4d
|
||||
ST r33, r254, 0a, 8h
|
||||
LI64 r33, 1d
|
||||
ST r33, r254, 8a, 8h
|
||||
LI64 r33, 3d
|
||||
ST r33, r254, 16a, 8h
|
||||
ADDI64 r2, r254, 0d
|
||||
ADDI64 r1, r254, 24d
|
||||
JAL r31, r0, :odher_pass
|
||||
LD r32, r254, 40a, 8h
|
||||
LI64 r33, 3d
|
||||
JNE r32, r33, :0
|
||||
ADDI64 r33, r254, 24d
|
||||
CP r2, r33
|
||||
LD r33, r254, 40a, 8h
|
||||
LI64 r32, 3d
|
||||
JNE r33, r32, :2
|
||||
ADDI64 r32, r254, 24d
|
||||
CP r2, r32
|
||||
JAL r31, r0, :pass
|
||||
JMP :1
|
||||
0: LI64 r1, 0d
|
||||
2: LI64 r1, 0d
|
||||
1: LD r31, r254, 48a, 24h
|
||||
ADDI64 r254, r254, 72d
|
||||
JALA r0, r31, 0a
|
||||
|
@ -43,6 +48,6 @@ pass:
|
|||
LD r31, r254, 0a, 40h
|
||||
ADDI64 r254, r254, 40d
|
||||
JALA r0, r31, 0a
|
||||
code size: 400
|
||||
code size: 440
|
||||
ret: 3
|
||||
status: Ok(())
|
||||
|
|
0
hblang/tests/son_tests_structs.txt
Normal file
0
hblang/tests/son_tests_structs.txt
Normal file
|
@ -35,7 +35,7 @@ pub trait Memory {
|
|||
///
|
||||
/// # Safety
|
||||
/// - Data read have to be valid
|
||||
unsafe fn prog_read<T: Copy>(&mut self, addr: Address) -> T;
|
||||
unsafe fn prog_read<T: Copy + 'static>(&mut self, addr: Address) -> T;
|
||||
}
|
||||
|
||||
/// Unhandled load access trap
|
||||
|
|
|
@ -382,7 +382,7 @@ where
|
|||
|
||||
/// Decode instruction operands
|
||||
#[inline(always)]
|
||||
unsafe fn decode<T: Copy>(&mut self) -> T {
|
||||
unsafe fn decode<T: Copy + 'static>(&mut self) -> T {
|
||||
unsafe { self.memory.prog_read::<T>(self.pc + 1_u64) }
|
||||
}
|
||||
|
||||
|
@ -446,7 +446,7 @@ where
|
|||
|
||||
/// Perform binary operation over register and immediate
|
||||
#[inline(always)]
|
||||
unsafe fn binary_op_imm<T: ValueVariant>(&mut self, op: impl Fn(T, T) -> T) {
|
||||
unsafe fn binary_op_imm<T: ValueVariant + 'static>(&mut self, op: impl Fn(T, T) -> T) {
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(packed)]
|
||||
struct OpsRRImm<I>(OpsRR, I);
|
||||
|
|
Loading…
Reference in a new issue