Compare commits

..

No commits in common. "trunk" and "trunk" have entirely different histories.
trunk ... trunk

26 changed files with 1795 additions and 2994 deletions

2
.gitignore vendored
View file

@ -9,5 +9,5 @@ db.sqlite-journal
# assets
/depell/src/*.gz
/depell/src/*.wasm
#**/*-sv.rs
**/*-sv.rs
/bytecode/src/instrs.rs

52
Cargo.lock generated
View file

@ -276,15 +276,15 @@ dependencies = [
[[package]]
name = "bytes"
version = "1.8.0"
version = "1.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da"
checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3"
[[package]]
name = "cc"
version = "1.1.36"
version = "1.1.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baee610e9452a8f6f0a1b6194ec09ff9e2d85dea54432acdae41aa0761c95d70"
checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945"
dependencies = [
"jobserver",
"libc",
@ -721,9 +721,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "hyper"
version = "1.5.0"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a"
checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05"
dependencies = [
"bytes",
"futures-channel",
@ -741,9 +741,9 @@ dependencies = [
[[package]]
name = "hyper-util"
version = "0.1.10"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4"
checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b"
dependencies = [
"bytes",
"futures-util",
@ -979,18 +979,18 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "pin-project"
version = "1.1.7"
version = "1.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95"
checksum = "baf123a161dde1e524adf36f90bc5d8d3462824a9c43553ad07a8183161189ec"
dependencies = [
"pin-project-internal",
]
[[package]]
name = "pin-project-internal"
version = "1.1.7"
version = "1.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c"
checksum = "a4502d8515ca9f32f1fb543d987f63d95a14934883db45bdb48060b6b69257f8"
dependencies = [
"proc-macro2",
"quote",
@ -999,9 +999,9 @@ dependencies = [
[[package]]
name = "pin-project-lite"
version = "0.2.15"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff"
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
[[package]]
name = "pin-utils"
@ -1073,9 +1073,9 @@ dependencies = [
[[package]]
name = "regex"
version = "1.11.1"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8"
dependencies = [
"aho-corasick",
"memchr",
@ -1162,9 +1162,9 @@ dependencies = [
[[package]]
name = "rustls"
version = "0.23.16"
version = "0.23.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eee87ff5d9b36712a58574e12e9f0ea80f915a5b0ac518d322b24a465617925e"
checksum = "415d9944693cb90382053259f89fbb077ea730ad7273047ec63b19bc9b160ba8"
dependencies = [
"aws-lc-rs",
"once_cell",
@ -1185,9 +1185,9 @@ dependencies = [
[[package]]
name = "rustls-pki-types"
version = "1.10.0"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b"
checksum = "0e696e35370c65c9c541198af4543ccd580cf17fc25d8e05c5a242b202488c55"
[[package]]
name = "rustls-webpki"
@ -1203,9 +1203,9 @@ dependencies = [
[[package]]
name = "rustversion"
version = "1.0.18"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248"
checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
[[package]]
name = "ryu"
@ -1241,9 +1241,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.132"
version = "1.0.128"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03"
checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8"
dependencies = [
"itoa",
"memchr",
@ -1366,9 +1366,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
[[package]]
name = "tokio"
version = "1.41.1"
version = "1.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33"
checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998"
dependencies = [
"backtrace",
"bytes",

View file

@ -5,9 +5,8 @@
use {
alloc::{string::String, vec::Vec},
hblang::{
parser::FileId,
son::{hbvm::HbvmBackend, Codegen, CodegenCtx},
ty::Module,
Ent,
},
};
@ -61,7 +60,7 @@ unsafe fn compile_and_run(mut fuel: usize) {
let files = {
let paths = files.iter().map(|f| f.path).collect::<Vec<_>>();
let mut loader = |path: &str, _: &str, kind| match kind {
hblang::parser::FileKind::Module => Ok(paths.binary_search(&path).unwrap()),
hblang::parser::FileKind::Module => Ok(paths.binary_search(&path).unwrap() as FileId),
hblang::parser::FileKind::Embed => Err("embeds are not supported".into()),
};
files
@ -80,7 +79,7 @@ unsafe fn compile_and_run(mut fuel: usize) {
let mut ct = {
let mut backend = HbvmBackend::default();
Codegen::new(&mut backend, &files, &mut ctx).generate(Module::new(root));
Codegen::new(&mut backend, &files, &mut ctx).generate(root as FileId);
if !ctx.parser.errors.borrow().is_empty() {
log::error!("{}", ctx.parser.errors.borrow());

File diff suppressed because one or more lines are too long

View file

@ -482,6 +482,15 @@ pub mod test {
let mut ctx = Ctx::default();
let ast = parser::Ast::new(ident, minned, &mut ctx, &mut parser::no_loader);
//log::error!(
// "{} / {} = {} | {} / {} = {}",
// ast.mem.size(),
// input.len(),
// ast.mem.size() as f32 / input.len() as f32,
// ast.mem.size(),
// ast.file.len(),
// ast.mem.size() as f32 / ast.file.len() as f32
//);
let mut output = String::new();
write!(output, "{ast}").unwrap();

View file

@ -1,8 +1,7 @@
use {
crate::{
parser::{Ast, Ctx, FileKind},
parser::{self, Ast, Ctx, FileKind},
son::{self, hbvm::HbvmBackend},
ty,
},
alloc::{string::String, vec::Vec},
core::{fmt::Write, num::NonZeroUsize, ops::Deref},
@ -43,10 +42,10 @@ pub struct Options {
}
impl Options {
pub fn from_args(args: &[&str], out: &mut Vec<u8>) -> std::io::Result<Self> {
pub fn from_args(args: &[&str]) -> std::io::Result<Self> {
if args.contains(&"--help") || args.contains(&"-h") {
writeln!(out, "Usage: hbc [OPTIONS...] <FILE>")?;
writeln!(out, include_str!("../command-help.txt"))?;
log::error!("Usage: hbc [OPTIONS...] <FILE>");
log::error!(include_str!("../command-help.txt"));
return Err(std::io::ErrorKind::Other.into());
}
@ -59,9 +58,7 @@ impl Options {
.position(|&a| a == "--threads")
.map(|i| {
args[i + 1].parse::<NonZeroUsize>().map_err(|e| {
writeln!(out, "--threads expects non zero integer: {e}")
.err()
.unwrap_or(std::io::ErrorKind::Other.into())
std::io::Error::other(format!("--threads expects non zero integer: {e}"))
})
})
.transpose()?
@ -74,22 +71,22 @@ impl Options {
pub fn run_compiler(root_file: &str, options: Options, out: &mut Vec<u8>) -> std::io::Result<()> {
let parsed = parse_from_fs(options.extra_threads, root_file)?;
if (options.fmt || options.fmt_stdout) && !parsed.errors.is_empty() {
*out = parsed.errors.into_bytes();
return Err(std::io::Error::other("fmt fialed (errors are in out)"));
fn format_ast(ast: parser::Ast) -> std::io::Result<()> {
let mut output = String::new();
write!(output, "{ast}").unwrap();
if ast.file.deref().trim() != output.as_str().trim() {
std::fs::write(&*ast.path, output)?;
}
Ok(())
}
if options.fmt {
let mut output = String::new();
for ast in parsed.ast {
write!(output, "{ast}").unwrap();
if ast.file.deref().trim() != output.as_str().trim() {
std::fs::write(&*ast.path, &output)?;
}
output.clear();
for parsed in parsed.ast {
format_ast(parsed)?;
}
} else if options.fmt_stdout {
write!(out, "{}", &parsed.ast[0])?;
let ast = parsed.ast.into_iter().next().unwrap();
write!(out, "{ast}").unwrap();
} else {
let mut backend = HbvmBackend::default();
let mut ctx = crate::son::CodegenCtx::default();
@ -97,12 +94,11 @@ pub fn run_compiler(root_file: &str, options: Options, out: &mut Vec<u8>) -> std
let mut codegen = son::Codegen::new(&mut backend, &parsed.ast, &mut ctx);
codegen.push_embeds(parsed.embeds);
codegen.generate(ty::Module::MAIN);
codegen.generate(0);
if !codegen.errors.borrow().is_empty() {
drop(codegen);
*out = ctx.parser.errors.into_inner().into_bytes();
return Err(std::io::Error::other("compilation faoled (errors are in out)"));
log::error!("{}", codegen.errors.borrow());
return Err(std::io::Error::other("compilation faoled"));
}
codegen.assemble(out);
@ -243,10 +239,10 @@ pub fn parse_from_fs(extra_threads: usize, root: &str) -> io::Result<Loaded> {
}
}
type Task = (usize, PathBuf);
type Task = (u32, PathBuf);
let seen_modules = Mutex::new(crate::HashMap::<PathBuf, usize>::default());
let seen_embeds = Mutex::new(crate::HashMap::<PathBuf, usize>::default());
let seen_modules = Mutex::new(crate::HashMap::<PathBuf, u32>::default());
let seen_embeds = Mutex::new(crate::HashMap::<PathBuf, u32>::default());
let tasks = TaskQueue::<Task>::new(extra_threads + 1);
let ast = Mutex::new(Vec::<io::Result<Ast>>::new());
let embeds = Mutex::new(Vec::<Vec<u8>>::new());
@ -265,7 +261,7 @@ pub fn parse_from_fs(extra_threads: usize, root: &str) -> io::Result<Loaded> {
}
hash_map::Entry::Vacant(entry) => {
physiscal_path = entry.insert_entry(len as _).key().clone();
len
len as u32
}
}
};
@ -290,7 +286,7 @@ pub fn parse_from_fs(extra_threads: usize, root: &str) -> io::Result<Loaded> {
}
hash_map::Entry::Vacant(entry) => {
physiscal_path = entry.insert_entry(len as _).key().clone();
len
len as u32
}
}
};
@ -332,9 +328,9 @@ pub fn parse_from_fs(extra_threads: usize, root: &str) -> io::Result<Loaded> {
while let Some(task @ (indx, ..)) = tasks.pop() {
let res = execute_task(&mut ctx, task, &mut tmp);
let mut ast = ast.lock().unwrap();
let len = ast.len().max(indx + 1);
let len = ast.len().max(indx as usize + 1);
ast.resize_with(len, || Err(io::ErrorKind::InvalidData.into()));
ast[indx] = res;
ast[indx as usize] = res;
}
ctx.errors.into_inner()
};

View file

@ -3,7 +3,6 @@ use {
lexer::TokenKind,
parser,
son::{hbvm::HbvmBackend, Codegen, CodegenCtx},
ty::Module,
},
alloc::string::String,
core::{fmt::Write, hash::BuildHasher, ops::Range},
@ -136,6 +135,6 @@ pub fn fuzz(seed_range: Range<u64>) {
let mut backend = HbvmBackend::default();
let mut cdg = Codegen::new(&mut backend, core::slice::from_ref(&parsed), &mut ctx);
cdg.generate(Module::MAIN);
cdg.generate(0);
}
}

View file

@ -32,13 +32,11 @@
#[cfg(feature = "std")]
pub use fs::*;
pub use utils::Ent;
use {
self::{
lexer::TokenKind,
parser::{idfl, CommentOr, Expr, ExprRef, Pos},
ty::{ArrayLen, Builtin, Module},
utils::EntVec,
parser::{idfl, CommentOr, Expr, ExprRef, FileId, Pos},
ty::ArrayLen,
},
alloc::{string::String, vec::Vec},
core::{cell::Cell, fmt::Display, ops::Range},
@ -253,17 +251,17 @@ impl Ident {
pos..pos + len
}
fn builtin(builtin: Builtin) -> Ident {
Self(builtin.index() as _)
fn builtin(builtin: u32) -> Ident {
debug_assert!(Self(builtin).is_null());
Self(builtin)
}
}
pub mod ty {
mod ty {
use {
crate::{
lexer::TokenKind,
parser::{self, Pos},
utils::Ent,
parser::{self, FileId, Pos},
Ident, Size, Types,
},
core::{num::NonZeroU32, ops::Range},
@ -271,10 +269,16 @@ pub mod ty {
pub type ArrayLen = u32;
impl Func {
pub const ECA: Func = Func(u32::MAX);
pub const MAIN: Func = Func(u32::MIN);
}
pub type Builtin = u32;
pub type Struct = u32;
pub type Opt = u32;
pub type Ptr = u32;
pub type Func = u32;
pub type Global = u32;
pub type Module = u32;
pub type Slice = u32;
pub const ECA: Func = Func::MAX;
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, PartialOrd, Ord)]
pub struct Tuple(pub u32);
@ -301,10 +305,6 @@ pub mod ty {
self.0 as usize & Self::LEN_MASK
}
pub fn is_empty(self) -> bool {
self.len() == 0
}
pub fn empty() -> Self {
Self(0)
}
@ -350,30 +350,28 @@ pub mod ty {
fn key<'a>(&self, ctx: &'a Self::Ctx) -> Self::Key<'a> {
match self.expand() {
Kind::Struct(s) => {
let st = &ctx.structs[s];
let st = &ctx.structs[s as usize];
debug_assert_ne!(st.pos, Pos::MAX);
crate::SymKey::Struct(st.file, st.pos, st.captures)
}
Kind::Ptr(p) => crate::SymKey::Pointer(&ctx.ptrs[p]),
Kind::Opt(p) => crate::SymKey::Optional(&ctx.opts[p]),
Kind::Ptr(p) => crate::SymKey::Pointer(&ctx.ptrs[p as usize]),
Kind::Opt(p) => crate::SymKey::Optional(&ctx.opts[p as usize]),
Kind::Func(f) => {
let fc = &ctx.funcs[f];
let fc = &ctx.funcs[f as usize];
if let Some(base) = fc.base {
// TODO: merge base and sig
crate::SymKey::FuncInst(base, fc.sig.unwrap().args)
} else {
crate::SymKey::Decl(fc.file, fc.name)
}
}
Kind::Global(g) => {
let gb = &ctx.globals[g];
let gb = &ctx.globals[g as usize];
crate::SymKey::Decl(gb.file, gb.name)
}
Kind::Slice(s) => crate::SymKey::Array(&ctx.slices[s]),
Kind::Slice(s) => crate::SymKey::Array(&ctx.slices[s as usize]),
Kind::Module(_) | Kind::Builtin(_) => {
crate::SymKey::Decl(Module::default(), Ident::INVALID)
crate::SymKey::Decl(FileId::MAX, Ident::INVALID)
}
Kind::Const(c) => crate::SymKey::Constant(&ctx.consts[c]),
}
}
}
@ -390,7 +388,7 @@ pub mod ty {
pub fn bin_ret(self, op: TokenKind) -> Id {
use TokenKind as T;
match op {
T::Lt | T::Gt | T::Le | T::Ge | T::Ne | T::Eq => Id::BOOL,
T::Lt | T::Gt | T::Le | T::Ge | T::Ne | T::Eq => BOOL.into(),
_ => self,
}
}
@ -417,7 +415,7 @@ pub mod ty {
pub fn strip_pointer(self) -> Self {
match self.expand() {
Kind::Ptr(_) => Id::UINT,
Kind::Ptr(_) => Kind::Builtin(UINT).compress(),
_ => self,
}
}
@ -434,8 +432,8 @@ pub mod ty {
let (oa, ob) = (Self(self.0.min(ob.0)), Self(self.0.max(ob.0)));
let (a, b) = (oa.strip_pointer(), ob.strip_pointer());
Some(match () {
_ if oa == Id::NEVER => ob,
_ if ob == Id::NEVER => oa,
_ if oa == Self::from(NEVER) => ob,
_ if ob == Self::from(NEVER) => oa,
_ if oa == ob => oa,
_ if ob.is_optional() => ob,
_ if oa.is_pointer() && ob.is_pointer() => return None,
@ -458,12 +456,12 @@ pub mod ty {
pub(crate) fn simple_size(&self) -> Option<Size> {
Some(match self.expand() {
Kind::Ptr(_) => 8,
Kind::Builtin(Builtin(VOID)) => 0,
Kind::Builtin(Builtin(NEVER)) => 0,
Kind::Builtin(Builtin(INT | UINT | F64)) => 8,
Kind::Builtin(Builtin(I32 | U32 | TYPE | F32)) => 4,
Kind::Builtin(Builtin(I16 | U16)) => 2,
Kind::Builtin(Builtin(I8 | U8 | BOOL)) => 1,
Kind::Builtin(VOID) => 0,
Kind::Builtin(NEVER) => 0,
Kind::Builtin(INT | UINT | F64) => 8,
Kind::Builtin(I32 | U32 | TYPE | F32) => 4,
Kind::Builtin(I16 | U16) => 2,
Kind::Builtin(I8 | U8 | BOOL) => 1,
_ => return None,
})
}
@ -481,7 +479,7 @@ pub mod ty {
pub(crate) fn loc(&self, tys: &Types) -> Loc {
match self.expand() {
Kind::Opt(o)
if let ty = tys.ins.opts[o].base
if let ty = tys.ins.opts[o as usize].base
&& ty.loc(tys) == Loc::Reg
&& (ty.is_pointer() || tys.size_of(ty) < 8) =>
{
@ -490,9 +488,7 @@ pub mod ty {
Kind::Ptr(_) | Kind::Builtin(_) => Loc::Reg,
Kind::Struct(_) if tys.size_of(*self) == 0 => Loc::Reg,
Kind::Struct(_) | Kind::Slice(_) | Kind::Opt(_) => Loc::Stack,
Kind::Func(_) | Kind::Global(_) | Kind::Module(_) | Kind::Const(_) => {
unreachable!()
}
Kind::Func(_) | Kind::Global(_) | Kind::Module(_) => unreachable!(),
}
}
@ -500,7 +496,7 @@ pub mod ty {
match self.expand() {
Kind::Struct(s) => tys.struct_fields(s).iter().any(|f| f.ty.has_pointers(tys)),
Kind::Ptr(_) => true,
Kind::Slice(s) => tys.ins.slices[s].len == ArrayLen::MAX,
Kind::Slice(s) => tys.ins.slices[s as usize].len == ArrayLen::MAX,
_ => false,
}
}
@ -518,6 +514,12 @@ pub mod ty {
}
}
impl From<u32> for Id {
fn from(id: u32) -> Self {
Kind::Builtin(id).compress()
}
}
const fn array_to_lower_case<const N: usize>(array: [u8; N]) -> [u8; N] {
let mut result = [0; N];
let mut i = 0;
@ -531,7 +533,7 @@ pub mod ty {
macro_rules! builtin_type {
($($name:ident;)*) => {
$(const $name: u32 = ${index(0)} + 1;)*
$(pub const $name: Builtin = ${index(0)} + 1;)*
mod __lc_names {
use super::*;
@ -545,23 +547,20 @@ pub mod ty {
};)*
}
#[expect(dead_code)]
impl Id {
$(pub const $name: Self = Kind::Builtin(Builtin($name)).compress();)*
}
impl Kind {
$(pub const $name: Self = Kind::Builtin(Builtin($name));)*
$(pub const $name: Self = Kind::Builtin($name).compress();)*
}
pub fn from_str(name: &str) -> Option<Builtin> {
match name {
$(__lc_names::$name => Some(Builtin($name)),)*
$(__lc_names::$name => Some($name),)*
_ => None,
}
}
pub fn to_str(ty: Builtin) -> &'static str {
match ty.0 {
match ty {
$($name => __lc_names::$name,)*
v => unreachable!("invalid type: {}", v),
}
@ -591,10 +590,6 @@ pub mod ty {
macro_rules! type_kind {
($(#[$meta:meta])* $vis:vis enum $name:ident {$( $variant:ident, )*}) => {
crate::utils::decl_ent! {
$(pub struct $variant(u32);)*
}
$(#[$meta])*
$vis enum $name {
$($variant($variant),)*
@ -608,32 +603,24 @@ pub mod ty {
$vis fn from_ty(ty: Id) -> Self {
let (flag, index) = (ty.repr() >> Self::FLAG_OFFSET, ty.repr() & Self::INDEX_MASK);
match flag {
$(${index(0)} => Self::$variant($variant(index)),)*
$(${index(0)} => Self::$variant(index),)*
i => unreachable!("{i}"),
}
}
$vis const fn compress(self) -> Id {
let (index, flag) = match self {
$(Self::$variant(index) => (index.0, ${index(0)}),)*
$(Self::$variant(index) => (index, ${index(0)}),)*
};
Id(unsafe { NonZeroU32::new_unchecked((flag << Self::FLAG_OFFSET) | index) })
}
$vis const fn inner(self) -> u32 {
match self {
$(Self::$variant(index) => index,)*
}
}
}
$(
impl From<$variant> for $name {
fn from(value: $variant) -> Self {
Self::$variant(value)
}
}
impl From<$variant> for Id {
fn from(value: $variant) -> Self {
$name::$variant(value).compress()
}
}
)*
};
}
@ -648,35 +635,12 @@ pub mod ty {
Func,
Global,
Module,
Const,
}
}
impl Module {
pub const MAIN: Self = Self(0);
}
impl Default for Module {
fn default() -> Self {
Self(u32::MAX)
}
}
impl TryFrom<Ident> for Builtin {
type Error = ();
fn try_from(value: Ident) -> Result<Self, Self::Error> {
if value.is_null() {
Ok(Self(value.len()))
} else {
Err(())
}
}
}
impl Default for Kind {
fn default() -> Self {
Id::UNDECLARED.expand()
Self::Builtin(UNDECLARED)
}
}
@ -702,7 +666,7 @@ pub mod ty {
match TK::from_ty(self.ty) {
TK::Module(idx) => {
f.write_str("@use(\"")?;
self.files[idx.index()].path.fmt(f)?;
self.files[idx as usize].path.fmt(f)?;
f.write_str(")[")?;
idx.fmt(f)?;
f.write_str("]")
@ -710,14 +674,14 @@ pub mod ty {
TK::Builtin(ty) => f.write_str(to_str(ty)),
TK::Opt(ty) => {
f.write_str("?")?;
self.rety(self.tys.ins.opts[ty].base).fmt(f)
self.rety(self.tys.ins.opts[ty as usize].base).fmt(f)
}
TK::Ptr(ty) => {
f.write_str("^")?;
self.rety(self.tys.ins.ptrs[ty].base).fmt(f)
self.rety(self.tys.ins.ptrs[ty as usize].base).fmt(f)
}
TK::Struct(idx) => {
let record = &self.tys.ins.structs[idx];
let record = &self.tys.ins.structs[idx as usize];
if record.name.is_null() {
f.write_str("[")?;
idx.fmt(f)?;
@ -734,7 +698,7 @@ pub mod ty {
}
f.write_str("}")
} else {
let file = &self.files[record.file.index()];
let file = &self.files[record.file as usize];
f.write_str(file.ident_str(record.name))
}
}
@ -743,12 +707,11 @@ pub mod ty {
idx.fmt(f)
}
TK::Global(idx) => {
let global = &self.tys.ins.globals[idx];
let file = &self.files[global.file.index()];
f.write_str(file.ident_str(global.name))
f.write_str("global")?;
idx.fmt(f)
}
TK::Slice(idx) => {
let array = self.tys.ins.slices[idx];
let array = self.tys.ins.slices[idx as usize];
f.write_str("[")?;
self.rety(array.elem).fmt(f)?;
if array.len != ArrayLen::MAX {
@ -757,11 +720,6 @@ pub mod ty {
}
f.write_str("]")
}
TK::Const(idx) => {
let cnst = &self.tys.ins.consts[idx];
let file = &self.files[cnst.file.index()];
f.write_str(file.ident_str(cnst.name))
}
}
}
}
@ -774,11 +732,10 @@ type Size = u32;
pub enum SymKey<'a> {
Pointer(&'a Ptr),
Optional(&'a Opt),
Struct(Module, Pos, ty::Tuple),
Struct(FileId, Pos, ty::Tuple),
FuncInst(ty::Func, ty::Tuple),
Decl(Module, Ident),
Decl(FileId, Ident),
Array(&'a Array),
Constant(&'a Const),
}
#[derive(Clone, Copy)]
@ -787,9 +744,8 @@ pub struct Sig {
ret: ty::Id,
}
#[derive(Default)]
struct Func {
file: Module,
file: FileId,
name: Ident,
base: Option<ty::Func>,
expr: ExprRef,
@ -797,6 +753,19 @@ struct Func {
comp_state: [CompState; 2],
}
impl Default for Func {
fn default() -> Self {
Self {
file: u32::MAX,
name: Default::default(),
base: None,
expr: Default::default(),
sig: None,
comp_state: Default::default(),
}
}
}
#[derive(Default, PartialEq, Eq)]
enum CompState {
#[default]
@ -811,19 +780,23 @@ struct TypedReloc {
reloc: Reloc,
}
#[derive(Clone, Default)]
#[derive(Clone)]
struct Global {
file: Module,
file: FileId,
name: Ident,
ty: ty::Id,
data: Vec<u8>,
}
#[derive(PartialEq, Eq, Hash)]
pub struct Const {
ast: ExprRef,
name: Ident,
file: Module,
impl Default for Global {
fn default() -> Self {
Self {
ty: Default::default(),
data: Default::default(),
file: u32::MAX,
name: Default::default(),
}
}
}
// TODO: make into bit struct (width: u2, sub_offset: u3, offset: u27)
@ -862,7 +835,7 @@ struct Field {
struct Struct {
name: Ident,
pos: Pos,
file: Module,
file: FileId,
size: Cell<Size>,
align: Cell<u8>,
captures: ty::Tuple,
@ -961,19 +934,18 @@ struct TypesTmp {
#[derive(Default)]
pub struct TypeIns {
funcs: Vec<Func>,
args: Vec<ty::Id>,
globals: Vec<Global>,
structs: Vec<Struct>,
fields: Vec<Field>,
funcs: EntVec<ty::Func, Func>,
globals: EntVec<ty::Global, Global>,
consts: EntVec<ty::Const, Const>,
structs: EntVec<ty::Struct, Struct>,
ptrs: EntVec<ty::Ptr, Ptr>,
opts: EntVec<ty::Opt, Opt>,
slices: EntVec<ty::Slice, Array>,
ptrs: Vec<Ptr>,
opts: Vec<Opt>,
slices: Vec<Array>,
}
struct FTask {
file: Module,
file: FileId,
id: ty::Func,
ct: bool,
}
@ -981,11 +953,11 @@ struct FTask {
struct StringRef(ty::Global);
impl ctx_map::CtxEntry for StringRef {
type Ctx = EntVec<ty::Global, Global>;
type Ctx = [Global];
type Key<'a> = &'a [u8];
fn key<'a>(&self, ctx: &'a Self::Ctx) -> Self::Key<'a> {
&ctx[self.0].data
&ctx[self.0 as usize].data
}
}
@ -1003,16 +975,16 @@ trait TypeParser {
fn tys(&mut self) -> &mut Types;
fn on_reuse(&mut self, existing: ty::Id);
fn find_local_ty(&mut self, name: Ident) -> Option<ty::Id>;
fn eval_const(&mut self, file: Module, expr: &Expr, ty: ty::Id) -> u64;
fn eval_global(&mut self, file: Module, name: Ident, expr: &Expr) -> ty::Id;
fn eval_const(&mut self, file: FileId, expr: &Expr, ty: ty::Id) -> u64;
fn eval_global(&mut self, file: FileId, name: Ident, expr: &Expr) -> ty::Id;
fn infer_type(&mut self, expr: &Expr) -> ty::Id;
fn report(&self, file: Module, pos: Pos, msg: impl Display) -> ty::Id;
fn report(&self, file: FileId, pos: Pos, msg: impl Display) -> ty::Id;
fn find_type(
&mut self,
pos: Pos,
from_file: Module,
file: Module,
from_file: FileId,
file: FileId,
id: Result<Ident, &str>,
files: &[parser::Ast],
) -> ty::Id {
@ -1027,9 +999,9 @@ trait TypeParser {
self.on_reuse(ty);
ty
} else {
let f = &files[file.index()];
let f = &files[file as usize];
let Some((expr @ Expr::BinOp { left, right, .. }, name)) = f.find_decl(id) else {
let Some((Expr::BinOp { left, right, .. }, name)) = f.find_decl(id) else {
return match id {
Ok(_) => ty::Id::NEVER,
Err("main") => self.report(
@ -1053,19 +1025,20 @@ trait TypeParser {
ty
} else {
let ty = left
.find_pattern_path(name, right, |right, is_ct| {
if is_ct {
self.tys()
.ins
.consts
.push(Const { ast: ExprRef::new(expr), name, file })
.into()
} else {
self.parse_ty(file, right, Some(name), files)
}
.find_pattern_path(name, right, |right| {
self.parse_ty(file, right, Some(name), files)
})
.unwrap_or_else(|_| unreachable!());
let tys = self.tys();
let nm = match ty.expand() {
ty::Kind::Struct(s) => &mut tys.ins.structs[s as usize].name,
ty::Kind::Func(s) => &mut tys.ins.funcs[s as usize].name,
ty::Kind::Global(s) => &mut tys.ins.globals[s as usize].name,
_ => &mut Ident::default(),
};
if nm.is_null() {
*nm = name;
}
tys.syms.insert(SymKey::Decl(file, name), ty, &tys.ins);
ty
}
@ -1073,7 +1046,7 @@ trait TypeParser {
let tys = self.tys();
if let ty::Kind::Global(g) = ty.expand() {
let g = &tys.ins.globals[g];
let g = &tys.ins.globals[g as usize];
if g.ty == ty::Id::TYPE {
return ty::Id::from(
u32::from_ne_bytes(g.data.as_slice().try_into().unwrap()) as u64
@ -1086,7 +1059,7 @@ trait TypeParser {
/// returns none if comptime eval is required
fn parse_ty(
&mut self,
file: Module,
file: FileId,
expr: &Expr,
name: Option<Ident>,
files: &[parser::Ast],
@ -1101,7 +1074,7 @@ trait TypeParser {
let base = self.parse_ty(file, val, None, files);
self.tys().make_opt(base)
}
Expr::Ident { id, .. } if let Ok(bt) = ty::Builtin::try_from(id) => bt.into(),
Expr::Ident { id, .. } if id.is_null() => id.len().into(),
Expr::Ident { id, pos, .. } => self.find_type(pos, file, file, Ok(id), files),
Expr::Field { target, pos, name }
if let ty::Kind::Module(inside) =
@ -1146,21 +1119,18 @@ trait TypeParser {
}
let tys = self.tys();
let ty = tys
.ins
.structs
.push(Struct {
file,
pos,
name: name.unwrap_or_default(),
field_start: tys.ins.fields.len() as _,
explicit_alignment: packed.then_some(1),
..Default::default()
})
.into();
tys.ins.structs.push(Struct {
file,
pos,
name: name.unwrap_or_default(),
field_start: tys.ins.fields.len() as _,
explicit_alignment: packed.then_some(1),
..Default::default()
});
tys.ins.fields.extend(tys.tmp.fields.drain(prev_tmp..));
let ty = ty::Kind::Struct(tys.ins.structs.len() as u32 - 1).compress();
tys.syms.insert(sym, ty, &tys.ins);
ty
}
@ -1171,7 +1141,7 @@ trait TypeParser {
sig: 'b: {
let arg_base = self.tys().tmp.args.len();
for arg in args {
let sym = parser::find_symbol(&files[file.index()].symbols, arg.id);
let sym = parser::find_symbol(&files[file as usize].symbols, arg.id);
if sym.flags & idfl::COMPTIME != 0 {
self.tys().tmp.args.truncate(arg_base);
break 'b None;
@ -1191,7 +1161,10 @@ trait TypeParser {
..Default::default()
};
self.tys().ins.funcs.push(func).into()
let id = self.tys().ins.funcs.len() as _;
self.tys().ins.funcs.push(func);
ty::Kind::Func(id).compress()
}
_ if let Some(name) = name => self.eval_global(file, name, expr),
_ => ty::Id::from(self.eval_const(file, expr, ty::Id::TYPE)),
@ -1201,9 +1174,12 @@ trait TypeParser {
impl Types {
fn struct_field_range(&self, strct: ty::Struct) -> Range<usize> {
let start = self.ins.structs[strct].field_start as usize;
let end =
self.ins.structs.next(strct).map_or(self.ins.fields.len(), |s| s.field_start as usize);
let start = self.ins.structs[strct as usize].field_start as usize;
let end = self
.ins
.structs
.get(strct as usize + 1)
.map_or(self.ins.fields.len(), |s| s.field_start as usize);
start..end
}
@ -1234,30 +1210,49 @@ impl Types {
}
fn make_opt(&mut self, base: ty::Id) -> ty::Id {
self.make_generic_ty(Opt { base }, |ins| &mut ins.opts, |e| SymKey::Optional(e))
self.make_generic_ty(
Opt { base },
|ins| &mut ins.opts,
|e| SymKey::Optional(e),
ty::Kind::Opt,
)
}
fn make_ptr(&mut self, base: ty::Id) -> ty::Id {
self.make_generic_ty(Ptr { base }, |ins| &mut ins.ptrs, |e| SymKey::Pointer(e))
self.make_generic_ty(
Ptr { base },
|ins| &mut ins.ptrs,
|e| SymKey::Pointer(e),
ty::Kind::Ptr,
)
}
fn make_array(&mut self, elem: ty::Id, len: ArrayLen) -> ty::Id {
self.make_generic_ty(Array { elem, len }, |ins| &mut ins.slices, |e| SymKey::Array(e))
self.make_generic_ty(
Array { elem, len },
|ins| &mut ins.slices,
|e| SymKey::Array(e),
ty::Kind::Slice,
)
}
fn make_generic_ty<K: Ent + Into<ty::Id>, T: Copy>(
fn make_generic_ty<T: Copy>(
&mut self,
ty: T,
get_col: fn(&mut TypeIns) -> &mut EntVec<K, T>,
get_col: fn(&mut TypeIns) -> &mut Vec<T>,
key: fn(&T) -> SymKey,
kind: fn(u32) -> ty::Kind,
) -> ty::Id {
*self.syms.get_or_insert(key(&{ ty }), &mut self.ins, |ins| get_col(ins).push(ty).into())
*self.syms.get_or_insert(key(&{ ty }), &mut self.ins, |ins| {
get_col(ins).push(ty);
kind(get_col(ins).len() as u32 - 1).compress()
})
}
fn size_of(&self, ty: ty::Id) -> Size {
match ty.expand() {
ty::Kind::Slice(arr) => {
let arr = &self.ins.slices[arr];
let arr = &self.ins.slices[arr as usize];
match arr.len {
0 => 0,
ArrayLen::MAX => 16,
@ -1265,17 +1260,17 @@ impl Types {
}
}
ty::Kind::Struct(stru) => {
if self.ins.structs[stru].size.get() != 0 {
return self.ins.structs[stru].size.get();
if self.ins.structs[stru as usize].size.get() != 0 {
return self.ins.structs[stru as usize].size.get();
}
let mut oiter = OffsetIter::new(stru, self);
while oiter.next(self).is_some() {}
self.ins.structs[stru].size.set(oiter.offset);
self.ins.structs[stru as usize].size.set(oiter.offset);
oiter.offset
}
ty::Kind::Opt(opt) => {
let base = self.ins.opts[opt].base;
let base = self.ins.opts[opt as usize].base;
if self.nieche_of(base).is_some() {
self.size_of(base)
} else {
@ -1290,10 +1285,10 @@ impl Types {
fn align_of(&self, ty: ty::Id) -> Size {
match ty.expand() {
ty::Kind::Struct(stru) => {
if self.ins.structs[stru].align.get() != 0 {
return self.ins.structs[stru].align.get() as _;
if self.ins.structs[stru as usize].align.get() != 0 {
return self.ins.structs[stru as usize].align.get() as _;
}
let align = self.ins.structs[stru].explicit_alignment.map_or_else(
let align = self.ins.structs[stru as usize].explicit_alignment.map_or_else(
|| {
self.struct_fields(stru)
.iter()
@ -1303,11 +1298,11 @@ impl Types {
},
|a| a as _,
);
self.ins.structs[stru].align.set(align.try_into().unwrap());
self.ins.structs[stru as usize].align.set(align.try_into().unwrap());
align
}
ty::Kind::Slice(arr) => {
let arr = &self.ins.slices[arr];
let arr = &self.ins.slices[arr as usize];
match arr.len {
ArrayLen::MAX => 8,
_ => self.align_of(arr.elem),
@ -1319,14 +1314,14 @@ impl Types {
fn base_of(&self, ty: ty::Id) -> Option<ty::Id> {
match ty.expand() {
ty::Kind::Ptr(p) => Some(self.ins.ptrs[p].base),
ty::Kind::Ptr(p) => Some(self.ins.ptrs[p as usize].base),
_ => None,
}
}
fn inner_of(&self, ty: ty::Id) -> Option<ty::Id> {
match ty.expand() {
ty::Kind::Opt(o) => Some(self.ins.opts[o].base),
ty::Kind::Opt(o) => Some(self.ins.opts[o as usize].base),
_ => None,
}
}
@ -1406,7 +1401,7 @@ impl OffsetIter {
}
fn next<'a>(&mut self, tys: &'a Types) -> Option<(&'a Field, Offset)> {
let stru = &tys.ins.structs[self.strct];
let stru = &tys.ins.structs[self.strct as usize];
let field = &tys.ins.fields[self.fields.next()?];
let align = stru.explicit_alignment.map_or_else(|| tys.align_of(field.ty), |a| a as u32);
@ -1584,10 +1579,12 @@ fn test_parse_files(
FileKind::Module => module_map
.iter()
.position(|&(name, _)| name == path)
.map(|i| i as parser::FileId)
.ok_or("Module Not Found".to_string()),
FileKind::Embed => embed_map
.iter()
.position(|&(name, _)| name == path)
.map(|i| i as parser::FileId)
.ok_or("Embed Not Found".to_string()),
};

View file

@ -1,26 +1,17 @@
#[cfg(feature = "std")]
fn main() {
fn main() -> std::io::Result<()> {
use std::io::Write;
fn run(out: &mut Vec<u8>) -> std::io::Result<()> {
let args = std::env::args().collect::<Vec<_>>();
let args = args.iter().map(String::as_str).collect::<Vec<_>>();
log::set_logger(&hblang::Logger).unwrap();
log::set_max_level(log::LevelFilter::Info);
let opts = hblang::Options::from_args(&args, out)?;
let file = args.iter().filter(|a| !a.starts_with('-')).nth(1).copied().unwrap_or("main.hb");
let args = std::env::args().collect::<Vec<_>>();
let args = args.iter().map(String::as_str).collect::<Vec<_>>();
hblang::run_compiler(file, opts, out)
}
log::set_logger(&hblang::fs::Logger).unwrap();
log::set_max_level(log::LevelFilter::Error);
let opts = hblang::Options::from_args(&args)?;
let file = args.iter().filter(|a| !a.starts_with('-')).nth(1).copied().unwrap_or("main.hb");
let mut out = Vec::new();
match run(&mut out) {
Ok(_) => std::io::stdout().write_all(&out).unwrap(),
Err(_) => {
std::io::stderr().write_all(&out).unwrap();
std::process::exit(1);
}
}
hblang::run_compiler(file, opts, &mut out)?;
std::io::stdout().write_all(&out)
}

View file

@ -2,8 +2,6 @@ use {
crate::{
fmt::Formatter,
lexer::{self, Lexer, Token, TokenKind},
ty::{Global, Module},
utils::Ent as _,
Ident,
},
alloc::{boxed::Box, string::String, vec::Vec},
@ -21,9 +19,10 @@ use {
pub type Pos = u32;
pub type IdentFlags = u32;
pub type FileId = u32;
pub type IdentIndex = u16;
pub type LoaderError = String;
pub type Loader<'a> = &'a mut (dyn FnMut(&str, &str, FileKind) -> Result<usize, LoaderError> + 'a);
pub type Loader<'a> = &'a mut (dyn FnMut(&str, &str, FileKind) -> Result<FileId, LoaderError> + 'a);
#[derive(PartialEq, Eq, Debug)]
pub enum FileKind {
@ -64,7 +63,7 @@ pub mod idfl {
}
}
pub fn no_loader(_: &str, _: &str, _: FileKind) -> Result<usize, LoaderError> {
pub fn no_loader(_: &str, _: &str, _: FileKind) -> Result<FileId, LoaderError> {
Ok(0)
}
@ -283,14 +282,6 @@ impl<'a, 'b> Parser<'a, 'b> {
fn unit_expr(&mut self) -> Option<Expr<'a>> {
use {Expr as E, TokenKind as T};
if matches!(
self.token.kind,
T::RParen | T::RBrace | T::RBrack | T::Comma | T::Semi | T::Else
) {
self.report(self.token.start, "expected expression")?;
}
let frame = self.ctx.idents.len();
let token @ Token { start: pos, .. } = self.next();
let prev_boundary = self.ns_bound;
@ -308,7 +299,7 @@ impl<'a, 'b> Parser<'a, 'b> {
pos,
path,
id: match (self.loader)(path, self.path, FileKind::Module) {
Ok(id) => Module::new(id),
Ok(id) => id,
Err(e) => {
self.report(str.start, format_args!("error loading dependency: {e:#}"))?
}
@ -326,7 +317,7 @@ impl<'a, 'b> Parser<'a, 'b> {
pos,
path,
id: match (self.loader)(path, self.path, FileKind::Embed) {
Ok(id) => Global::new(id),
Ok(id) => id,
Err(e) => self.report(
str.start,
format_args!("error loading embedded file: {e:#}"),
@ -930,13 +921,13 @@ generate_expr! {
/// `'@use' '(' String ')'`
Mod {
pos: Pos,
id: Module,
id: FileId,
path: &'a str,
},
/// `'@use' '(' String ')'`
Embed {
pos: Pos,
id: Global,
id: FileId,
path: &'a str,
},
}
@ -961,14 +952,14 @@ impl Expr<'_> {
}
}
pub fn find_pattern_path<T, F: FnOnce(&Expr, bool) -> T>(
pub fn find_pattern_path<T, F: FnOnce(&Expr) -> T>(
&self,
ident: Ident,
target: &Expr,
mut with_final: F,
) -> Result<T, F> {
match *self {
Self::Ident { id, is_ct, .. } if id == ident => Ok(with_final(target, is_ct)),
Self::Ident { id, .. } if id == ident => Ok(with_final(target)),
Self::Ctor { fields, .. } => {
for &CtorField { name, value, pos } in fields {
match value.find_pattern_path(
@ -1235,7 +1226,7 @@ impl Default for Ast {
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[derive(Clone, Copy)]
#[repr(packed)]
pub struct ExprRef(NonNull<Expr<'static>>);

View file

@ -1,2 +1,150 @@
use {crate::reg::Reg, alloc::vec::Vec, core::ops::Range};
type Nid = u16;
pub trait Ctx {
fn uses_of(&self, nid: Nid) -> impl Iterator<Item = Nid>;
fn params_of(&self, nid: Nid) -> impl Iterator<Item = Nid>;
fn args_of(&self, nid: Nid) -> impl Iterator<Item = Nid>;
fn dom_of(&self, nid: Nid) -> Nid;
}
pub struct Env<'a, C: Ctx> {
ctx: &'a C,
func: &'a Func,
res: &'a mut Res,
}
impl<'a, C: Ctx> Env<'a, C> {
pub fn new(ctx: &'a C, func: &'a Func, res: &'a mut Res) -> Self {
Self { ctx, func, res }
}
pub fn run(&mut self) {
self.res.reg_to_node.clear();
self.res.reg_to_node.resize(self.func.instrs.len(), 0);
let mut bundle = Bundle::new(self.func.instrs.len());
for &inst in &self.func.instrs {
for uinst in self.ctx.uses_of(inst) {
let mut cursor = self.ctx.dom_of(uinst);
while cursor != self.ctx.dom_of(inst) {
let mut range = self.func.blocks
[self.func.id_to_block[cursor as usize] as usize]
.range
.clone();
range.start = range.start.max(inst as usize);
range.end = range.end.min(uinst as usize);
bundle.add(range);
cursor = self.ctx.dom_of(cursor);
}
}
match self.res.bundles.iter_mut().enumerate().find(|(_, b)| !b.overlaps(&bundle)) {
Some((i, other)) => {
other.merge(&bundle);
bundle.clear();
self.res.reg_to_node[inst as usize] = i as Reg;
}
None => {
self.res.reg_to_node[inst as usize] = self.res.bundles.len() as Reg;
self.res.bundles.push(bundle);
bundle = Bundle::new(self.func.instrs.len());
}
}
}
}
}
pub struct Res {
bundles: Vec<Bundle>,
pub reg_to_node: Vec<Reg>,
}
pub struct Bundle {
//unit_range: Range<usize>,
//set: BitSet,
taken: Vec<bool>,
}
impl Bundle {
fn new(size: usize) -> Self {
Self { taken: vec![false; size] }
}
fn add(&mut self, range: Range<usize>) {
self.taken[range].fill(true);
}
fn overlaps(&self, other: &Self) -> bool {
self.taken.iter().zip(other.taken.iter()).any(|(a, b)| a & b)
}
fn merge(&mut self, other: &Self) {
debug_assert!(!self.overlaps(other));
self.taken.iter_mut().zip(other.taken.iter()).for_each(|(a, b)| *a = *b);
}
fn clear(&mut self) {
self.taken.fill(false);
}
//fn overlaps(&self, other: &Self) -> bool {
// if self.unit_range.start >= other.unit_range.end
// || self.unit_range.end <= other.unit_range.start
// {
// return false;
// }
// let [mut a, mut b] = [self, other];
// if a.unit_range.start > b.unit_range.start {
// mem::swap(&mut a, &mut b);
// }
// let [mut tmp_a, mut tmp_b] = [0; 2];
// let [units_a, units_b] = [a.set.units(&mut tmp_a), b.set.units(&mut tmp_b)];
// let len = a.unit_range.len().min(b.unit_range.len());
// let [units_a, units_b] =
// [&units_a[b.unit_range.start - a.unit_range.start..][..len], &units_b[..len]];
// units_a.iter().zip(units_b).any(|(&a, &b)| a & b != 0)
//}
//fn merge(mut self, mut other: Self) -> Self {
// debug_assert!(!self.overlaps(&other));
// if self.unit_range.start > other.unit_range.start {
// mem::swap(&mut self, &mut other);
// }
// let final_range = self.unit_range.start..self.unit_range.end.max(other.unit_range.end);
// self.set.reserve(final_range.len());
// let mut tmp = 0;
// let other_units = other.set.units(&mut tmp);
// match self.set.units_mut() {
// Ok(units) => {
// units[other.unit_range.start - self.unit_range.start..]
// .iter_mut()
// .zip(other_units)
// .for_each(|(a, b)| *a |= b);
// }
// Err(view) => view.add_mask(tmp),
// }
// self
//}
}
pub struct Func {
pub blocks: Vec<Block>,
pub instrs: Vec<Nid>,
pub id_to_instr: Vec<Nid>,
pub id_to_block: Vec<Nid>,
}
pub struct Block {
pub range: Range<usize>,
pub start_id: Nid,
pub eld_id: Nid,
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,903 +0,0 @@
use {
super::{HbvmBackend, Nid, Nodes},
crate::{
lexer::TokenKind,
parser,
reg::{self, Reg},
son::{debug_assert_matches, Kind, ARG_START, MEM, VOID},
ty::{self, Arg, Loc},
utils::{BitSet, Vc},
Offset, PLoc, Reloc, Sig, TypedReloc, Types,
},
alloc::{borrow::ToOwned, vec::Vec},
core::{mem, ops::Range},
hbbytecode::{self as instrs},
};
impl HbvmBackend {
pub fn emit_body_code_my(
&mut self,
nodes: &mut Nodes,
sig: Sig,
tys: &Types,
files: &[parser::Ast],
) -> (usize, bool) {
let mut fuc = Function::new(nodes, tys, sig);
log::info!("{fuc:?}");
let mut res = mem::take(&mut self.ralloc_my);
Env::new(&fuc, &fuc.func, &mut res).run();
'_open_function: {
self.emit(instrs::addi64(reg::STACK_PTR, reg::STACK_PTR, 0));
self.emit(instrs::st(reg::RET_ADDR + fuc.tail as u8, reg::STACK_PTR, 0, 0));
}
let reg_offset = if fuc.tail { reg::RET + 12 } else { reg::RET_ADDR + 1 };
res.node_to_reg.iter_mut().filter(|r| **r != 0).for_each(|r| {
*r += reg_offset - 1;
if fuc.tail && *r >= reg::RET_ADDR {
*r += 1;
}
});
let atr = |allc: Nid| res.node_to_reg[allc as usize];
//for (id, node) in fuc.nodes.iter() {
// if node.kind == Kind::Phi {
// debug_assert_eq!(atr(node.inputs[1]), atr(node.inputs[2]));
// debug_assert_eq!(atr(id), atr(node.inputs[2]));
// }
//}
let (retl, mut parama) = tys.parama(sig.ret);
let mut typs = sig.args.args();
let mut args = fuc.nodes[VOID].outputs[ARG_START..].iter();
while let Some(aty) = typs.next(tys) {
let Arg::Value(ty) = aty else { continue };
let Some(loc) = parama.next(ty, tys) else { continue };
let &arg = args.next().unwrap();
let (rg, size) = match loc {
PLoc::WideReg(rg, size) => (rg, size),
PLoc::Reg(rg, size) if ty.loc(tys) == Loc::Stack => (rg, size),
PLoc::Reg(r, ..) | PLoc::Ref(r, ..) => {
self.emit(instrs::cp(atr(arg), r));
continue;
}
};
self.emit(instrs::st(rg, reg::STACK_PTR, self.offsets[arg as usize] as _, size));
if fuc.nodes[arg].lock_rc == 0 {
self.emit(instrs::addi64(rg, reg::STACK_PTR, self.offsets[arg as usize] as _));
}
self.emit(instrs::cp(atr(arg), rg));
}
for (i, block) in fuc.func.blocks.iter().enumerate() {
self.offsets[block.entry as usize] = self.code.len() as _;
for &nid in &fuc.func.instrs[block.range.clone()] {
if nid == VOID {
continue;
}
let node = &fuc.nodes[nid];
let extend = |base: ty::Id, dest: ty::Id, from: Nid, to: Nid| {
let (bsize, dsize) = (tys.size_of(base), tys.size_of(dest));
debug_assert!(bsize <= 8, "{}", ty::Display::new(tys, files, base));
debug_assert!(dsize <= 8, "{}", ty::Display::new(tys, files, dest));
if bsize == dsize {
return Default::default();
}
match (base.is_signed(), dest.is_signed()) {
(true, true) => {
let op = [instrs::sxt8, instrs::sxt16, instrs::sxt32]
[bsize.ilog2() as usize];
op(atr(to), atr(from))
}
_ => {
let mask = (1u64 << (bsize * 8)) - 1;
instrs::andi(atr(to), atr(from), mask)
}
}
};
match node.kind {
Kind::If => {
let &[_, cnd] = node.inputs.as_slice() else { unreachable!() };
if let Kind::BinOp { op } = fuc.nodes[cnd].kind
&& let Some((op, swapped)) =
op.cond_op(fuc.nodes[fuc.nodes[cnd].inputs[1]].ty)
{
let &[_, lhs, rhs] = fuc.nodes[cnd].inputs.as_slice() else {
unreachable!()
};
self.emit(extend(fuc.nodes[lhs].ty, fuc.nodes[lhs].ty.extend(), 0, 0));
self.emit(extend(fuc.nodes[rhs].ty, fuc.nodes[rhs].ty.extend(), 1, 1));
let rel = Reloc::new(self.code.len(), 3, 2);
self.jump_relocs.push((node.outputs[!swapped as usize], rel));
self.emit(op(atr(lhs), atr(rhs), 0));
} else {
self.emit(extend(fuc.nodes[cnd].ty, fuc.nodes[cnd].ty.extend(), 0, 0));
let rel = Reloc::new(self.code.len(), 3, 2);
self.jump_relocs.push((node.outputs[0], rel));
self.emit(instrs::jne(atr(cnd), reg::ZERO, 0));
}
}
Kind::Loop | Kind::Region => {
if (mem::replace(&mut fuc.backrefs[nid as usize], u16::MAX) != u16::MAX)
^ (node.kind == Kind::Loop)
{
let index = (node.kind == Kind::Loop) as usize + 1;
for &out in node.outputs.iter() {
if fuc.nodes[out].is_data_phi()
&& atr(out) != atr(fuc.nodes[out].inputs[index])
{
self.emit(instrs::cp(
atr(out),
atr(fuc.nodes[out].inputs[index]),
));
}
}
let rel = Reloc::new(self.code.len(), 1, 4);
self.jump_relocs.push((nid, rel));
self.emit(instrs::jmp(0));
} else {
let index = (node.kind != Kind::Loop) as usize + 1;
for &out in node.outputs.iter() {
if fuc.nodes[out].is_data_phi()
&& atr(out) != atr(fuc.nodes[out].inputs[index])
{
self.emit(instrs::cp(
atr(out),
atr(fuc.nodes[out].inputs[index]),
));
}
}
}
}
Kind::Return => {
let &[_, mut ret, ..] = node.inputs.as_slice() else { unreachable!() };
match retl {
None => {}
Some(PLoc::Reg(r, _)) if sig.ret.loc(tys) == Loc::Reg => {
self.emit(instrs::cp(r, atr(ret)));
}
Some(PLoc::Reg(r, size)) | Some(PLoc::WideReg(r, size)) => {
ret = match fuc.nodes[ret].kind {
Kind::Load { .. } => fuc.nodes[ret].inputs[1],
_ => ret,
};
self.emit(instrs::ld(r, atr(ret), 0, size))
}
Some(PLoc::Ref(_, size)) => {
ret = match fuc.nodes[ret].kind {
Kind::Load { .. } => fuc.nodes[ret].inputs[1],
_ => ret,
};
let [src, dst] = [atr(ret), atr(MEM)];
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 _));
}
}
}
if i != fuc.func.blocks.len() - 1 {
let rel = Reloc::new(self.code.len(), 1, 4);
self.ret_relocs.push(rel);
self.emit(instrs::jmp(0));
}
}
Kind::Die => self.emit(instrs::un()),
Kind::CInt { value } if node.ty.is_float() => {
self.emit(match node.ty {
ty::Id::F32 => instrs::li32(
atr(nid),
(f64::from_bits(value as _) as f32).to_bits(),
),
ty::Id::F64 => instrs::li64(atr(nid), value as _),
_ => unreachable!(),
});
}
Kind::CInt { value } => self.emit(match tys.size_of(node.ty) {
1 => instrs::li8(atr(nid), value as _),
2 => instrs::li16(atr(nid), value as _),
4 => instrs::li32(atr(nid), value as _),
_ => instrs::li64(atr(nid), value as _),
}),
Kind::UnOp { op } => {
let op = op
.unop(node.ty, fuc.nodes[node.inputs[1]].ty)
.expect("TODO: unary operator not supported");
self.emit(op(atr(nid), atr(node.inputs[1])));
}
Kind::BinOp { .. } if node.lock_rc != 0 => {}
Kind::BinOp { op } => {
let &[.., lhs, rhs] = node.inputs.as_slice() else { unreachable!() };
if let Kind::CInt { value } = fuc.nodes[rhs].kind
&& fuc.nodes[rhs].lock_rc != 0
&& let Some(op) = op.imm_binop(node.ty)
{
self.emit(op(atr(nid), atr(lhs), value as _));
} else if let Some(op) =
op.binop(node.ty).or(op.float_cmp(fuc.nodes[lhs].ty))
{
self.emit(op(atr(nid), atr(lhs), atr(rhs)));
} else if let Some(against) = op.cmp_against() {
let op_ty = fuc.nodes[lhs].ty;
self.emit(extend(fuc.nodes[lhs].ty, fuc.nodes[lhs].ty.extend(), 0, 0));
self.emit(extend(fuc.nodes[rhs].ty, fuc.nodes[rhs].ty.extend(), 1, 1));
if op_ty.is_float() && matches!(op, TokenKind::Le | TokenKind::Ge) {
let opop = match op {
TokenKind::Le => TokenKind::Gt,
TokenKind::Ge => TokenKind::Lt,
_ => unreachable!(),
};
let op_fn = opop.float_cmp(op_ty).unwrap();
self.emit(op_fn(atr(nid), atr(lhs), atr(rhs)));
self.emit(instrs::not(atr(nid), atr(nid)));
} else if op_ty.is_integer() {
let op_fn =
if op_ty.is_signed() { instrs::cmps } else { instrs::cmpu };
self.emit(op_fn(atr(nid), atr(lhs), atr(rhs)));
self.emit(instrs::cmpui(atr(nid), atr(nid), against));
if matches!(op, TokenKind::Eq | TokenKind::Lt | TokenKind::Gt) {
self.emit(instrs::not(atr(nid), atr(nid)));
}
} else {
todo!("unhandled operator: {op}");
}
} else {
todo!("unhandled operator: {op}");
}
}
Kind::Call { args, func } => {
let (ret, mut parama) = tys.parama(node.ty);
let mut args = args.args();
let mut allocs = node.inputs[1..].iter();
while let Some(arg) = args.next(tys) {
let Arg::Value(ty) = arg else { continue };
let Some(loc) = parama.next(ty, tys) else { continue };
let mut arg = *allocs.next().unwrap();
let (rg, size) = match loc {
PLoc::Reg(rg, size) if ty.loc(tys) == Loc::Stack => (rg, size),
PLoc::WideReg(rg, size) => (rg, size),
PLoc::Ref(r, ..) => {
arg = match fuc.nodes[arg].kind {
Kind::Load { .. } => fuc.nodes[arg].inputs[1],
_ => arg,
};
self.emit(instrs::cp(r, atr(arg)));
continue;
}
PLoc::Reg(r, ..) => {
self.emit(instrs::cp(r, atr(arg)));
continue;
}
};
arg = match fuc.nodes[arg].kind {
Kind::Load { .. } => fuc.nodes[arg].inputs[1],
_ => arg,
};
self.emit(instrs::ld(rg, atr(arg), 0, size));
}
debug_assert!(
!matches!(ret, Some(PLoc::Ref(..))) || allocs.next().is_some()
);
if func == ty::Func::ECA {
self.emit(instrs::eca());
} else {
self.relocs.push(TypedReloc {
target: ty::Kind::Func(func).compress(),
reloc: Reloc::new(self.code.len(), 3, 4),
});
self.emit(instrs::jal(reg::RET_ADDR, reg::ZERO, 0));
}
match ret {
Some(PLoc::WideReg(r, size)) => {
debug_assert_eq!(
fuc.nodes[*node.inputs.last().unwrap()].kind,
Kind::Stck
);
let stck = self.offsets[*node.inputs.last().unwrap() as usize];
self.emit(instrs::st(r, reg::STACK_PTR, stck as _, size));
}
Some(PLoc::Reg(r, size)) if node.ty.loc(tys) == Loc::Stack => {
debug_assert_eq!(
fuc.nodes[*node.inputs.last().unwrap()].kind,
Kind::Stck
);
let stck = self.offsets[*node.inputs.last().unwrap() as usize];
self.emit(instrs::st(r, reg::STACK_PTR, stck as _, size));
}
Some(PLoc::Reg(r, ..)) => self.emit(instrs::cp(atr(nid), r)),
None | Some(PLoc::Ref(..)) => {}
}
}
Kind::Global { global } => {
let reloc = Reloc::new(self.code.len(), 3, 4);
self.relocs.push(TypedReloc {
target: ty::Kind::Global(global).compress(),
reloc,
});
self.emit(instrs::lra(atr(nid), 0, 0));
}
Kind::Stck => {
let base = reg::STACK_PTR;
let offset = self.offsets[nid as usize];
self.emit(instrs::addi64(atr(nid), base, offset as _));
}
Kind::Load => {
let mut region = node.inputs[1];
let mut offset = 0;
if fuc.nodes[region].kind == (Kind::BinOp { op: TokenKind::Add })
&& let Kind::CInt { value } =
fuc.nodes[fuc.nodes[region].inputs[2]].kind
{
region = fuc.nodes[region].inputs[1];
offset = value as Offset;
}
let size = tys.size_of(node.ty);
if node.ty.loc(tys) != Loc::Stack {
let (base, offset) = match fuc.nodes[region].kind {
Kind::Stck => {
(reg::STACK_PTR, self.offsets[region as usize] + offset)
}
_ => (atr(region), offset),
};
self.emit(instrs::ld(atr(nid), base, offset as _, size as _));
}
}
Kind::Stre if node.inputs[1] == VOID => {}
Kind::Stre => {
let mut region = node.inputs[2];
let mut offset = 0;
let size = u16::try_from(tys.size_of(node.ty)).expect("TODO");
if fuc.nodes[region].kind == (Kind::BinOp { op: TokenKind::Add })
&& let Kind::CInt { value } =
fuc.nodes[fuc.nodes[region].inputs[2]].kind
&& node.ty.loc(tys) == Loc::Reg
{
region = fuc.nodes[region].inputs[1];
offset = value as Offset;
}
let nd = &fuc.nodes[region];
let value = node.inputs[1];
let (base, offset, src) = match nd.kind {
Kind::Stck if node.ty.loc(tys) == Loc::Reg => {
(reg::STACK_PTR, self.offsets[region as usize] + offset, value)
}
_ => (atr(region), offset, match fuc.nodes[value].kind {
Kind::Load { .. } => fuc.nodes[value].inputs[1],
_ => value,
}),
};
match node.ty.loc(tys) {
Loc::Reg => self.emit(instrs::st(atr(src), base, offset as _, size)),
Loc::Stack => {
debug_assert_eq!(offset, 0);
self.emit(instrs::bmc(atr(src), base, size))
}
}
}
Kind::Mem => self.emit(instrs::cp(atr(MEM), reg::RET)),
Kind::Arg => {}
e @ (Kind::Start
| Kind::Entry
| Kind::End
| Kind::Loops
| Kind::Then
| Kind::Else
| Kind::Phi
| Kind::Assert { .. }) => unreachable!("{e:?}"),
}
}
}
self.ralloc_my = res;
let bundle_count = self.ralloc_my.bundles.len() + (reg_offset as usize);
(
if fuc.tail {
bundle_count.saturating_sub(reg::RET_ADDR as _)
} else {
assert!(bundle_count < reg::STACK_PTR as usize, "TODO: spill memory");
self.ralloc_my.bundles.len()
},
fuc.tail,
)
}
}
pub struct Function<'a> {
sig: Sig,
tail: bool,
backrefs: Vec<u16>,
nodes: &'a mut Nodes,
tys: &'a Types,
visited: BitSet,
func: Func,
}
impl Function<'_> {
fn vreg_count(&self) -> usize {
self.nodes.values.len()
}
fn uses_of(&self, nid: Nid, buf: &mut Vec<Nid>) {
if self.nodes[nid].kind.is_cfg() && !matches!(self.nodes[nid].kind, Kind::Call { .. }) {
return;
}
self.nodes[nid]
.outputs
.iter()
.filter(|&&n| self.nodes.is_data_dep(nid, n))
.collect_into(buf);
}
fn phi_inputs_of(&self, nid: Nid, buf: &mut Vec<Nid>) {
match self.nodes[nid].kind {
Kind::Region => {
for &inp in self.nodes[nid].outputs.as_slice() {
if self.nodes[inp].is_data_phi() {
buf.extend(&self.nodes[inp].inputs[1..]);
buf.push(inp);
}
}
}
Kind::Loop => {
for &inp in self.nodes[nid].outputs.as_slice() {
if self.nodes[inp].is_data_phi() {
buf.push(self.nodes[inp].inputs[1]);
buf.push(inp);
buf.push(self.nodes[inp].inputs[2]);
}
}
}
_ => {}
}
}
fn instr_of(&self, nid: Nid) -> Option<Nid> {
if self.nodes[nid].kind == Kind::Phi || self.nodes[nid].lock_rc != 0 {
return None;
}
debug_assert_ne!(self.backrefs[nid as usize], Nid::MAX, "{:?}", self.nodes[nid]);
Some(self.backrefs[nid as usize])
}
fn block_of(&self, nid: Nid) -> Nid {
debug_assert!(self.nodes[nid].kind.starts_basic_block());
self.backrefs[nid as usize]
}
fn idom_of(&self, mut nid: Nid) -> Nid {
while !self.nodes[nid].kind.starts_basic_block() {
nid = self.nodes.idom(nid);
}
nid
}
fn use_block(&self, inst: Nid, uinst: Nid) -> Nid {
let mut block = self.nodes.use_block(inst, uinst);
while !self.nodes[block].kind.starts_basic_block() {
block = self.nodes.idom(block);
}
block
}
}
impl core::fmt::Debug for Function<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
for block in &self.func.blocks {
writeln!(f, "{:?}", self.nodes[block.entry].kind)?;
for &instr in &self.func.instrs[block.range.clone()] {
writeln!(f, "{:?}", self.nodes[instr].kind)?;
}
}
Ok(())
}
}
impl<'a> Function<'a> {
fn new(nodes: &'a mut Nodes, tys: &'a Types, sig: Sig) -> Self {
let mut s = Self {
backrefs: vec![u16::MAX; nodes.values.len()],
tail: true,
nodes,
tys,
sig,
visited: Default::default(),
func: Default::default(),
};
s.visited.clear(s.nodes.values.len());
s.emit_node(VOID);
s
}
fn add_block(&mut self, entry: Nid) {
self.func
.blocks
.push(Block { range: self.func.instrs.len()..self.func.instrs.len(), entry });
self.backrefs[entry as usize] = self.func.blocks.len() as u16 - 1;
}
fn close_block(&mut self, exit: Nid) {
if !matches!(self.nodes[exit].kind, Kind::Loop | Kind::Region) {
self.add_instr(exit);
} else {
self.func.instrs.push(exit);
}
let prev = self.func.blocks.last_mut().unwrap();
prev.range.end = self.func.instrs.len();
}
fn add_instr(&mut self, nid: Nid) {
debug_assert_ne!(self.nodes[nid].kind, Kind::Loop);
self.backrefs[nid as usize] = self.func.instrs.len() as u16;
self.func.instrs.push(nid);
}
fn emit_node(&mut self, nid: Nid) {
if matches!(self.nodes[nid].kind, Kind::Region | Kind::Loop) {
match (self.nodes[nid].kind, self.visited.set(nid)) {
(Kind::Loop, false) | (Kind::Region, true) => {
self.close_block(nid);
return;
}
_ => {}
}
} else if !self.visited.set(nid) {
return;
}
if self.nodes.is_never_used(nid, self.tys) {
self.nodes.lock(nid);
return;
}
let mut node = self.nodes[nid].clone();
match node.kind {
Kind::Start => {
debug_assert_matches!(self.nodes[node.outputs[0]].kind, Kind::Entry);
self.add_block(VOID);
self.emit_node(node.outputs[0])
}
Kind::If => {
let &[_, cond] = node.inputs.as_slice() else { unreachable!() };
let &[mut then, mut else_] = node.outputs.as_slice() else { unreachable!() };
if let Kind::BinOp { op } = self.nodes[cond].kind
&& let Some((_, swapped)) = op.cond_op(node.ty)
&& swapped
{
mem::swap(&mut then, &mut else_);
}
self.close_block(nid);
self.emit_node(then);
self.emit_node(else_);
}
Kind::Region | Kind::Loop => {
self.close_block(nid);
self.add_block(nid);
self.reschedule_block(nid, &mut node.outputs);
for o in node.outputs.into_iter().rev() {
self.emit_node(o);
}
}
Kind::Return | Kind::Die => {
self.close_block(nid);
self.emit_node(node.outputs[0]);
}
Kind::Entry => {
let (ret, mut parama) = self.tys.parama(self.sig.ret);
let mut typs = self.sig.args.args();
#[expect(clippy::unnecessary_to_owned)]
let mut args = self.nodes[VOID].outputs[ARG_START..].to_owned().into_iter();
while let Some(ty) = typs.next_value(self.tys) {
let arg = args.next().unwrap();
debug_assert_eq!(self.nodes[arg].kind, Kind::Arg);
match parama.next(ty, self.tys) {
None => {}
Some(_) => self.add_instr(arg),
}
}
if let Some(PLoc::Ref(..)) = ret {
self.add_instr(MEM);
}
self.reschedule_block(nid, &mut node.outputs);
for o in node.outputs.into_iter().rev() {
self.emit_node(o);
}
}
Kind::Then | Kind::Else => {
self.add_block(nid);
self.reschedule_block(nid, &mut node.outputs);
for o in node.outputs.into_iter().rev() {
self.emit_node(o);
}
}
Kind::Call { func, .. } => {
self.tail &= func == ty::Func::ECA;
self.add_instr(nid);
self.reschedule_block(nid, &mut node.outputs);
for o in node.outputs.into_iter().rev() {
if self.nodes[o].inputs[0] == nid
|| (matches!(self.nodes[o].kind, Kind::Loop | Kind::Region)
&& self.nodes[o].inputs[1] == nid)
{
self.emit_node(o);
}
}
}
Kind::CInt { .. }
| Kind::BinOp { .. }
| Kind::UnOp { .. }
| Kind::Global { .. }
| Kind::Load { .. }
| Kind::Stre
| Kind::Stck => self.add_instr(nid),
Kind::End | Kind::Phi | Kind::Arg | Kind::Mem | Kind::Loops => {}
Kind::Assert { .. } => unreachable!(),
}
}
fn reschedule_block(&mut self, from: Nid, outputs: &mut Vc) {
let from = Some(&from);
let mut buf = Vec::with_capacity(outputs.len());
let mut seen = BitSet::default();
seen.clear(self.nodes.values.len());
for &o in outputs.iter() {
if !self.nodes.is_cfg(o) {
continue;
}
seen.set(o);
let mut cursor = buf.len();
buf.push(o);
while let Some(&n) = buf.get(cursor) {
for &i in &self.nodes[n].inputs[1..] {
if from == self.nodes[i].inputs.first()
&& self.nodes[i]
.outputs
.iter()
.all(|&o| self.nodes[o].inputs.first() != from || seen.get(o))
&& seen.set(i)
{
buf.push(i);
}
}
cursor += 1;
}
}
for &o in outputs.iter() {
if !seen.set(o) {
continue;
}
let mut cursor = buf.len();
buf.push(o);
while let Some(&n) = buf.get(cursor) {
for &i in &self.nodes[n].inputs[1..] {
if from == self.nodes[i].inputs.first()
&& self.nodes[i]
.outputs
.iter()
.all(|&o| self.nodes[o].inputs.first() != from || seen.get(o))
&& seen.set(i)
{
buf.push(i);
}
}
cursor += 1;
}
}
debug_assert!(
outputs.len() == buf.len() || outputs.len() == buf.len() + 1,
"{:?} {:?}",
outputs,
buf
);
if buf.len() + 1 == outputs.len() {
outputs.remove(outputs.len() - 1);
}
outputs.copy_from_slice(&buf);
}
}
pub struct Env<'a> {
ctx: &'a Function<'a>,
func: &'a Func,
res: &'a mut Res,
}
impl<'a> Env<'a> {
pub fn new(ctx: &'a Function<'a>, func: &'a Func, res: &'a mut Res) -> Self {
Self { ctx, func, res }
}
pub fn run(&mut self) {
self.res.bundles.clear();
self.res.node_to_reg.clear();
self.res.node_to_reg.resize(self.ctx.vreg_count(), 0);
debug_assert!(self.res.dfs_buf.is_empty());
debug_assert!(self.res.use_buf.is_empty());
debug_assert!(self.res.phi_input_buf.is_empty());
let mut bundle = Bundle::new(self.func.instrs.len());
let mut visited = BitSet::with_capacity(self.ctx.nodes.values.len());
let mut use_buf = mem::take(&mut self.res.use_buf);
let mut phi_input_buf = mem::take(&mut self.res.phi_input_buf);
for block in &self.func.blocks {
self.ctx.phi_inputs_of(block.entry, &mut phi_input_buf);
for param in phi_input_buf.drain(..) {
if !visited.set(param) {
continue;
}
self.append_bundle(param, &mut bundle, &mut use_buf);
}
}
self.res.phi_input_buf = phi_input_buf;
for &inst in &self.func.instrs {
if visited.get(inst) || inst == 0 {
continue;
}
self.append_bundle(inst, &mut bundle, &mut use_buf);
}
self.res.use_buf = use_buf;
}
fn append_bundle(&mut self, inst: Nid, bundle: &mut Bundle, use_buf: &mut Vec<Nid>) {
let mut dom = self.ctx.idom_of(inst);
if self.ctx.nodes[dom].kind == Kind::Loop && self.ctx.nodes[inst].kind == Kind::Phi {
dom = self.ctx.nodes.idom(dom);
dom = self.ctx.idom_of(dom);
}
self.ctx.uses_of(inst, use_buf);
for uinst in use_buf.drain(..) {
let cursor = self.ctx.use_block(inst, uinst);
self.reverse_cfg_dfs(cursor, dom, |_, n, b| {
let mut range = b.range.clone();
range.start =
range.start.max(self.ctx.instr_of(inst).map_or(0, |n| n + 1) as usize);
range.end = range.end.min(
self.ctx
.instr_of(uinst)
.filter(|_| self.ctx.nodes.loop_depth(dom) == self.ctx.nodes.loop_depth(n))
.map_or(Nid::MAX, |n| n + 1) as usize,
);
bundle.add(range);
});
}
match self.res.bundles.iter_mut().enumerate().find(|(_, b)| !b.overlaps(bundle)) {
Some((i, other)) => {
other.merge(bundle);
bundle.clear();
self.res.node_to_reg[inst as usize] = i as Reg + 1;
}
None => {
self.res.bundles.push(mem::replace(bundle, Bundle::new(self.func.instrs.len())));
self.res.node_to_reg[inst as usize] = self.res.bundles.len() as Reg;
}
}
}
fn reverse_cfg_dfs(
&mut self,
from: Nid,
until: Nid,
mut each: impl FnMut(&mut Self, Nid, &Block),
) {
debug_assert!(self.res.dfs_buf.is_empty());
self.res.dfs_buf.push(from);
self.res.dfs_seem.clear(self.ctx.nodes.values.len());
while let Some(nid) = self.res.dfs_buf.pop() {
each(self, nid, &self.func.blocks[self.ctx.block_of(nid) as usize]);
if nid == until {
continue;
}
match self.ctx.nodes[nid].kind {
Kind::Then | Kind::Else | Kind::Region | Kind::Loop => {
for &n in self.ctx.nodes[nid].inputs.iter() {
let d = self.ctx.idom_of(n);
if self.res.dfs_seem.set(d) {
self.res.dfs_buf.push(d);
}
}
}
Kind::Start => {}
_ => unreachable!(),
}
}
}
}
#[derive(Default)]
pub struct Res {
pub bundles: Vec<Bundle>,
pub node_to_reg: Vec<Reg>,
use_buf: Vec<Nid>,
phi_input_buf: Vec<Nid>,
dfs_buf: Vec<Nid>,
dfs_seem: BitSet,
}
pub struct Bundle {
taken: Vec<bool>,
}
impl Bundle {
fn new(size: usize) -> Self {
Self { taken: vec![false; size] }
}
fn add(&mut self, range: Range<usize>) {
self.taken[range].fill(true);
}
fn overlaps(&self, other: &Self) -> bool {
self.taken.iter().zip(other.taken.iter()).any(|(a, b)| a & b)
}
fn merge(&mut self, other: &Self) {
debug_assert!(!self.overlaps(other));
self.taken.iter_mut().zip(other.taken.iter()).for_each(|(a, b)| *a |= *b);
}
fn clear(&mut self) {
self.taken.fill(false);
}
}
#[derive(Default)]
pub struct Func {
pub blocks: Vec<Block>,
pub instrs: Vec<Nid>,
}
pub struct Block {
pub range: Range<usize>,
pub entry: Nid,
}

File diff suppressed because it is too large Load diff

View file

@ -5,7 +5,6 @@ use {
alloc::Layout,
fmt::Debug,
hint::unreachable_unchecked,
marker::PhantomData,
mem::MaybeUninit,
ops::{Deref, DerefMut, Not},
ptr::Unique,
@ -63,12 +62,6 @@ impl BitSet {
const INLINE_ELEMS: usize = Self::UNIT - 1;
const UNIT: usize = core::mem::size_of::<usize>() * 8;
pub fn with_capacity(len: usize) -> Self {
let mut s = Self::default();
s.reserve(len);
s
}
fn is_inline(&self) -> bool {
unsafe { self.inline & Self::FLAG != 0 }
}
@ -533,97 +526,3 @@ struct AllocedVc {
len: Nid,
base: Unique<Nid>,
}
pub trait Ent: Copy {
fn new(index: usize) -> Self;
fn index(self) -> usize;
}
pub struct EntVec<K: Ent, T> {
data: ::alloc::vec::Vec<T>,
k: PhantomData<fn(K)>,
}
impl<K: Ent, T> Default for EntVec<K, T> {
fn default() -> Self {
Self { data: Default::default(), k: PhantomData }
}
}
impl<K: Ent, T> EntVec<K, T> {
pub fn clear(&mut self) {
self.data.clear();
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
pub fn len(&self) -> usize {
self.data.len()
}
pub fn push(&mut self, value: T) -> K {
let k = K::new(self.data.len());
self.data.push(value);
k
}
pub fn next(&self, index: K) -> Option<&T> {
self.data.get(index.index() + 1)
}
pub fn shadow(&mut self, len: usize)
where
T: Default,
{
if self.data.len() < len {
self.data.resize_with(len, Default::default);
}
}
pub fn iter(&self) -> core::slice::Iter<T> {
self.data.iter()
}
}
impl<K: Ent, T> core::ops::Index<K> for EntVec<K, T> {
type Output = T;
fn index(&self, index: K) -> &Self::Output {
&self.data[index.index()]
}
}
impl<K: Ent, T> core::ops::IndexMut<K> for EntVec<K, T> {
fn index_mut(&mut self, index: K) -> &mut Self::Output {
&mut self.data[index.index()]
}
}
macro_rules! decl_ent {
($(
$vis:vis struct $name:ident($index:ty);
)*) => {$(
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
$vis struct $name($index);
impl crate::utils::Ent for $name {
fn new(index: usize) -> Self {
Self(index as $index)
}
fn index(self) -> usize {
self.0 as _
}
}
impl core::fmt::Display for $name {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, concat!(stringify!($name), "{}"), self.0)
}
}
)*};
}
pub(crate) use decl_ent;

View file

@ -1,44 +1,53 @@
main:
ADDI64 r254, r254, -24d
ST r31, r254, 0a, 24h
ADDI64 r254, r254, -32d
ST r31, r254, 0a, 32h
LI32 r32, 1148846080w
CP r2, r32
JAL r31, r0, :sin
FMUL32 r33, r1, r32
FTI32 r1, r33, 1b
LD r31, r254, 0a, 24h
ADDI64 r254, r254, 24d
FTI32 r34, r33, 1b
ANDI r1, r34, 4294967295d
LD r31, r254, 0a, 32h
ADDI64 r254, r254, 32d
JALA r0, r31, 0a
sin:
LI32 r4, 1124073472w
LI32 r5, 1078530011w
FMUL32 r7, r2, r4
FDIV32 r9, r7, r5
FTI32 r11, r9, 1b
ANDI r10, r11, 255d
ITF64 r5, r11
MULI64 r4, r10, 4d
LRA r3, r0, :SIN_TABLE
LI32 r7, 1086918619w
FC64T32 r9, r5, 1b
ADDI64 r5, r11, 64d
ADD64 r8, r3, r4
LI32 r1, 1132462080w
FMUL32 r6, r9, r7
ANDI r7, r5, 255d
LI32 r5, 1056964608w
LD r4, r8, 0a, 4h
FDIV32 r8, r6, r1
MULI64 r6, r7, 4d
FMUL32 r10, r4, r5
LRA r4, r0, :TABLE_SIZE
LD r6, r4, 0a, 4h
ITF32 r8, r6
LRA r12, r0, :PI
LI32 r7, 1056964608w
FMUL32 r1, r8, r2
LD r4, r12, 0a, 4h
FMUL32 r3, r1, r7
FDIV32 r9, r3, r4
FTI32 r9, r9, 1b
ADDI32 r10, r6, 4294967295w
LI32 r11, 4w
AND r12, r10, r9
DIRS32 r11, r0, r6, r11
ANDI r6, r12, 4294967295d
LI32 r5, 1073741824w
ITF32 r12, r9
ADD32 r3, r11, r9
MULI64 r11, r6, 4d
LRA r9, r0, :SIN_TABLE
FMUL32 r12, r12, r5
AND r10, r10, r3
ADD64 r3, r9, r11
FMUL32 r4, r4, r12
ANDI r10, r10, 4294967295d
LD r6, r3, 0a, 4h
FDIV32 r8, r4, r8
MULI64 r10, r10, 4d
FMUL32 r3, r6, r7
FSUB32 r11, r2, r8
ADD64 r9, r3, r6
FMUL32 r2, r11, r10
LD r12, r9, 0a, 4h
FSUB32 r5, r12, r2
FMUL32 r7, r5, r11
FADD32 r1, r4, r7
ADD64 r2, r9, r10
FMUL32 r7, r11, r3
LD r5, r2, 0a, 4h
FSUB32 r9, r5, r7
FMUL32 r11, r9, r11
FADD32 r1, r6, r11
JALA r0, r31, 0a
code size: 1303
ret: 826
code size: 1370
ret: 1000000
status: Ok(())

View file

@ -1,6 +0,0 @@
main:
LI64 r1, 69d
JALA r0, r31, 0a
code size: 29
ret: 69
status: Ok(())

View file

@ -76,20 +76,20 @@ push:
CP r3, r39
JAL r31, r0, :malloc
CP r40, r1
CP r41, r36
ST r38, r41, 16a, 8h
LI64 r1, 0d
CP r41, r40
JNE r41, r1, :3
CP r42, r40
JNE r42, r1, :3
JMP :4
3: CP r40, r41
CP r42, r36
ST r38, r42, 16a, 8h
LD r36, r42, 8a, 8h
3: CP r40, r42
LD r36, r41, 8a, 8h
MULI64 r43, r36, 8d
LD r44, r42, 0a, 8h
LD r44, r41, 0a, 8h
ADD64 r45, r44, r43
CP r46, r40
9: LD r2, r42, 0a, 8h
LD r47, r42, 8a, 8h
9: LD r2, r41, 0a, 8h
LD r47, r41, 8a, 8h
JNE r45, r44, :5
JEQ r47, r37, :6
CP r4, r39
@ -98,27 +98,27 @@ push:
CP r1, r40
JMP :7
6: CP r1, r40
7: ST r1, r42, 0a, 8h
7: ST r1, r41, 0a, 8h
JMP :8
5: CP r1, r40
CP r4, r39
ADDI64 r41, r46, 8d
ADDI64 r48, r44, 8d
ADDI64 r48, r46, 8d
ADDI64 r42, r44, 8d
LD r49, r44, 0a, 8h
ST r49, r46, 0a, 8h
CP r44, r48
CP r46, r41
CP r44, r42
CP r46, r48
JMP :9
0: CP r42, r36
8: LD r50, r42, 8a, 8h
0: CP r41, r36
8: LD r50, r41, 8a, 8h
MULI64 r51, r50, 8d
LD r52, r42, 0a, 8h
LD r52, r41, 0a, 8h
ADD64 r1, r52, r51
CP r3, r32
ST r3, r1, 0a, 8h
LD r53, r42, 8a, 8h
LD r53, r41, 8a, 8h
ADD64 r54, r53, r33
ST r54, r42, 8a, 8h
ST r54, r41, 8a, 8h
4: LD r31, r254, 0a, 192h
ADDI64 r254, r254, 192d
JALA r0, r31, 0a

View file

@ -1,34 +0,0 @@
main:
ADDI64 r254, r254, -38d
ST r31, r254, 6a, 32h
LI8 r32, 0b
ADDI64 r33, r254, 0d
2: JAL r31, r0, :return_fn
ST r1, r254, 0a, 6h
LD r34, r254, 0a, 1h
ANDI r34, r34, 255d
ANDI r32, r32, 255d
JEQ r34, r32, :0
LI64 r1, 1d
JMP :1
0: JMP :2
1: LD r31, r254, 6a, 32h
ADDI64 r254, r254, 38d
JALA r0, r31, 0a
return_fn:
ADDI64 r254, r254, -6d
LI8 r4, 1b
ADDI64 r3, r254, 0d
ST r4, r254, 0a, 1h
LI8 r4, 0b
ST r4, r254, 1a, 1h
ST r4, r254, 2a, 1h
ST r4, r254, 3a, 1h
ST r4, r254, 4a, 1h
ST r4, r254, 5a, 1h
LD r1, r3, 0a, 6h
ADDI64 r254, r254, 6d
JALA r0, r31, 0a
code size: 302
ret: 1
status: Ok(())

View file

@ -1,5 +1,7 @@
get_ptr:
LI64 r1, 0d
ADDI64 r254, r254, -8d
ADDI64 r1, r254, 0d
ADDI64 r254, r254, 8d
JALA r0, r31, 0a
main:
ADDI64 r254, r254, -40d
@ -19,6 +21,6 @@ main:
1: LD r31, r254, 0a, 40h
ADDI64 r254, r254, 40d
JALA r0, r31, 0a
code size: 185
ret: 0
code size: 208
ret: 10
status: Ok(())

View file

@ -1,65 +0,0 @@
main:
ADDI64 r254, r254, -122d
ST r31, r254, 26a, 96h
JAL r31, r0, :returner_fn
CP r32, r1
ADDI64 r1, r254, 2d
JAL r31, r0, :returner_bn
ADDI64 r33, r254, 0d
JAL r31, r0, :returner_cn
ST r1, r254, 0a, 2h
LI8 r34, 0b
LI8 r35, 0b
LD r36, r254, 2a, 1h
CP r1, r32
ANDI r37, r37, 255d
ANDI r1, r1, 255d
CMPU r37, r1, r34
CMPUI r37, r37, 0d
ANDI r38, r38, 255d
ANDI r36, r36, 255d
CMPU r38, r36, r35
CMPUI r38, r38, 0d
LD r39, r254, 0a, 1h
AND r40, r38, r37
ANDI r41, r41, 255d
ANDI r39, r39, 255d
CMPU r41, r39, r35
CMPUI r41, r41, 0d
AND r42, r41, r40
ANDI r42, r42, 255d
JNE r42, r0, :0
LI64 r1, 0d
JMP :1
0: LI64 r1, 1d
1: LD r31, r254, 26a, 96h
ADDI64 r254, r254, 122d
JALA r0, r31, 0a
returner_bn:
ADDI64 r254, r254, -24d
LI8 r6, 1b
ADDI64 r5, r254, 0d
ST r6, r254, 0a, 1h
LI64 r6, 0d
ST r6, r254, 8a, 8h
ST r6, r254, 16a, 8h
BMC r5, r1, 24h
ADDI64 r254, r254, 24d
JALA r0, r31, 0a
returner_cn:
ADDI64 r254, r254, -2d
LI8 r4, 1b
ADDI64 r3, r254, 0d
ST r4, r254, 0a, 1h
LI8 r4, 0b
ST r4, r254, 1a, 1h
LD r1, r3, 0a, 2h
ADDI64 r254, r254, 2d
JALA r0, r31, 0a
returner_fn:
LD r1, r254, 0a, 0h
ORI r1, r1, 128d
JALA r0, r31, 0a
code size: 546
ret: 1
status: Ok(())

View file

@ -2,8 +2,8 @@ decide:
LI8 r1, 1b
JALA r0, r31, 0a
main:
ADDI64 r254, r254, -232d
ST r31, r254, 80a, 152h
ADDI64 r254, r254, -224d
ST r31, r254, 80a, 144h
JAL r31, r0, :decide
LI64 r32, 0d
ADDI64 r2, r254, 72d
@ -13,54 +13,52 @@ main:
CP r34, r32
JMP :1
0: CP r34, r33
1: LI64 r35, 1d
ST r35, r254, 72a, 8h
JNE r34, r32, :2
1: JNE r34, r32, :2
LI64 r1, 9001d
JMP :3
2: JAL r31, r0, :decide
LI8 r36, 0b
LI8 r35, 0b
ANDI r1, r1, 255d
JNE r1, r0, :4
LI8 r37, 1b
ST r37, r254, 56a, 1h
LD r37, r34, 0a, 8h
ST r37, r254, 64a, 8h
LI8 r36, 1b
ST r36, r254, 56a, 1h
LD r36, r34, 0a, 8h
ST r36, r254, 64a, 8h
JMP :5
4: ST r36, r254, 56a, 1h
5: LD r38, r254, 56a, 1h
ANDI r38, r38, 255d
ANDI r36, r36, 255d
JEQ r38, r36, :6
4: ST r35, r254, 56a, 1h
5: LD r37, r254, 56a, 1h
ANDI r37, r37, 255d
ANDI r35, r35, 255d
JEQ r37, r35, :6
LI64 r1, 42d
JMP :3
6: JAL r31, r0, :decide
LI32 r39, 0w
LI32 r38, 0w
ANDI r1, r1, 255d
JNE r1, r0, :7
CP r40, r39
CP r39, r38
JMP :8
7: LI32 r40, 2147483649w
8: ANDI r40, r40, 4294967295d
ANDI r39, r39, 4294967295d
JNE r40, r39, :9
7: LI32 r39, 8388609w
8: ANDI r39, r39, 4294967295d
ANDI r38, r38, 4294967295d
JNE r39, r38, :9
LI64 r1, 69d
JMP :3
9: ADDI64 r3, r254, 40d
CP r41, r3
CP r40, r3
JAL r31, r0, :new_foo
ST r1, r254, 40a, 16h
LI64 r42, 0d
LD r43, r254, 40a, 8h
JNE r43, r42, :10
LI64 r32, 0d
LD r41, r254, 40a, 8h
JNE r41, r32, :10
LI64 r1, 999d
JMP :3
10: LRA r4, r0, :"foo\0"
CP r3, r41
CP r3, r40
CP r2, r3
LD r2, r2, 0a, 16h
JAL r31, r0, :use_foo
ADDI64 r44, r254, 0d
ADDI64 r42, r254, 0d
JAL r31, r0, :no_foo
ST r1, r254, 0a, 16h
JAL r31, r0, :decide
@ -70,9 +68,11 @@ main:
JMP :12
11: CP r2, r33
ST r2, r254, 0a, 8h
ST r35, r254, 8a, 8h
12: LD r45, r254, 0a, 8h
JNE r45, r42, :13
LI64 r43, 1d
ST r43, r254, 8a, 8h
ST r43, r254, 72a, 8h
12: LD r44, r254, 0a, 8h
JNE r44, r32, :13
LI64 r1, 34d
JMP :3
13: ADDI64 r1, r254, 16d
@ -81,29 +81,24 @@ main:
ANDI r1, r1, 255d
JNE r1, r0, :14
JMP :15
14: ST r36, r254, 16a, 1h
15: LD r46, r254, 16a, 1h
ANDI r46, r46, 255d
ANDI r36, r36, 255d
JEQ r46, r36, :16
14: ST r35, r254, 16a, 1h
15: LD r45, r254, 16a, 1h
ANDI r45, r45, 255d
ANDI r35, r35, 255d
JEQ r45, r35, :16
LI64 r1, 420d
JMP :3
16: LD r47, r254, 0a, 8h
LD r48, r47, 0a, 8h
ANDI r49, r40, 65535d
SUB64 r1, r49, r48
3: LD r31, r254, 80a, 152h
ADDI64 r254, r254, 232d
16: LD r46, r254, 0a, 8h
LD r47, r46, 0a, 8h
ANDI r48, r39, 65535d
SUB64 r1, r48, r47
3: LD r31, r254, 80a, 144h
ADDI64 r254, r254, 224d
JALA r0, r31, 0a
new_bar:
ADDI64 r254, r254, -24d
LI8 r8, 1b
ADDI64 r7, r254, 0d
ST r8, r254, 0a, 1h
ST r2, r254, 8a, 8h
LI64 r9, 1d
ST r9, r254, 16a, 8h
BMC r7, r1, 24h
ADDI64 r5, r254, 0d
BMC r1, r1, 24h
ADDI64 r254, r254, 24d
JALA r0, r31, 0a
new_foo:
@ -130,6 +125,6 @@ use_foo:
ADDI64 r2, r254, 0d
ADDI64 r254, r254, 16d
JALA r0, r31, 0a
code size: 1143
code size: 1091
ret: 0
status: Ok(())

View file

@ -1,22 +1,3 @@
main:
ADDI64 r254, r254, -16d
ADDI64 r3, r254, 0d
LI64 r6, 0d
CP r3, r6
CP r4, r6
CP r5, r6
ECA
ST r1, r254, 0a, 16h
LI8 r8, 0b
LD r9, r254, 0a, 1h
ANDI r9, r9, 255d
ANDI r8, r8, 255d
JNE r9, r8, :0
UN
0: LD r1, r254, 8a, 8h
ADDI64 r254, r254, 16d
JALA r0, r31, 0a
unknown ecall: 0
code size: 142
ret: 0
status: Err(Unreachable)
test.hb:8:2: can't prove the value is not 'null', use '@unwrap(<opt>)' if you believe compiler is stupid, or explicitly check for null and handle it ('if <opt> == null { /* handle */ } else { /* use opt */ }')
return a
^

