From e0d4955bd52addbd965fdfbb08b7a9bb70119aa0 Mon Sep 17 00:00:00 2001 From: Jakub Doka Date: Thu, 24 Oct 2024 13:25:30 +0200 Subject: [PATCH] fixing small struct return --- lang/README.md | 13 +++ lang/src/codegen.rs | 32 ++----- lang/src/lib.rs | 32 ++++++- lang/src/son.rs | 95 +++++++++++-------- .../son_tests_returning_global_struct.txt | 29 ++++++ lang/tests/son_tests_wide_ret.txt | 3 +- 6 files changed, 137 insertions(+), 67 deletions(-) create mode 100644 lang/tests/son_tests_returning_global_struct.txt diff --git a/lang/README.md b/lang/README.md index 0046888..fb5edf3 100644 --- a/lang/README.md +++ b/lang/README.md @@ -527,6 +527,19 @@ main := fn(): int { ### Purely Testing Examples +#### returning_global_struct +```hb +Color := struct {r: u8, g: u8, b: u8, a: u8} +white := Color.(255, 255, 255, 255) +random_color := fn(): Color { + return white +} +main := fn(): int { + val := random_color() + return @as(int, val.r) + val.g + val.b + val.a +} +``` + #### wide_ret ```hb OemIdent := struct { diff --git a/lang/src/codegen.rs b/lang/src/codegen.rs index d679c23..cda4d2b 100644 --- a/lang/src/codegen.rs +++ b/lang/src/codegen.rs @@ -37,6 +37,7 @@ fn ensure_loaded(value: CtValue, derefed: bool, size: u32) -> u64 { mod stack { use { super::{Offset, Size}, + crate::debug, alloc::vec::Vec, core::num::NonZeroU32, }; @@ -81,17 +82,7 @@ mod stack { impl Drop for Id { fn drop(&mut self) { - let is_panicking = { - #[cfg(feature = "std")] - { - std::thread::panicking() - } - #[cfg(not(feature = "std"))] - { - false - } - }; - if !is_panicking && !self.is_ref() { + if !debug::panicking() && !self.is_ref() { unreachable!("stack id leaked: {:?}", self.0); } } @@ -163,7 +154,10 @@ mod stack { } mod rall { - use {crate::reg::*, alloc::vec::Vec}; + use { + crate::{debug, reg::*}, + alloc::vec::Vec, + }; type Reg = u8; @@ -208,17 +202,9 @@ mod rall { #[cfg(all(debug_assertions, feature = "std"))] impl Drop for Id { fn drop(&mut self) { - let is_panicking = { - #[cfg(all(debug_assertions, feature = "std"))] - { - std::thread::panicking() - } - #[cfg(not(all(debug_assertions, feature = "std")))] - { - false - } - }; - if !is_panicking && let Some(bt) = self.1.take() { + if !debug::panicking() + && let Some(bt) = self.1.take() + { unreachable!("reg id leaked: {:?} {bt}", self.0); } } diff --git a/lang/src/lib.rs b/lang/src/lib.rs index 60ec2dc..7223c80 100644 --- a/lang/src/lib.rs +++ b/lang/src/lib.rs @@ -75,6 +75,34 @@ pub mod lexer; #[cfg(feature = "opts")] mod vc; +mod debug { + + pub fn panicking() -> bool { + #[cfg(feature = "std")] + { + std::thread::panicking() + } + #[cfg(not(feature = "std"))] + { + false + } + } + + #[cfg(all(debug_assertions, feature = "std"))] + pub type Trace = std::rc::Rc; + #[cfg(not(all(debug_assertions, feature = "std")))] + pub type Trace = (); + + pub fn trace() -> Trace { + #[cfg(all(debug_assertions, feature = "std"))] + { + std::rc::Rc::new(std::backtrace::Backtrace::capture()) + } + #[cfg(not(all(debug_assertions, feature = "std")))] + {} + } +} + pub mod reg { pub const STACK_PTR: Reg = 254; pub const ZERO: Reg = 0; @@ -252,7 +280,7 @@ mod ty { parser::{self, Pos}, Size, Types, }, - core::{num::NonZeroU32, ops::Range, usize}, + core::{num::NonZeroU32, ops::Range}, }; pub type ArrayLen = u32; @@ -1268,7 +1296,7 @@ impl Types { }) .chain(self.ins.globals.iter().filter(|g| task::is_done(g.offset)).map(|g| { let name = if g.file == u32::MAX { - core::str::from_utf8(&g.data).unwrap() + core::str::from_utf8(&g.data).unwrap_or("invalid utf-8") } else { let file = &files[g.file as usize]; file.ident_str(g.name) diff --git a/lang/src/son.rs b/lang/src/son.rs index 3a26050..aa7e0ea 100644 --- a/lang/src/son.rs +++ b/lang/src/son.rs @@ -2,6 +2,7 @@ use { self::var::{Scope, Variable}, crate::{ ctx_map::CtxEntry, + debug, ident::Ident, instrs, lexer::{self, TokenKind}, @@ -23,6 +24,7 @@ use { fmt::{self, Debug, Display, Write}, format_args as fa, mem, ops::{self}, + u16, }, hashbrown::hash_map, hbbytecode::DisasmError, @@ -49,7 +51,7 @@ impl StoreId for Nid { } impl crate::ctx_map::CtxEntry for Nid { - type Ctx = [Result]; + type Ctx = [Result]; type Key<'a> = (Kind, &'a [Nid], ty::Id); fn key<'a>(&self, ctx: &'a Self::Ctx) -> Self::Key<'a> { @@ -57,23 +59,9 @@ impl crate::ctx_map::CtxEntry for Nid { } } -#[cfg(debug_assertions)] -type Trace = std::rc::Rc; -#[cfg(not(debug_assertions))] -type Trace = (); - -fn trace() -> Trace { - #[cfg(debug_assertions)] - { - std::rc::Rc::new(std::backtrace::Backtrace::capture()) - } - #[cfg(not(debug_assertions))] - {} -} - #[derive(Clone)] struct Nodes { - values: Vec>, + values: Vec>, visited: BitSet, free: Nid, lookup: Lookup, @@ -165,15 +153,18 @@ impl Nodes { } fn graphviz_in_browser(&self, tys: &Types, files: &[parser::Ast]) { - let out = &mut String::new(); - _ = self.graphviz_low(tys, files, out); - if !std::process::Command::new("brave") - .arg(format!("https://dreampuf.github.io/GraphvizOnline/#{out}")) - .status() - .unwrap() - .success() + #[cfg(all(debug_assertions, feature = "std"))] { - log::error!("{out}"); + let out = &mut String::new(); + _ = self.graphviz_low(tys, files, out); + if !std::process::Command::new("brave") + .arg(format!("https://dreampuf.github.io/GraphvizOnline/#{out}")) + .status() + .unwrap() + .success() + { + log::error!("{out}"); + } } } @@ -188,11 +179,12 @@ impl Nodes { fn remove_low(&mut self, id: Nid) -> Node { if cfg!(debug_assertions) { let value = - mem::replace(&mut self.values[id as usize], Err((self.free, trace()))).unwrap(); + mem::replace(&mut self.values[id as usize], Err((self.free, debug::trace()))) + .unwrap(); self.free = id; value } else { - mem::replace(&mut self.values[id as usize], Err((Nid::MAX, trace()))).unwrap() + mem::replace(&mut self.values[id as usize], Err((Nid::MAX, debug::trace()))).unwrap() } } @@ -240,7 +232,7 @@ impl Nodes { if self.free == Nid::MAX { self.free = self.values.len() as _; - self.values.push(Err((Nid::MAX, trace()))); + self.values.push(Err((Nid::MAX, debug::trace()))); } let free = self.free; @@ -1132,7 +1124,7 @@ struct Loop { mod var { use { super::{Kind, Nid, Nodes}, - crate::{ident::Ident, ty}, + crate::{debug, ident::Ident, ty}, alloc::vec::Vec, }; @@ -1189,7 +1181,7 @@ mod var { impl Drop for Variable { fn drop(&mut self) { - if self.ty != ty::Id::UNDECLARED && !std::thread::panicking() { + if self.ty != ty::Id::UNDECLARED && !debug::panicking() { panic!("variable unproperly deinitialized") } } @@ -1427,11 +1419,19 @@ impl ItemCtx { self.emit(instrs::ld(r, atr(allocs[0]), 0, size)) } Some(PLoc::Ref(_, size)) => { - self.emit(instrs::bmc( - atr(allocs[0]), - atr(allocs[1]), - size.try_into().expect("TODO: handle huge copies"), - )); + let [src, dst] = [atr(allocs[0]), atr(allocs[1])]; + if let Ok(size) = u16::try_from(size) { + self.emit(instrs::bmc(src, dst, size)); + } else { + for _ in 0..size / u16::MAX as u32 { + self.emit(instrs::bmc(src, dst, u16::MAX)); + self.emit(instrs::addi64(src, src, u16::MAX as _)); + self.emit(instrs::addi64(dst, dst, u16::MAX as _)); + } + self.emit(instrs::bmc(src, dst, size as u16)); + self.emit(instrs::addi64(src, src, size.wrapping_neg() as _)); + self.emit(instrs::addi64(dst, dst, size.wrapping_neg() as _)); + } } } @@ -1522,6 +1522,12 @@ impl ItemCtx { let stck = fuc.nodes[*node.inputs.last().unwrap()].offset; self.emit(instrs::st(r, reg::STACK_PTR, stck as _, size)); } + if let Some(PLoc::Reg(r, size)) = ret + && node.ty.loc(tys) == Loc::Stack + { + let stck = fuc.nodes[*node.inputs.last().unwrap()].offset; + self.emit(instrs::st(r, reg::STACK_PTR, stck as _, size)); + } } Kind::Global { global } => { let reloc = Reloc::new(self.code.len(), 3, 4); @@ -2523,8 +2529,7 @@ impl<'a> Codegen<'a> { Expr::Directive { name: "as", args: [ty, expr], .. } => { let ty = self.ty(ty); let ctx = Ctx::default().with_ty(ty); - let mut val = self.raw_expr_ctx(expr, ctx)?; - self.strip_var(&mut val); + let mut val = self.expr_ctx(expr, ctx)?; self.assert_ty(expr.pos(), &mut val, ty, "hinted expr"); Some(val) } @@ -3230,11 +3235,18 @@ impl<'a> Codegen<'a> { // FIXME: could fuck us ty::Id::UNDECLARED } else { - debug_assert_eq!( - ty, - ty::Id::TYPE, - "TODO: we dont support anything except type generics" - ); + if ty != ty::Id::TYPE { + self.report( + arg.pos(), + fa!( + "arbitrary comptime types are not supported yet \ + (expected '{}' got '{}')", + self.ty_display(ty::Id::TYPE), + self.ty_display(ty) + ), + ); + return None; + } let ty = self.ty(arg); self.tys.tmp.args.push(ty); ty @@ -4437,6 +4449,7 @@ mod tests { fb_driver; // Purely Testing Examples; + returning_global_struct; wide_ret; comptime_min_reg_leak; different_types; diff --git a/lang/tests/son_tests_returning_global_struct.txt b/lang/tests/son_tests_returning_global_struct.txt new file mode 100644 index 0000000..21d52e8 --- /dev/null +++ b/lang/tests/son_tests_returning_global_struct.txt @@ -0,0 +1,29 @@ +main: + ADDI64 r254, r254, -24d + ST r31, r254, 8a, 16h + ADDI64 r32, r254, 4d + JAL r31, r0, :random_color + ST r1, r254, 4a, 4h + ADDI64 r5, r254, 0d + BMC r32, r5, 4h + LD r4, r254, 3a, 1h + ANDI r6, r4, 255d + LD r3, r254, 2a, 1h + ANDI r5, r3, 255d + LD r2, r254, 1a, 1h + ANDI r4, r2, 255d + LD r2, r254, 0a, 1h + ANDI r7, r2, 255d + ADD64 r8, r4, r7 + ADD64 r12, r8, r5 + ADD64 r1, r12, r6 + LD r31, r254, 8a, 16h + ADDI64 r254, r254, 24d + JALA r0, r31, 0a +random_color: + LRA r1, r0, :white + LD r1, r1, 0a, 4h + JALA r0, r31, 0a +code size: 257 +ret: 1020 +status: Ok(()) diff --git a/lang/tests/son_tests_wide_ret.txt b/lang/tests/son_tests_wide_ret.txt index 363b029..31815c9 100644 --- a/lang/tests/son_tests_wide_ret.txt +++ b/lang/tests/son_tests_wide_ret.txt @@ -20,6 +20,7 @@ maina: ST r31, r254, 32a, 16h ADDI64 r32, r254, 28d JAL r31, r0, :small_struct + ST r1, r254, 28a, 4h LI64 r2, 1d LI64 r4, 3d LI64 r1, 0d @@ -51,6 +52,6 @@ small_struct: LD r1, r3, 0a, 4h ADDI64 r254, r254, 4d JALA r0, r31, 0a -code size: 544 +code size: 557 ret: 2 status: Ok(())