making functions example pass
This commit is contained in:
parent
d9aab2191b
commit
955e4a5c7a
|
@ -8,7 +8,7 @@ use {
|
|||
parser::{self, find_symbol, idfl, CtorField, Expr, ExprRef, FileId, Pos},
|
||||
HashMap,
|
||||
},
|
||||
std::{fmt::Display, ops::Range, rc::Rc, usize},
|
||||
std::{fmt::Display, ops::Range, rc::Rc},
|
||||
};
|
||||
|
||||
type Offset = u32;
|
||||
|
|
|
@ -81,6 +81,7 @@ mod log {
|
|||
Wrn,
|
||||
Inf,
|
||||
Dbg,
|
||||
Trc,
|
||||
}
|
||||
|
||||
pub const LOG_LEVEL: Level = match option_env!("LOG_LEVEL") {
|
||||
|
@ -89,6 +90,7 @@ mod log {
|
|||
b'w' => Level::Wrn,
|
||||
b'i' => Level::Inf,
|
||||
b'd' => Level::Dbg,
|
||||
b't' => Level::Trc,
|
||||
_ => panic!("Invalid log level."),
|
||||
},
|
||||
None => {
|
||||
|
@ -118,9 +120,10 @@ mod log {
|
|||
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, wrn};
|
||||
pub(crate) use {dbg, err, inf, log, trc, wrn};
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -615,6 +618,7 @@ pub fn parse_from_fs(extra_threads: usize, root: &str) -> io::Result<Vec<Ast>> {
|
|||
}
|
||||
|
||||
type HashMap<K, V> = std::collections::HashMap<K, V, std::hash::BuildHasherDefault<FnvHasher>>;
|
||||
type _HashSet<K> = std::collections::HashSet<K, std::hash::BuildHasherDefault<FnvHasher>>;
|
||||
|
||||
struct FnvHasher(u64);
|
||||
|
||||
|
@ -654,7 +658,19 @@ pub fn run_test(
|
|||
}
|
||||
|
||||
let mut output = String::new();
|
||||
test(ident, input, &mut output);
|
||||
{
|
||||
struct DumpOut<'a>(&'a mut String);
|
||||
impl Drop for DumpOut<'_> {
|
||||
fn drop(&mut self) {
|
||||
if std::thread::panicking() {
|
||||
println!("{}", self.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let dump = DumpOut(&mut output);
|
||||
test(ident, input, dump.0);
|
||||
}
|
||||
|
||||
let mut root = PathBuf::from(
|
||||
std::env::var("PT_TEST_ROOT")
|
||||
|
|
|
@ -59,7 +59,7 @@ mod reg {
|
|||
impl Drop for Id {
|
||||
fn drop(&mut self) {
|
||||
if !std::thread::panicking() && self.1 {
|
||||
unreachable!("reg id leaked: {:?}", self.0);
|
||||
panic!("reg id leaked: {:?}", self.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -450,6 +450,7 @@ impl Nodes {
|
|||
|
||||
fn clear(&mut self) {
|
||||
self.values.clear();
|
||||
self.lookup.clear();
|
||||
self.free = u32::MAX;
|
||||
}
|
||||
|
||||
|
@ -462,15 +463,15 @@ impl Nodes {
|
|||
let mut inputs = [NILL; MAX_INPUTS];
|
||||
inputs[..inps.len()].copy_from_slice(&inps);
|
||||
|
||||
if let Some(&id) = self.lookup.get(&(kind, inputs)) {
|
||||
debug_assert_eq!(self[id].kind, kind);
|
||||
if let Some(&id) = self.lookup.get(&(kind.clone(), inputs)) {
|
||||
debug_assert_eq!(&self[id].kind, &kind);
|
||||
debug_assert_eq!(self[id].inputs, inputs);
|
||||
return id;
|
||||
}
|
||||
|
||||
let id = self.add(Node {
|
||||
inputs,
|
||||
kind,
|
||||
kind: kind.clone(),
|
||||
loc: Default::default(),
|
||||
depth: u32::MAX,
|
||||
lock_rc: 0,
|
||||
|
@ -511,7 +512,7 @@ impl Nodes {
|
|||
self[inp].outputs.swap_remove(index);
|
||||
self.remove(inp);
|
||||
}
|
||||
let res = self.lookup.remove(&(self[target].kind, self[target].inputs));
|
||||
let res = self.lookup.remove(&(self[target].kind.clone(), self[target].inputs));
|
||||
debug_assert_eq!(res, Some(target));
|
||||
self.remove_low(target);
|
||||
}
|
||||
|
@ -524,6 +525,7 @@ impl Nodes {
|
|||
Kind::Return => {}
|
||||
Kind::Tuple { .. } => {}
|
||||
Kind::ConstInt { .. } => {}
|
||||
Kind::Call { .. } => {}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
@ -547,8 +549,8 @@ impl Nodes {
|
|||
}
|
||||
}
|
||||
|
||||
if let (Kind::ConstInt { value: a }, Kind::ConstInt { value: b }) =
|
||||
(self[lhs].kind, self[rhs].kind)
|
||||
if let (&Kind::ConstInt { value: a }, &Kind::ConstInt { value: b }) =
|
||||
(&self[lhs].kind, &self[rhs].kind)
|
||||
{
|
||||
return Some(self.new_node(
|
||||
self[target].ty,
|
||||
|
@ -613,7 +615,7 @@ impl Nodes {
|
|||
}
|
||||
|
||||
if changed {
|
||||
return Some(self.new_node(self[target].ty, self[target].kind, [lhs, rhs]));
|
||||
return Some(self.new_node(self[target].ty, self[target].kind.clone(), [lhs, rhs]));
|
||||
}
|
||||
|
||||
None
|
||||
|
@ -641,12 +643,13 @@ impl Nodes {
|
|||
}
|
||||
|
||||
fn modify_input(&mut self, target: Nid, inp_index: usize, with: Nid) -> Nid {
|
||||
let out = self.lookup.remove(&(self[target].kind, self[target].inputs));
|
||||
let out = self.lookup.remove(&(self[target].kind.clone(), self[target].inputs));
|
||||
debug_assert!(out == Some(target));
|
||||
debug_assert_ne!(self[target].inputs[inp_index], with);
|
||||
|
||||
self[target].inputs[inp_index] = with;
|
||||
if let Err(other) = self.lookup.try_insert((self[target].kind, self[target].inputs), target)
|
||||
if let Err(other) =
|
||||
self.lookup.try_insert((self[target].kind.clone(), self[target].inputs), target)
|
||||
{
|
||||
let rpl = *other.entry.get();
|
||||
self.replace(target, rpl);
|
||||
|
@ -722,13 +725,46 @@ impl Nodes {
|
|||
self.fmt(f, o, rcs)?;
|
||||
}
|
||||
}
|
||||
Kind::Call { func, ref args } => {
|
||||
if is_ready() {
|
||||
write!(f, "{}: call {}(", node, func)?;
|
||||
for (i, &value) in args.iter().enumerate() {
|
||||
if i != 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
self.fmt(f, value, rcs)?;
|
||||
}
|
||||
writeln!(f, ")")?;
|
||||
for &o in &self[node].outputs {
|
||||
if self.is_cfg(o) {
|
||||
self.fmt(f, o, rcs)?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
write!(f, "call{node}")?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_cfg(&self, o: Nid) -> bool {
|
||||
matches!(self[o].kind, Kind::Start | Kind::End | Kind::Return | Kind::Tuple { .. })
|
||||
matches!(
|
||||
self[o].kind,
|
||||
Kind::Start | Kind::End | Kind::Return | Kind::Tuple { .. } | Kind::Call { .. }
|
||||
)
|
||||
}
|
||||
|
||||
fn check_final_integrity(&self) {
|
||||
for slot in &self.values {
|
||||
match slot {
|
||||
PoolSlot::Value(node) => {
|
||||
debug_assert_eq!(node.lock_rc, 0);
|
||||
}
|
||||
PoolSlot::Next(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -758,7 +794,7 @@ enum PoolSlot {
|
|||
Next(u32),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
#[repr(u8)]
|
||||
pub enum Kind {
|
||||
Start,
|
||||
|
@ -767,6 +803,7 @@ pub enum Kind {
|
|||
ConstInt { value: i64 },
|
||||
Tuple { index: u32 },
|
||||
BinOp { op: lexer::TokenKind },
|
||||
Call { func: ty::Func, args: Vec<Nid> },
|
||||
}
|
||||
|
||||
impl Kind {
|
||||
|
@ -864,6 +901,12 @@ impl ItemCtx {
|
|||
fn emit(&mut self, instr: (usize, [u8; instrs::MAX_SIZE])) {
|
||||
emit(&mut self.code, instr);
|
||||
}
|
||||
|
||||
fn free_loc(&mut self, loc: impl Into<Option<Loc>>) {
|
||||
if let Some(loc) = loc.into() {
|
||||
self.regs.free(loc.reg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn emit(out: &mut Vec<u8>, (len, instr): (usize, [u8; instrs::MAX_SIZE])) {
|
||||
|
@ -922,6 +965,8 @@ struct TypedReloc {
|
|||
}
|
||||
|
||||
struct Global {
|
||||
file: FileId,
|
||||
name: Ident,
|
||||
ty: ty::Id,
|
||||
offset: Offset,
|
||||
data: Vec<u8>,
|
||||
|
@ -929,7 +974,13 @@ struct Global {
|
|||
|
||||
impl Default for Global {
|
||||
fn default() -> Self {
|
||||
Self { ty: Default::default(), offset: u32::MAX, data: Default::default() }
|
||||
Self {
|
||||
ty: Default::default(),
|
||||
offset: u32::MAX,
|
||||
data: Default::default(),
|
||||
file: 0,
|
||||
name: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -941,6 +992,25 @@ struct Reloc {
|
|||
width: u8,
|
||||
}
|
||||
|
||||
impl Reloc {
|
||||
fn new(offset: usize, sub_offset: u8, width: u8) -> Self {
|
||||
Self { offset: offset as u32, sub_offset, width }
|
||||
}
|
||||
|
||||
fn apply_jump(mut self, code: &mut [u8], to: u32, from: u32) -> i64 {
|
||||
self.offset += from;
|
||||
let offset = to as i64 - self.offset as i64;
|
||||
self.write_offset(code, offset);
|
||||
offset
|
||||
}
|
||||
|
||||
fn write_offset(&self, code: &mut [u8], offset: i64) {
|
||||
let bytes = offset.to_ne_bytes();
|
||||
let slice = &mut code[self.offset as usize + self.sub_offset as usize..];
|
||||
slice[..self.width as usize].copy_from_slice(&bytes[..self.width as usize]);
|
||||
}
|
||||
}
|
||||
|
||||
struct Field {
|
||||
name: Rc<str>,
|
||||
ty: ty::Id,
|
||||
|
@ -1131,6 +1201,11 @@ impl Ctx {
|
|||
struct Loc {
|
||||
reg: reg::Id,
|
||||
}
|
||||
impl Loc {
|
||||
fn as_ref(&self) -> Loc {
|
||||
Loc { reg: self.reg.as_ref() }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
struct GenCtx {
|
||||
|
@ -1165,7 +1240,82 @@ impl Codegen {
|
|||
self.complete_call_graph_low();
|
||||
}
|
||||
|
||||
pub fn dump_reachable(&mut self, from: ty::Func, to: &mut Vec<u8>) {}
|
||||
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::<HashMap<_, _>>();
|
||||
crate::disasm(&mut sluce, &functions, output, |_| {})
|
||||
}
|
||||
|
||||
fn make_func_reachable(&mut self, func: ty::Func) {
|
||||
let fuc = &mut self.tys.funcs[func as usize];
|
||||
|
@ -1189,19 +1339,35 @@ impl Codegen {
|
|||
}
|
||||
|
||||
fn expr_ctx(&mut self, expr: &Expr, ctx: Ctx) -> Option<Nid> {
|
||||
let msg = "i know nothing about this name gal which is vired \
|
||||
because we parsed succesfully";
|
||||
match *expr {
|
||||
Expr::Comment { .. } => Some(NILL),
|
||||
Expr::Ident { pos, id, .. } => {
|
||||
let msg = "i know nothing about this name gal which is vired\
|
||||
because we parsed succesfully";
|
||||
Some(
|
||||
self.ci
|
||||
.vars
|
||||
.iter()
|
||||
.find(|v| v.id == id)
|
||||
.unwrap_or_else(|| self.report(pos, msg))
|
||||
.value,
|
||||
)
|
||||
Expr::Ident { pos, id, .. } => Some(
|
||||
self.ci
|
||||
.vars
|
||||
.iter()
|
||||
.find(|v| v.id == id)
|
||||
.unwrap_or_else(|| self.report(pos, msg))
|
||||
.value,
|
||||
),
|
||||
Expr::BinOp { left: &Expr::Ident { id, .. }, op: TokenKind::Decl, right } => {
|
||||
let value = self.expr(right)?;
|
||||
self.ci.nodes.lock(value);
|
||||
self.ci.vars.push(Variable { id, value });
|
||||
Some(NILL)
|
||||
}
|
||||
Expr::BinOp { left: &Expr::Ident { id, pos, .. }, op: TokenKind::Assign, right } => {
|
||||
let value = self.expr(right)?;
|
||||
self.ci.nodes.lock(value);
|
||||
|
||||
let Some(var) = self.ci.vars.iter_mut().find(|v| v.id == id) else {
|
||||
self.report(pos, msg);
|
||||
};
|
||||
|
||||
let prev = std::mem::replace(&mut var.value, value);
|
||||
self.ci.nodes.unlock_free(prev);
|
||||
Some(NILL)
|
||||
}
|
||||
Expr::BinOp { left, op, right } => {
|
||||
let lhs = self.expr_ctx(left, ctx)?;
|
||||
|
@ -1214,6 +1380,54 @@ impl Codegen {
|
|||
self.ci.nodes.new_node(ty::bin_ret(ty, op), Kind::BinOp { op }, [lhs, rhs]);
|
||||
Some(id)
|
||||
}
|
||||
Expr::Call { func: &Expr::Ident { pos, id, name, .. }, args, .. } => {
|
||||
let func = self.find_or_declare(pos, self.ci.file, Some(id), name);
|
||||
let ty::Kind::Func(func) = func else {
|
||||
self.report(
|
||||
pos,
|
||||
format_args!(
|
||||
"compiler cant (yet) call '{}'",
|
||||
self.ty_display(func.compress())
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
let fuc = &self.tys.funcs[func as usize];
|
||||
let sig = fuc.sig.expect("TODO: generic functions");
|
||||
let ast = self.files[fuc.file as usize].clone();
|
||||
let Expr::BinOp { right: &Expr::Closure { args: cargs, .. }, .. } =
|
||||
fuc.expr.get(&ast).unwrap()
|
||||
else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
if args.len() != cargs.len() {
|
||||
let s = if cargs.len() == 1 { "" } else { "s" };
|
||||
self.report(
|
||||
pos,
|
||||
format_args!(
|
||||
"expected {} function argumenr{s}, got {}",
|
||||
cargs.len(),
|
||||
args.len()
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
let mut inps = vec![];
|
||||
for ((arg, _carg), tyx) in args.iter().zip(cargs).zip(sig.args.range()) {
|
||||
let ty = self.tys.args[tyx];
|
||||
let value = self.expr_ctx(arg, Ctx::default().with_ty(ty))?;
|
||||
_ = self.assert_ty(arg.pos(), self.tof(value), ty, true);
|
||||
inps.push(value);
|
||||
}
|
||||
self.ci.cfg =
|
||||
self.ci
|
||||
.nodes
|
||||
.new_node(sig.ret, Kind::Call { func, args: inps.clone() }, [self.ci.cfg]);
|
||||
self.ci.nodes.add_deps(self.ci.cfg, &inps);
|
||||
|
||||
Some(self.ci.cfg)
|
||||
}
|
||||
Expr::Return { pos, val } => {
|
||||
let ty = if let Some(val) = val {
|
||||
let value = self.expr_ctx(val, Ctx { ty: self.ci.ret })?;
|
||||
|
@ -1249,7 +1463,7 @@ impl Codegen {
|
|||
ret
|
||||
}
|
||||
Expr::Number { value, .. } => Some(self.ci.nodes.new_node(
|
||||
ctx.ty.unwrap_or(ty::UINT.into()),
|
||||
ctx.ty.unwrap_or(ty::INT.into()),
|
||||
Kind::ConstInt { value },
|
||||
[],
|
||||
)),
|
||||
|
@ -1325,6 +1539,13 @@ impl Codegen {
|
|||
self.ci.nodes.unlock(var.value);
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
self.ci.nodes.check_final_integrity();
|
||||
}
|
||||
|
||||
log::trc!("{}", self.ci.nodes);
|
||||
|
||||
'_open_function: {
|
||||
self.ci.emit(instrs::addi64(reg::STACK_PTR, reg::STACK_PTR, 0));
|
||||
self.ci.emit(instrs::st(reg::RET_ADDR, reg::STACK_PTR, 0, 0));
|
||||
|
@ -1332,16 +1553,22 @@ impl Codegen {
|
|||
|
||||
self.ci.regs.init();
|
||||
|
||||
let mut params = self.tys.parama(sig.ret);
|
||||
for var in self.ci.vars.drain(..) {
|
||||
match self.tys.size_of(self.ci.nodes[var.value].ty) {
|
||||
0 => {}
|
||||
1..=8 => {
|
||||
let reg = self.ci.regs.allocate();
|
||||
emit(&mut self.ci.code, instrs::cp(reg.get(), params.next()));
|
||||
self.ci.nodes[var.value].loc = Loc { reg };
|
||||
'_copy_args: {
|
||||
let mut params = self.tys.parama(sig.ret);
|
||||
for var in self.ci.vars.drain(..) {
|
||||
if self.ci.nodes[var.value].outputs.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
match self.tys.size_of(self.ci.nodes[var.value].ty) {
|
||||
0 => {}
|
||||
1..=8 => {
|
||||
let reg = self.ci.regs.allocate();
|
||||
emit(&mut self.ci.code, instrs::cp(reg.get(), params.next()));
|
||||
self.ci.nodes[var.value].loc = Loc { reg };
|
||||
}
|
||||
s => todo!("{s}"),
|
||||
}
|
||||
s => todo!("{s}"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1357,36 +1584,87 @@ impl Codegen {
|
|||
|
||||
self.ci.emit(instrs::ld(reg::RET_ADDR, reg::STACK_PTR, stack as _, pushed as _));
|
||||
self.ci.emit(instrs::addi64(reg::STACK_PTR, reg::STACK_PTR, (pushed + stack) as _));
|
||||
self.ci.emit(instrs::jala(reg::ZERO, reg::RET_ADDR, 0));
|
||||
}
|
||||
|
||||
self.tys.funcs[id as usize].code.append(&mut self.ci.code);
|
||||
self.tys.funcs[id as usize].relocs.append(&mut self.ci.relocs);
|
||||
self.ci.nodes.clear();
|
||||
self.pool.cis.push(std::mem::replace(&mut self.ci, prev_ci));
|
||||
}
|
||||
|
||||
fn emit_control(&mut self, ctrl: Nid) {
|
||||
match self.ci.nodes[ctrl].kind {
|
||||
Kind::Start => unreachable!(),
|
||||
Kind::End => unreachable!(),
|
||||
Kind::Return => {
|
||||
let ret_loc = match self.tys.size_of(self.ci.ret.expect("TODO")) {
|
||||
0 => Loc::default(),
|
||||
1..=8 => Loc { reg: 1u8.into() },
|
||||
s => todo!("{s}"),
|
||||
};
|
||||
fn emit_control(&mut self, mut ctrl: Nid) {
|
||||
loop {
|
||||
match self.ci.nodes[ctrl].kind.clone() {
|
||||
Kind::Start => unreachable!(),
|
||||
Kind::End => unreachable!(),
|
||||
Kind::Return => {
|
||||
let ret_loc = match self.tys.size_of(self.ci.ret.expect("TODO")) {
|
||||
0 => Loc::default(),
|
||||
1..=8 => Loc { reg: 1u8.into() },
|
||||
s => todo!("{s}"),
|
||||
};
|
||||
|
||||
self.emit_expr(self.ci.nodes[ctrl].inputs[1], GenCtx::default().with_loc(ret_loc));
|
||||
let dst = self.emit_expr(
|
||||
self.ci.nodes[ctrl].inputs[1],
|
||||
GenCtx::default().with_loc(ret_loc),
|
||||
);
|
||||
self.ci.free_loc(dst);
|
||||
|
||||
break;
|
||||
}
|
||||
Kind::ConstInt { .. } => unreachable!(),
|
||||
Kind::Tuple { index } => {
|
||||
debug_assert!(index == 0);
|
||||
ctrl = self.ci.nodes[ctrl].outputs[0];
|
||||
}
|
||||
Kind::BinOp { .. } => unreachable!(),
|
||||
Kind::Call { func, args } => {
|
||||
let ret = self.tof(ctrl);
|
||||
|
||||
let mut parama = self.tys.parama(ret);
|
||||
for &arg in args.iter() {
|
||||
let dst = match self.tys.size_of(self.tof(arg)) {
|
||||
0 => continue,
|
||||
1..=8 => Loc { reg: parama.next().into() },
|
||||
s => todo!("{s}"),
|
||||
};
|
||||
let dst = self.emit_expr(arg, GenCtx::default().with_loc(dst));
|
||||
self.ci.free_loc(dst);
|
||||
}
|
||||
|
||||
let ret_loc = match self.tys.size_of(ret) {
|
||||
0 => Loc::default(),
|
||||
1..=8 => Loc { reg: 1u8.into() },
|
||||
s => todo!("{s}"),
|
||||
};
|
||||
|
||||
let reloc = Reloc::new(self.ci.code.len(), 3, 4);
|
||||
self.ci
|
||||
.relocs
|
||||
.push(TypedReloc { target: ty::Kind::Func(func).compress(), reloc });
|
||||
self.ci.emit(instrs::jal(reg::RET_ADDR, reg::ZERO, 0));
|
||||
self.make_func_reachable(func);
|
||||
|
||||
// TODO: allocate return register
|
||||
let loc = Loc { reg: self.ci.regs.allocate() };
|
||||
self.ci.emit(instrs::cp(loc.reg.get(), ret_loc.reg.get()));
|
||||
|
||||
self.ci.nodes[ctrl].loc = loc;
|
||||
self.ci.nodes[ctrl].lock_rc += 1;
|
||||
|
||||
ctrl = *self.ci.nodes[ctrl]
|
||||
.outputs
|
||||
.iter()
|
||||
.find(|&&o| self.ci.nodes.is_cfg(o))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
Kind::ConstInt { value } => unreachable!(),
|
||||
Kind::Tuple { index } => {
|
||||
debug_assert!(index == 0);
|
||||
self.emit_control(self.ci.nodes[ctrl].outputs[0]);
|
||||
}
|
||||
Kind::BinOp { op } => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_expr(&mut self, expr: Nid, ctx: GenCtx) {
|
||||
#[must_use = "dont forget to drop the location"]
|
||||
fn emit_expr(&mut self, expr: Nid, ctx: GenCtx) -> Option<Loc> {
|
||||
match self.ci.nodes[expr].kind {
|
||||
Kind::Start => unreachable!(),
|
||||
Kind::End => unreachable!(),
|
||||
|
@ -1400,9 +1678,75 @@ impl Codegen {
|
|||
self.ci.nodes[expr].loc = Loc { reg };
|
||||
}
|
||||
}
|
||||
Kind::Tuple { index } => unreachable!(),
|
||||
Kind::BinOp { op } => unreachable!(),
|
||||
Kind::Tuple { index } => {
|
||||
debug_assert!(index != 0);
|
||||
}
|
||||
Kind::BinOp { op } => {
|
||||
let ty = self.tof(expr);
|
||||
let [left, right, ..] = self.ci.nodes[expr].inputs;
|
||||
let mut ldst = self.emit_expr(left, GenCtx::default());
|
||||
let mut rdst = self.emit_expr(right, GenCtx::default());
|
||||
|
||||
let op = Self::math_op(op, ty.is_signed(), self.tys.size_of(ty))
|
||||
.expect("TODO: what now?");
|
||||
|
||||
let loc = ctx
|
||||
.loc
|
||||
.or_else(|| ldst.take())
|
||||
.or_else(|| rdst.take())
|
||||
.unwrap_or_else(|| Loc { reg: self.ci.regs.allocate() });
|
||||
|
||||
self.ci.free_loc(ldst);
|
||||
self.ci.free_loc(rdst);
|
||||
|
||||
self.ci.emit(op(
|
||||
loc.reg.get(),
|
||||
self.ci.nodes[left].loc.reg.get(),
|
||||
self.ci.nodes[right].loc.reg.get(),
|
||||
));
|
||||
self.ci.nodes[expr].loc = loc;
|
||||
}
|
||||
Kind::Call { .. } => {}
|
||||
}
|
||||
|
||||
self.ci.nodes[expr].lock_rc += 1;
|
||||
if self.ci.nodes[expr].lock_rc as usize == self.ci.nodes[expr].outputs.len() {
|
||||
let re = self.ci.nodes[expr].loc.as_ref();
|
||||
return Some(std::mem::replace(&mut self.ci.nodes[expr].loc, re));
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn math_op(
|
||||
op: TokenKind,
|
||||
signed: bool,
|
||||
size: u32,
|
||||
) -> Option<fn(u8, u8, u8) -> (usize, [u8; instrs::MAX_SIZE])> {
|
||||
use {instrs::*, TokenKind as T};
|
||||
|
||||
macro_rules! div { ($($op:ident),*) => {[$(|a, b, c| $op(a, reg::ZERO, b, c)),*]}; }
|
||||
macro_rules! rem { ($($op:ident),*) => {[$(|a, b, c| $op(reg::ZERO, a, b, c)),*]}; }
|
||||
|
||||
let ops = match op {
|
||||
T::Add => [add8, add16, add32, add64],
|
||||
T::Sub => [sub8, sub16, sub32, sub64],
|
||||
T::Mul => [mul8, mul16, mul32, mul64],
|
||||
T::Div if signed => div!(dirs8, dirs16, dirs32, dirs64),
|
||||
T::Div => div!(diru8, diru16, diru32, diru64),
|
||||
T::Mod if signed => rem!(dirs8, dirs16, dirs32, dirs64),
|
||||
T::Mod => rem!(diru8, diru16, diru32, diru64),
|
||||
T::Band => return Some(and),
|
||||
T::Bor => return Some(or),
|
||||
T::Xor => return Some(xor),
|
||||
T::Shl => [slu8, slu16, slu32, slu64],
|
||||
T::Shr if signed => [srs8, srs16, srs32, srs64],
|
||||
T::Shr => [sru8, sru16, sru32, sru64],
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
Some(ops[size.ilog2() as usize])
|
||||
}
|
||||
|
||||
// TODO: sometimes its better to do this in bulk
|
||||
|
@ -1420,7 +1764,7 @@ impl Codegen {
|
|||
name: Option<Ident>,
|
||||
lit_name: &str,
|
||||
) -> ty::Kind {
|
||||
log::dbg!("find_or_declare: {lit_name} {file}");
|
||||
log::trc!("find_or_declare: {lit_name} {file}");
|
||||
|
||||
let f = self.files[file as usize].clone();
|
||||
let Some((expr, ident)) = f.find_decl(name.ok_or(lit_name)) else {
|
||||
|
@ -1566,10 +1910,76 @@ impl Codegen {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct LoggedMem {
|
||||
pub mem: hbvm::mem::HostMemory,
|
||||
}
|
||||
|
||||
impl hbvm::mem::Memory for LoggedMem {
|
||||
unsafe fn load(
|
||||
&mut self,
|
||||
addr: hbvm::mem::Address,
|
||||
target: *mut u8,
|
||||
count: usize,
|
||||
) -> Result<(), hbvm::mem::LoadError> {
|
||||
log::trc!(
|
||||
"load: {:x} {:?}",
|
||||
addr.get(),
|
||||
core::slice::from_raw_parts(addr.get() as *const u8, count)
|
||||
.iter()
|
||||
.rev()
|
||||
.map(|&b| format!("{b:02x}"))
|
||||
.collect::<String>()
|
||||
);
|
||||
self.mem.load(addr, target, count)
|
||||
}
|
||||
|
||||
unsafe fn store(
|
||||
&mut self,
|
||||
addr: hbvm::mem::Address,
|
||||
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>()
|
||||
);
|
||||
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()
|
||||
} else {
|
||||
core::slice::from_raw_parts(addr.get() as *const u8, core::mem::size_of::<T>())
|
||||
.iter()
|
||||
.map(|&b| format!("{:02x}", b))
|
||||
.collect::<String>()
|
||||
}
|
||||
);
|
||||
self.mem.prog_read(addr)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use {
|
||||
crate::parser::{self, FileId},
|
||||
crate::{
|
||||
parser::{self, FileId},
|
||||
son::LoggedMem,
|
||||
},
|
||||
std::io,
|
||||
};
|
||||
|
||||
|
@ -1632,16 +2042,68 @@ mod tests {
|
|||
|
||||
codegen.generate();
|
||||
|
||||
use std::fmt::Write;
|
||||
let mut out = Vec::new();
|
||||
codegen.assemble(&mut out);
|
||||
|
||||
write!(output, "{}", codegen.ci.nodes).unwrap();
|
||||
let mut buf = Vec::<u8>::new();
|
||||
let err = codegen.disasm(&out, &mut buf);
|
||||
output.push_str(String::from_utf8(buf).unwrap().as_str());
|
||||
if let Err(e) = err {
|
||||
writeln!(output, "!!! asm is invalid: {e}").unwrap();
|
||||
return;
|
||||
}
|
||||
|
||||
let mut stack = [0_u64; 128];
|
||||
|
||||
let mut vm = unsafe {
|
||||
hbvm::Vm::<_, 0>::new(
|
||||
LoggedMem::default(),
|
||||
hbvm::mem::Address::new(out.as_ptr() as u64),
|
||||
)
|
||||
};
|
||||
|
||||
vm.write_reg(super::reg::STACK_PTR, unsafe { stack.as_mut_ptr().add(stack.len()) } as u64);
|
||||
|
||||
use std::fmt::Write;
|
||||
let stat = loop {
|
||||
match vm.run() {
|
||||
Ok(hbvm::VmRunOk::End) => break Ok(()),
|
||||
Ok(hbvm::VmRunOk::Ecall) => match vm.read_reg(2).0 {
|
||||
1 => writeln!(output, "ev: Ecall").unwrap(), // compatibility with a test
|
||||
69 => {
|
||||
let [size, align] = [vm.read_reg(3).0 as usize, vm.read_reg(4).0 as usize];
|
||||
let layout = std::alloc::Layout::from_size_align(size, align).unwrap();
|
||||
let ptr = unsafe { std::alloc::alloc(layout) };
|
||||
vm.write_reg(1, ptr as u64);
|
||||
}
|
||||
96 => {
|
||||
let [ptr, size, align] = [
|
||||
vm.read_reg(3).0 as usize,
|
||||
vm.read_reg(4).0 as usize,
|
||||
vm.read_reg(5).0 as usize,
|
||||
];
|
||||
|
||||
let layout = std::alloc::Layout::from_size_align(size, align).unwrap();
|
||||
unsafe { std::alloc::dealloc(ptr as *mut u8, layout) };
|
||||
}
|
||||
3 => vm.write_reg(1, 42),
|
||||
unknown => unreachable!("unknown ecall: {unknown:?}"),
|
||||
},
|
||||
Ok(ev) => writeln!(output, "ev: {:?}", ev).unwrap(),
|
||||
Err(e) => break Err(e),
|
||||
}
|
||||
};
|
||||
|
||||
writeln!(output, "code size: {}", out.len()).unwrap();
|
||||
writeln!(output, "ret: {:?}", vm.read_reg(1).0).unwrap();
|
||||
writeln!(output, "status: {:?}", stat).unwrap();
|
||||
}
|
||||
|
||||
crate::run_tests! { generate:
|
||||
arithmetic => README;
|
||||
const_folding_with_arg => README;
|
||||
//variables => README;
|
||||
//functions => README;
|
||||
variables => README;
|
||||
functions => README;
|
||||
//comments => README;
|
||||
//if_statements => README;
|
||||
//loops => README;
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
0: Start
|
||||
2: Tuple { index: 0 }
|
||||
4: return [2] 1
|
||||
1: End
|
||||
main:
|
||||
ADDI64 r254, r254, -8d
|
||||
ST r31, r254, 0a, 8h
|
||||
LI64 r1, 1d
|
||||
LD r31, r254, 0a, 8h
|
||||
ADDI64 r254, r254, 8d
|
||||
JALA r0, r31, 0a
|
||||
code size: 77
|
||||
ret: 1
|
||||
status: Ok(())
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
0: Start
|
||||
2: Tuple { index: 0 }
|
||||
7: return [2] 0
|
||||
1: End
|
||||
Start.1
|
||||
main:
|
||||
ADDI64 r254, r254, -8d
|
||||
ST r31, r254, 0a, 8h
|
||||
LI64 r1, 0d
|
||||
LD r31, r254, 0a, 8h
|
||||
ADDI64 r254, r254, 8d
|
||||
JALA r0, r31, 0a
|
||||
code size: 77
|
||||
ret: 0
|
||||
status: Ok(())
|
||||
|
|
34
hblang/tests/son_tests_functions.txt
Normal file
34
hblang/tests/son_tests_functions.txt
Normal file
|
@ -0,0 +1,34 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -24d
|
||||
ST r31, r254, 0a, 24h
|
||||
LI64 r2, 10d
|
||||
JAL r31, r0, :add_one
|
||||
CP r32, r1
|
||||
LI64 r2, 20d
|
||||
JAL r31, r0, :add_two
|
||||
CP r33, r1
|
||||
ADD64 r1, r33, r32
|
||||
LD r31, r254, 0a, 24h
|
||||
ADDI64 r254, r254, 24d
|
||||
JALA r0, r31, 0a
|
||||
add_one:
|
||||
ADDI64 r254, r254, -24d
|
||||
ST r31, r254, 0a, 24h
|
||||
CP r32, r2
|
||||
LI64 r33, 1d
|
||||
ADD64 r1, r32, r33
|
||||
LD r31, r254, 0a, 24h
|
||||
ADDI64 r254, r254, 24d
|
||||
JALA r0, r31, 0a
|
||||
add_two:
|
||||
ADDI64 r254, r254, -24d
|
||||
ST r31, r254, 0a, 24h
|
||||
CP r32, r2
|
||||
LI64 r33, 2d
|
||||
ADD64 r1, r32, r33
|
||||
LD r31, r254, 0a, 24h
|
||||
ADDI64 r254, r254, 24d
|
||||
JALA r0, r31, 0a
|
||||
code size: 263
|
||||
ret: 33
|
||||
status: Ok(())
|
10
hblang/tests/son_tests_variables.txt
Normal file
10
hblang/tests/son_tests_variables.txt
Normal file
|
@ -0,0 +1,10 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -8d
|
||||
ST r31, r254, 0a, 8h
|
||||
LI64 r1, 0d
|
||||
LD r31, r254, 0a, 8h
|
||||
ADDI64 r254, r254, 8d
|
||||
JALA r0, r31, 0a
|
||||
code size: 77
|
||||
ret: 0
|
||||
status: Ok(())
|
Loading…
Reference in a new issue