View file

@ -1,31 +0,0 @@
get_format:
ADDI64 r254, r254, -16d
LI8 r5, 1b
ADDI64 r4, r254, 0d
LRA r3, r0, :BMP
ST r5, r254, 0a, 1h
LD r6, r3, 0a, 8h
ST r6, r254, 8a, 8h
LD r1, r4, 0a, 16h
ADDI64 r254, r254, 16d
JALA r0, r31, 0a
main:
ADDI64 r254, r254, -48d
ST r31, r254, 16a, 32h
ADDI64 r32, r254, 0d
JAL r31, r0, :get_format
ST r1, r254, 0a, 16h
LI8 r33, 0b
LD r34, r254, 0a, 1h
ANDI r34, r34, 255d
ANDI r33, r33, 255d
JNE r34, r33, :0
LI64 r1, 1d
JMP :1
0: LD r1, r254, 8a, 8h
1: LD r31, r254, 16a, 32h
ADDI64 r254, r254, 48d
JALA r0, r31, 0a
code size: 283
ret: 0
status: Ok(())

View file

@ -1,6 +0,0 @@
test.hb:5:23: returning value with local provenance (pointer will be invalid after function returns)
dangle := fn(): ^uint return &0
^
test.hb:5:30: ...the pointer points to stack allocation created here
dangle := fn(): ^uint return &0
^