forked from AbleOS/holey-bytes
Compare commits
18 commits
b15e66b2af
...
65e9f272a8
Author | SHA1 | Date | |
---|---|---|---|
65e9f272a8 | |||
d2052cd2a3 | |||
29367d8f8b | |||
a299bad75b | |||
7d48d3beb1 | |||
68c0248189 | |||
0ef74d89cb | |||
1b2b9f899d | |||
455f70db6e | |||
0374848b28 | |||
513d2c7127 | |||
9d2f419140 | |||
f535ea7b0a | |||
be6d0d3f18 | |||
2718ef8523 | |||
3ee78f3a31 | |||
2bac7c1fb3 | |||
mlokis | 79a3f1ab2b |
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -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
52
Cargo.lock
generated
|
@ -276,15 +276,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.7.2"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3"
|
||||
checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.1.30"
|
||||
version = "1.1.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945"
|
||||
checksum = "baee610e9452a8f6f0a1b6194ec09ff9e2d85dea54432acdae41aa0761c95d70"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
|
@ -721,9 +721,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
|||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "1.4.1"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05"
|
||||
checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
|
@ -741,9 +741,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "hyper-util"
|
||||
version = "0.1.9"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b"
|
||||
checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-util",
|
||||
|
@ -979,18 +979,18 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
|||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "1.1.6"
|
||||
version = "1.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf123a161dde1e524adf36f90bc5d8d3462824a9c43553ad07a8183161189ec"
|
||||
checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95"
|
||||
dependencies = [
|
||||
"pin-project-internal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-internal"
|
||||
version = "1.1.6"
|
||||
version = "1.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4502d8515ca9f32f1fb543d987f63d95a14934883db45bdb48060b6b69257f8"
|
||||
checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -999,9 +999,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.14"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
|
||||
checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
|
@ -1073,9 +1073,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.11.0"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8"
|
||||
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
|
@ -1162,9 +1162,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.14"
|
||||
version = "0.23.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "415d9944693cb90382053259f89fbb077ea730ad7273047ec63b19bc9b160ba8"
|
||||
checksum = "eee87ff5d9b36712a58574e12e9f0ea80f915a5b0ac518d322b24a465617925e"
|
||||
dependencies = [
|
||||
"aws-lc-rs",
|
||||
"once_cell",
|
||||
|
@ -1185,9 +1185,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustls-pki-types"
|
||||
version = "1.9.0"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e696e35370c65c9c541198af4543ccd580cf17fc25d8e05c5a242b202488c55"
|
||||
checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b"
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
|
@ -1203,9 +1203,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.17"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
|
||||
checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
|
@ -1241,9 +1241,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.128"
|
||||
version = "1.0.132"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8"
|
||||
checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
|
@ -1366,9 +1366,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
|
|||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.40.0"
|
||||
version = "1.41.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998"
|
||||
checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
|
|
|
@ -5,8 +5,9 @@
|
|||
use {
|
||||
alloc::{string::String, vec::Vec},
|
||||
hblang::{
|
||||
parser::FileId,
|
||||
son::{hbvm::HbvmBackend, Codegen, CodegenCtx},
|
||||
ty::Module,
|
||||
Ent,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -60,7 +61,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() as FileId),
|
||||
hblang::parser::FileKind::Module => Ok(paths.binary_search(&path).unwrap()),
|
||||
hblang::parser::FileKind::Embed => Err("embeds are not supported".into()),
|
||||
};
|
||||
files
|
||||
|
@ -79,7 +80,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(root as FileId);
|
||||
Codegen::new(&mut backend, &files, &mut ctx).generate(Module::new(root));
|
||||
|
||||
if !ctx.parser.errors.borrow().is_empty() {
|
||||
log::error!("{}", ctx.parser.errors.borrow());
|
||||
|
|
117
lang/README.md
117
lang/README.md
File diff suppressed because one or more lines are too long
|
@ -482,15 +482,6 @@ 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();
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use {
|
||||
crate::{
|
||||
parser::{self, Ast, Ctx, FileKind},
|
||||
parser::{Ast, Ctx, FileKind},
|
||||
son::{self, hbvm::HbvmBackend},
|
||||
ty,
|
||||
},
|
||||
alloc::{string::String, vec::Vec},
|
||||
core::{fmt::Write, num::NonZeroUsize, ops::Deref},
|
||||
|
@ -42,10 +43,10 @@ pub struct Options {
|
|||
}
|
||||
|
||||
impl Options {
|
||||
pub fn from_args(args: &[&str]) -> std::io::Result<Self> {
|
||||
pub fn from_args(args: &[&str], out: &mut Vec<u8>) -> std::io::Result<Self> {
|
||||
if args.contains(&"--help") || args.contains(&"-h") {
|
||||
log::error!("Usage: hbc [OPTIONS...] <FILE>");
|
||||
log::error!(include_str!("../command-help.txt"));
|
||||
writeln!(out, "Usage: hbc [OPTIONS...] <FILE>")?;
|
||||
writeln!(out, include_str!("../command-help.txt"))?;
|
||||
return Err(std::io::ErrorKind::Other.into());
|
||||
}
|
||||
|
||||
|
@ -58,7 +59,9 @@ impl Options {
|
|||
.position(|&a| a == "--threads")
|
||||
.map(|i| {
|
||||
args[i + 1].parse::<NonZeroUsize>().map_err(|e| {
|
||||
std::io::Error::other(format!("--threads expects non zero integer: {e}"))
|
||||
writeln!(out, "--threads expects non zero integer: {e}")
|
||||
.err()
|
||||
.unwrap_or(std::io::ErrorKind::Other.into())
|
||||
})
|
||||
})
|
||||
.transpose()?
|
||||
|
@ -71,22 +74,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)?;
|
||||
|
||||
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 || options.fmt_stdout) && !parsed.errors.is_empty() {
|
||||
*out = parsed.errors.into_bytes();
|
||||
return Err(std::io::Error::other("fmt fialed (errors are in out)"));
|
||||
}
|
||||
|
||||
if options.fmt {
|
||||
for parsed in parsed.ast {
|
||||
format_ast(parsed)?;
|
||||
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();
|
||||
}
|
||||
} else if options.fmt_stdout {
|
||||
let ast = parsed.ast.into_iter().next().unwrap();
|
||||
write!(out, "{ast}").unwrap();
|
||||
write!(out, "{}", &parsed.ast[0])?;
|
||||
} else {
|
||||
let mut backend = HbvmBackend::default();
|
||||
let mut ctx = crate::son::CodegenCtx::default();
|
||||
|
@ -94,11 +97,12 @@ 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(0);
|
||||
codegen.generate(ty::Module::MAIN);
|
||||
|
||||
if !codegen.errors.borrow().is_empty() {
|
||||
log::error!("{}", codegen.errors.borrow());
|
||||
return Err(std::io::Error::other("compilation faoled"));
|
||||
drop(codegen);
|
||||
*out = ctx.parser.errors.into_inner().into_bytes();
|
||||
return Err(std::io::Error::other("compilation faoled (errors are in out)"));
|
||||
}
|
||||
|
||||
codegen.assemble(out);
|
||||
|
@ -239,10 +243,10 @@ pub fn parse_from_fs(extra_threads: usize, root: &str) -> io::Result<Loaded> {
|
|||
}
|
||||
}
|
||||
|
||||
type Task = (u32, PathBuf);
|
||||
type Task = (usize, PathBuf);
|
||||
|
||||
let seen_modules = Mutex::new(crate::HashMap::<PathBuf, u32>::default());
|
||||
let seen_embeds = Mutex::new(crate::HashMap::<PathBuf, u32>::default());
|
||||
let seen_modules = Mutex::new(crate::HashMap::<PathBuf, usize>::default());
|
||||
let seen_embeds = Mutex::new(crate::HashMap::<PathBuf, usize>::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());
|
||||
|
@ -261,7 +265,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 as u32
|
||||
len
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -286,7 +290,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 as u32
|
||||
len
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -328,9 +332,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 as usize + 1);
|
||||
let len = ast.len().max(indx + 1);
|
||||
ast.resize_with(len, || Err(io::ErrorKind::InvalidData.into()));
|
||||
ast[indx as usize] = res;
|
||||
ast[indx] = res;
|
||||
}
|
||||
ctx.errors.into_inner()
|
||||
};
|
||||
|
|
|
@ -3,6 +3,7 @@ use {
|
|||
lexer::TokenKind,
|
||||
parser,
|
||||
son::{hbvm::HbvmBackend, Codegen, CodegenCtx},
|
||||
ty::Module,
|
||||
},
|
||||
alloc::string::String,
|
||||
core::{fmt::Write, hash::BuildHasher, ops::Range},
|
||||
|
@ -135,6 +136,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(0);
|
||||
cdg.generate(Module::MAIN);
|
||||
}
|
||||
}
|
||||
|
|
349
lang/src/lib.rs
349
lang/src/lib.rs
|
@ -32,11 +32,13 @@
|
|||
|
||||
#[cfg(feature = "std")]
|
||||
pub use fs::*;
|
||||
pub use utils::Ent;
|
||||
use {
|
||||
self::{
|
||||
lexer::TokenKind,
|
||||
parser::{idfl, CommentOr, Expr, ExprRef, FileId, Pos},
|
||||
ty::ArrayLen,
|
||||
parser::{idfl, CommentOr, Expr, ExprRef, Pos},
|
||||
ty::{ArrayLen, Builtin, Module},
|
||||
utils::EntVec,
|
||||
},
|
||||
alloc::{string::String, vec::Vec},
|
||||
core::{cell::Cell, fmt::Display, ops::Range},
|
||||
|
@ -251,17 +253,17 @@ impl Ident {
|
|||
pos..pos + len
|
||||
}
|
||||
|
||||
fn builtin(builtin: u32) -> Ident {
|
||||
debug_assert!(Self(builtin).is_null());
|
||||
Self(builtin)
|
||||
fn builtin(builtin: Builtin) -> Ident {
|
||||
Self(builtin.index() as _)
|
||||
}
|
||||
}
|
||||
|
||||
mod ty {
|
||||
pub mod ty {
|
||||
use {
|
||||
crate::{
|
||||
lexer::TokenKind,
|
||||
parser::{self, FileId, Pos},
|
||||
parser::{self, Pos},
|
||||
utils::Ent,
|
||||
Ident, Size, Types,
|
||||
},
|
||||
core::{num::NonZeroU32, ops::Range},
|
||||
|
@ -269,16 +271,10 @@ mod ty {
|
|||
|
||||
pub type ArrayLen = u32;
|
||||
|
||||
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;
|
||||
impl Func {
|
||||
pub const ECA: Func = Func(u32::MAX);
|
||||
pub const MAIN: Func = Func(u32::MIN);
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, PartialOrd, Ord)]
|
||||
pub struct Tuple(pub u32);
|
||||
|
@ -305,6 +301,10 @@ 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,28 +350,30 @@ mod ty {
|
|||
fn key<'a>(&self, ctx: &'a Self::Ctx) -> Self::Key<'a> {
|
||||
match self.expand() {
|
||||
Kind::Struct(s) => {
|
||||
let st = &ctx.structs[s as usize];
|
||||
let st = &ctx.structs[s];
|
||||
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 as usize]),
|
||||
Kind::Opt(p) => crate::SymKey::Optional(&ctx.opts[p as usize]),
|
||||
Kind::Ptr(p) => crate::SymKey::Pointer(&ctx.ptrs[p]),
|
||||
Kind::Opt(p) => crate::SymKey::Optional(&ctx.opts[p]),
|
||||
Kind::Func(f) => {
|
||||
let fc = &ctx.funcs[f as usize];
|
||||
let fc = &ctx.funcs[f];
|
||||
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 as usize];
|
||||
let gb = &ctx.globals[g];
|
||||
crate::SymKey::Decl(gb.file, gb.name)
|
||||
}
|
||||
Kind::Slice(s) => crate::SymKey::Array(&ctx.slices[s as usize]),
|
||||
Kind::Slice(s) => crate::SymKey::Array(&ctx.slices[s]),
|
||||
Kind::Module(_) | Kind::Builtin(_) => {
|
||||
crate::SymKey::Decl(FileId::MAX, Ident::INVALID)
|
||||
crate::SymKey::Decl(Module::default(), Ident::INVALID)
|
||||
}
|
||||
Kind::Const(c) => crate::SymKey::Constant(&ctx.consts[c]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -388,7 +390,7 @@ 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 => BOOL.into(),
|
||||
T::Lt | T::Gt | T::Le | T::Ge | T::Ne | T::Eq => Id::BOOL,
|
||||
_ => self,
|
||||
}
|
||||
}
|
||||
|
@ -415,7 +417,7 @@ mod ty {
|
|||
|
||||
pub fn strip_pointer(self) -> Self {
|
||||
match self.expand() {
|
||||
Kind::Ptr(_) => Kind::Builtin(UINT).compress(),
|
||||
Kind::Ptr(_) => Id::UINT,
|
||||
_ => self,
|
||||
}
|
||||
}
|
||||
|
@ -432,8 +434,8 @@ 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 == Self::from(NEVER) => ob,
|
||||
_ if ob == Self::from(NEVER) => oa,
|
||||
_ if oa == Id::NEVER => ob,
|
||||
_ if ob == Id::NEVER => oa,
|
||||
_ if oa == ob => oa,
|
||||
_ if ob.is_optional() => ob,
|
||||
_ if oa.is_pointer() && ob.is_pointer() => return None,
|
||||
|
@ -456,12 +458,12 @@ mod ty {
|
|||
pub(crate) fn simple_size(&self) -> Option<Size> {
|
||||
Some(match self.expand() {
|
||||
Kind::Ptr(_) => 8,
|
||||
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,
|
||||
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,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
@ -479,7 +481,7 @@ mod ty {
|
|||
pub(crate) fn loc(&self, tys: &Types) -> Loc {
|
||||
match self.expand() {
|
||||
Kind::Opt(o)
|
||||
if let ty = tys.ins.opts[o as usize].base
|
||||
if let ty = tys.ins.opts[o].base
|
||||
&& ty.loc(tys) == Loc::Reg
|
||||
&& (ty.is_pointer() || tys.size_of(ty) < 8) =>
|
||||
{
|
||||
|
@ -488,7 +490,9 @@ 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(_) => unreachable!(),
|
||||
Kind::Func(_) | Kind::Global(_) | Kind::Module(_) | Kind::Const(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -496,7 +500,7 @@ 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 as usize].len == ArrayLen::MAX,
|
||||
Kind::Slice(s) => tys.ins.slices[s].len == ArrayLen::MAX,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -514,12 +518,6 @@ 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;
|
||||
|
@ -533,7 +531,7 @@ mod ty {
|
|||
|
||||
macro_rules! builtin_type {
|
||||
($($name:ident;)*) => {
|
||||
$(pub const $name: Builtin = ${index(0)} + 1;)*
|
||||
$(const $name: u32 = ${index(0)} + 1;)*
|
||||
|
||||
mod __lc_names {
|
||||
use super::*;
|
||||
|
@ -547,20 +545,23 @@ mod ty {
|
|||
};)*
|
||||
}
|
||||
|
||||
#[expect(dead_code)]
|
||||
impl Id {
|
||||
$(pub const $name: Self = Kind::Builtin($name).compress();)*
|
||||
$(pub const $name: Self = Kind::Builtin(Builtin($name)).compress();)*
|
||||
}
|
||||
|
||||
impl Kind {
|
||||
$(pub const $name: Self = Kind::Builtin(Builtin($name));)*
|
||||
}
|
||||
|
||||
pub fn from_str(name: &str) -> Option<Builtin> {
|
||||
match name {
|
||||
$(__lc_names::$name => Some($name),)*
|
||||
$(__lc_names::$name => Some(Builtin($name)),)*
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_str(ty: Builtin) -> &'static str {
|
||||
match ty {
|
||||
match ty.0 {
|
||||
$($name => __lc_names::$name,)*
|
||||
v => unreachable!("invalid type: {}", v),
|
||||
}
|
||||
|
@ -590,6 +591,10 @@ 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),)*
|
||||
|
@ -603,24 +608,32 @@ 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(index),)*
|
||||
$(${index(0)} => Self::$variant($variant(index)),)*
|
||||
i => unreachable!("{i}"),
|
||||
}
|
||||
}
|
||||
|
||||
$vis const fn compress(self) -> Id {
|
||||
let (index, flag) = match self {
|
||||
$(Self::$variant(index) => (index, ${index(0)}),)*
|
||||
$(Self::$variant(index) => (index.0, ${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()
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -635,12 +648,35 @@ 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 {
|
||||
Self::Builtin(UNDECLARED)
|
||||
Id::UNDECLARED.expand()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -666,7 +702,7 @@ mod ty {
|
|||
match TK::from_ty(self.ty) {
|
||||
TK::Module(idx) => {
|
||||
f.write_str("@use(\"")?;
|
||||
self.files[idx as usize].path.fmt(f)?;
|
||||
self.files[idx.index()].path.fmt(f)?;
|
||||
f.write_str(")[")?;
|
||||
idx.fmt(f)?;
|
||||
f.write_str("]")
|
||||
|
@ -674,14 +710,14 @@ mod ty {
|
|||
TK::Builtin(ty) => f.write_str(to_str(ty)),
|
||||
TK::Opt(ty) => {
|
||||
f.write_str("?")?;
|
||||
self.rety(self.tys.ins.opts[ty as usize].base).fmt(f)
|
||||
self.rety(self.tys.ins.opts[ty].base).fmt(f)
|
||||
}
|
||||
TK::Ptr(ty) => {
|
||||
f.write_str("^")?;
|
||||
self.rety(self.tys.ins.ptrs[ty as usize].base).fmt(f)
|
||||
self.rety(self.tys.ins.ptrs[ty].base).fmt(f)
|
||||
}
|
||||
TK::Struct(idx) => {
|
||||
let record = &self.tys.ins.structs[idx as usize];
|
||||
let record = &self.tys.ins.structs[idx];
|
||||
if record.name.is_null() {
|
||||
f.write_str("[")?;
|
||||
idx.fmt(f)?;
|
||||
|
@ -698,7 +734,7 @@ mod ty {
|
|||
}
|
||||
f.write_str("}")
|
||||
} else {
|
||||
let file = &self.files[record.file as usize];
|
||||
let file = &self.files[record.file.index()];
|
||||
f.write_str(file.ident_str(record.name))
|
||||
}
|
||||
}
|
||||
|
@ -707,11 +743,12 @@ mod ty {
|
|||
idx.fmt(f)
|
||||
}
|
||||
TK::Global(idx) => {
|
||||
f.write_str("global")?;
|
||||
idx.fmt(f)
|
||||
let global = &self.tys.ins.globals[idx];
|
||||
let file = &self.files[global.file.index()];
|
||||
f.write_str(file.ident_str(global.name))
|
||||
}
|
||||
TK::Slice(idx) => {
|
||||
let array = self.tys.ins.slices[idx as usize];
|
||||
let array = self.tys.ins.slices[idx];
|
||||
f.write_str("[")?;
|
||||
self.rety(array.elem).fmt(f)?;
|
||||
if array.len != ArrayLen::MAX {
|
||||
|
@ -720,6 +757,11 @@ 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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -732,10 +774,11 @@ type Size = u32;
|
|||
pub enum SymKey<'a> {
|
||||
Pointer(&'a Ptr),
|
||||
Optional(&'a Opt),
|
||||
Struct(FileId, Pos, ty::Tuple),
|
||||
Struct(Module, Pos, ty::Tuple),
|
||||
FuncInst(ty::Func, ty::Tuple),
|
||||
Decl(FileId, Ident),
|
||||
Decl(Module, Ident),
|
||||
Array(&'a Array),
|
||||
Constant(&'a Const),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
|
@ -744,8 +787,9 @@ pub struct Sig {
|
|||
ret: ty::Id,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Func {
|
||||
file: FileId,
|
||||
file: Module,
|
||||
name: Ident,
|
||||
base: Option<ty::Func>,
|
||||
expr: ExprRef,
|
||||
|
@ -753,19 +797,6 @@ 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]
|
||||
|
@ -780,23 +811,19 @@ struct TypedReloc {
|
|||
reloc: Reloc,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Default)]
|
||||
struct Global {
|
||||
file: FileId,
|
||||
file: Module,
|
||||
name: Ident,
|
||||
ty: ty::Id,
|
||||
data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Default for Global {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
ty: Default::default(),
|
||||
data: Default::default(),
|
||||
file: u32::MAX,
|
||||
name: Default::default(),
|
||||
}
|
||||
}
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
pub struct Const {
|
||||
ast: ExprRef,
|
||||
name: Ident,
|
||||
file: Module,
|
||||
}
|
||||
|
||||
// TODO: make into bit struct (width: u2, sub_offset: u3, offset: u27)
|
||||
|
@ -835,7 +862,7 @@ struct Field {
|
|||
struct Struct {
|
||||
name: Ident,
|
||||
pos: Pos,
|
||||
file: FileId,
|
||||
file: Module,
|
||||
size: Cell<Size>,
|
||||
align: Cell<u8>,
|
||||
captures: ty::Tuple,
|
||||
|
@ -934,18 +961,19 @@ struct TypesTmp {
|
|||
|
||||
#[derive(Default)]
|
||||
pub struct TypeIns {
|
||||
funcs: Vec<Func>,
|
||||
args: Vec<ty::Id>,
|
||||
globals: Vec<Global>,
|
||||
structs: Vec<Struct>,
|
||||
fields: Vec<Field>,
|
||||
ptrs: Vec<Ptr>,
|
||||
opts: Vec<Opt>,
|
||||
slices: Vec<Array>,
|
||||
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>,
|
||||
}
|
||||
|
||||
struct FTask {
|
||||
file: FileId,
|
||||
file: Module,
|
||||
id: ty::Func,
|
||||
ct: bool,
|
||||
}
|
||||
|
@ -953,11 +981,11 @@ struct FTask {
|
|||
struct StringRef(ty::Global);
|
||||
|
||||
impl ctx_map::CtxEntry for StringRef {
|
||||
type Ctx = [Global];
|
||||
type Ctx = EntVec<ty::Global, Global>;
|
||||
type Key<'a> = &'a [u8];
|
||||
|
||||
fn key<'a>(&self, ctx: &'a Self::Ctx) -> Self::Key<'a> {
|
||||
&ctx[self.0 as usize].data
|
||||
&ctx[self.0].data
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -975,16 +1003,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: FileId, expr: &Expr, ty: ty::Id) -> u64;
|
||||
fn eval_global(&mut self, file: FileId, name: Ident, expr: &Expr) -> 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 infer_type(&mut self, expr: &Expr) -> ty::Id;
|
||||
fn report(&self, file: FileId, pos: Pos, msg: impl Display) -> ty::Id;
|
||||
fn report(&self, file: Module, pos: Pos, msg: impl Display) -> ty::Id;
|
||||
|
||||
fn find_type(
|
||||
&mut self,
|
||||
pos: Pos,
|
||||
from_file: FileId,
|
||||
file: FileId,
|
||||
from_file: Module,
|
||||
file: Module,
|
||||
id: Result<Ident, &str>,
|
||||
files: &[parser::Ast],
|
||||
) -> ty::Id {
|
||||
|
@ -999,9 +1027,9 @@ trait TypeParser {
|
|||
self.on_reuse(ty);
|
||||
ty
|
||||
} else {
|
||||
let f = &files[file as usize];
|
||||
let f = &files[file.index()];
|
||||
|
||||
let Some((Expr::BinOp { left, right, .. }, name)) = f.find_decl(id) else {
|
||||
let Some((expr @ Expr::BinOp { left, right, .. }, name)) = f.find_decl(id) else {
|
||||
return match id {
|
||||
Ok(_) => ty::Id::NEVER,
|
||||
Err("main") => self.report(
|
||||
|
@ -1025,20 +1053,19 @@ trait TypeParser {
|
|||
ty
|
||||
} else {
|
||||
let ty = left
|
||||
.find_pattern_path(name, right, |right| {
|
||||
.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)
|
||||
}
|
||||
})
|
||||
.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
|
||||
}
|
||||
|
@ -1046,7 +1073,7 @@ trait TypeParser {
|
|||
|
||||
let tys = self.tys();
|
||||
if let ty::Kind::Global(g) = ty.expand() {
|
||||
let g = &tys.ins.globals[g as usize];
|
||||
let g = &tys.ins.globals[g];
|
||||
if g.ty == ty::Id::TYPE {
|
||||
return ty::Id::from(
|
||||
u32::from_ne_bytes(g.data.as_slice().try_into().unwrap()) as u64
|
||||
|
@ -1059,7 +1086,7 @@ trait TypeParser {
|
|||
/// returns none if comptime eval is required
|
||||
fn parse_ty(
|
||||
&mut self,
|
||||
file: FileId,
|
||||
file: Module,
|
||||
expr: &Expr,
|
||||
name: Option<Ident>,
|
||||
files: &[parser::Ast],
|
||||
|
@ -1074,7 +1101,7 @@ trait TypeParser {
|
|||
let base = self.parse_ty(file, val, None, files);
|
||||
self.tys().make_opt(base)
|
||||
}
|
||||
Expr::Ident { id, .. } if id.is_null() => id.len().into(),
|
||||
Expr::Ident { id, .. } if let Ok(bt) = ty::Builtin::try_from(id) => bt.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) =
|
||||
|
@ -1119,18 +1146,21 @@ trait TypeParser {
|
|||
}
|
||||
|
||||
let tys = self.tys();
|
||||
tys.ins.structs.push(Struct {
|
||||
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.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
|
||||
}
|
||||
|
@ -1141,7 +1171,7 @@ trait TypeParser {
|
|||
sig: 'b: {
|
||||
let arg_base = self.tys().tmp.args.len();
|
||||
for arg in args {
|
||||
let sym = parser::find_symbol(&files[file as usize].symbols, arg.id);
|
||||
let sym = parser::find_symbol(&files[file.index()].symbols, arg.id);
|
||||
if sym.flags & idfl::COMPTIME != 0 {
|
||||
self.tys().tmp.args.truncate(arg_base);
|
||||
break 'b None;
|
||||
|
@ -1161,10 +1191,7 @@ trait TypeParser {
|
|||
..Default::default()
|
||||
};
|
||||
|
||||
let id = self.tys().ins.funcs.len() as _;
|
||||
self.tys().ins.funcs.push(func);
|
||||
|
||||
ty::Kind::Func(id).compress()
|
||||
self.tys().ins.funcs.push(func).into()
|
||||
}
|
||||
_ if let Some(name) = name => self.eval_global(file, name, expr),
|
||||
_ => ty::Id::from(self.eval_const(file, expr, ty::Id::TYPE)),
|
||||
|
@ -1174,12 +1201,9 @@ trait TypeParser {
|
|||
|
||||
impl Types {
|
||||
fn struct_field_range(&self, strct: ty::Struct) -> Range<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);
|
||||
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);
|
||||
start..end
|
||||
}
|
||||
|
||||
|
@ -1210,49 +1234,30 @@ 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),
|
||||
ty::Kind::Opt,
|
||||
)
|
||||
self.make_generic_ty(Opt { base }, |ins| &mut ins.opts, |e| SymKey::Optional(e))
|
||||
}
|
||||
|
||||
fn make_ptr(&mut self, base: ty::Id) -> ty::Id {
|
||||
self.make_generic_ty(
|
||||
Ptr { base },
|
||||
|ins| &mut ins.ptrs,
|
||||
|e| SymKey::Pointer(e),
|
||||
ty::Kind::Ptr,
|
||||
)
|
||||
self.make_generic_ty(Ptr { base }, |ins| &mut ins.ptrs, |e| SymKey::Pointer(e))
|
||||
}
|
||||
|
||||
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),
|
||||
ty::Kind::Slice,
|
||||
)
|
||||
self.make_generic_ty(Array { elem, len }, |ins| &mut ins.slices, |e| SymKey::Array(e))
|
||||
}
|
||||
|
||||
fn make_generic_ty<T: Copy>(
|
||||
fn make_generic_ty<K: Ent + Into<ty::Id>, T: Copy>(
|
||||
&mut self,
|
||||
ty: T,
|
||||
get_col: fn(&mut TypeIns) -> &mut Vec<T>,
|
||||
get_col: fn(&mut TypeIns) -> &mut EntVec<K, 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);
|
||||
kind(get_col(ins).len() as u32 - 1).compress()
|
||||
})
|
||||
*self.syms.get_or_insert(key(&{ ty }), &mut self.ins, |ins| get_col(ins).push(ty).into())
|
||||
}
|
||||
|
||||
fn size_of(&self, ty: ty::Id) -> Size {
|
||||
match ty.expand() {
|
||||
ty::Kind::Slice(arr) => {
|
||||
let arr = &self.ins.slices[arr as usize];
|
||||
let arr = &self.ins.slices[arr];
|
||||
match arr.len {
|
||||
0 => 0,
|
||||
ArrayLen::MAX => 16,
|
||||
|
@ -1260,17 +1265,17 @@ impl Types {
|
|||
}
|
||||
}
|
||||
ty::Kind::Struct(stru) => {
|
||||
if self.ins.structs[stru as usize].size.get() != 0 {
|
||||
return self.ins.structs[stru as usize].size.get();
|
||||
if self.ins.structs[stru].size.get() != 0 {
|
||||
return self.ins.structs[stru].size.get();
|
||||
}
|
||||
|
||||
let mut oiter = OffsetIter::new(stru, self);
|
||||
while oiter.next(self).is_some() {}
|
||||
self.ins.structs[stru as usize].size.set(oiter.offset);
|
||||
self.ins.structs[stru].size.set(oiter.offset);
|
||||
oiter.offset
|
||||
}
|
||||
ty::Kind::Opt(opt) => {
|
||||
let base = self.ins.opts[opt as usize].base;
|
||||
let base = self.ins.opts[opt].base;
|
||||
if self.nieche_of(base).is_some() {
|
||||
self.size_of(base)
|
||||
} else {
|
||||
|
@ -1285,10 +1290,10 @@ impl Types {
|
|||
fn align_of(&self, ty: ty::Id) -> Size {
|
||||
match ty.expand() {
|
||||
ty::Kind::Struct(stru) => {
|
||||
if self.ins.structs[stru as usize].align.get() != 0 {
|
||||
return self.ins.structs[stru as usize].align.get() as _;
|
||||
if self.ins.structs[stru].align.get() != 0 {
|
||||
return self.ins.structs[stru].align.get() as _;
|
||||
}
|
||||
let align = self.ins.structs[stru as usize].explicit_alignment.map_or_else(
|
||||
let align = self.ins.structs[stru].explicit_alignment.map_or_else(
|
||||
|| {
|
||||
self.struct_fields(stru)
|
||||
.iter()
|
||||
|
@ -1298,11 +1303,11 @@ impl Types {
|
|||
},
|
||||
|a| a as _,
|
||||
);
|
||||
self.ins.structs[stru as usize].align.set(align.try_into().unwrap());
|
||||
self.ins.structs[stru].align.set(align.try_into().unwrap());
|
||||
align
|
||||
}
|
||||
ty::Kind::Slice(arr) => {
|
||||
let arr = &self.ins.slices[arr as usize];
|
||||
let arr = &self.ins.slices[arr];
|
||||
match arr.len {
|
||||
ArrayLen::MAX => 8,
|
||||
_ => self.align_of(arr.elem),
|
||||
|
@ -1314,14 +1319,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 as usize].base),
|
||||
ty::Kind::Ptr(p) => Some(self.ins.ptrs[p].base),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn inner_of(&self, ty: ty::Id) -> Option<ty::Id> {
|
||||
match ty.expand() {
|
||||
ty::Kind::Opt(o) => Some(self.ins.opts[o as usize].base),
|
||||
ty::Kind::Opt(o) => Some(self.ins.opts[o].base),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -1401,7 +1406,7 @@ impl OffsetIter {
|
|||
}
|
||||
|
||||
fn next<'a>(&mut self, tys: &'a Types) -> Option<(&'a Field, Offset)> {
|
||||
let stru = &tys.ins.structs[self.strct as usize];
|
||||
let stru = &tys.ins.structs[self.strct];
|
||||
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);
|
||||
|
@ -1579,12 +1584,10 @@ 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()),
|
||||
};
|
||||
|
||||
|
|
|
@ -1,17 +1,26 @@
|
|||
#[cfg(feature = "std")]
|
||||
fn main() -> std::io::Result<()> {
|
||||
fn main() {
|
||||
use std::io::Write;
|
||||
|
||||
log::set_logger(&hblang::Logger).unwrap();
|
||||
log::set_max_level(log::LevelFilter::Info);
|
||||
|
||||
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<_>>();
|
||||
|
||||
let opts = hblang::Options::from_args(&args)?;
|
||||
let opts = hblang::Options::from_args(&args, out)?;
|
||||
let file = args.iter().filter(|a| !a.starts_with('-')).nth(1).copied().unwrap_or("main.hb");
|
||||
|
||||
hblang::run_compiler(file, opts, out)
|
||||
}
|
||||
|
||||
log::set_logger(&hblang::fs::Logger).unwrap();
|
||||
log::set_max_level(log::LevelFilter::Error);
|
||||
|
||||
let mut out = Vec::new();
|
||||
hblang::run_compiler(file, opts, &mut out)?;
|
||||
std::io::stdout().write_all(&out)
|
||||
match run(&mut out) {
|
||||
Ok(_) => std::io::stdout().write_all(&out).unwrap(),
|
||||
Err(_) => {
|
||||
std::io::stderr().write_all(&out).unwrap();
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ use {
|
|||
crate::{
|
||||
fmt::Formatter,
|
||||
lexer::{self, Lexer, Token, TokenKind},
|
||||
ty::{Global, Module},
|
||||
utils::Ent as _,
|
||||
Ident,
|
||||
},
|
||||
alloc::{boxed::Box, string::String, vec::Vec},
|
||||
|
@ -19,10 +21,9 @@ 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<FileId, LoaderError> + 'a);
|
||||
pub type Loader<'a> = &'a mut (dyn FnMut(&str, &str, FileKind) -> Result<usize, LoaderError> + 'a);
|
||||
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
pub enum FileKind {
|
||||
|
@ -63,7 +64,7 @@ pub mod idfl {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn no_loader(_: &str, _: &str, _: FileKind) -> Result<FileId, LoaderError> {
|
||||
pub fn no_loader(_: &str, _: &str, _: FileKind) -> Result<usize, LoaderError> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
|
@ -282,6 +283,14 @@ 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;
|
||||
|
@ -299,7 +308,7 @@ impl<'a, 'b> Parser<'a, 'b> {
|
|||
pos,
|
||||
path,
|
||||
id: match (self.loader)(path, self.path, FileKind::Module) {
|
||||
Ok(id) => id,
|
||||
Ok(id) => Module::new(id),
|
||||
Err(e) => {
|
||||
self.report(str.start, format_args!("error loading dependency: {e:#}"))?
|
||||
}
|
||||
|
@ -317,7 +326,7 @@ impl<'a, 'b> Parser<'a, 'b> {
|
|||
pos,
|
||||
path,
|
||||
id: match (self.loader)(path, self.path, FileKind::Embed) {
|
||||
Ok(id) => id,
|
||||
Ok(id) => Global::new(id),
|
||||
Err(e) => self.report(
|
||||
str.start,
|
||||
format_args!("error loading embedded file: {e:#}"),
|
||||
|
@ -921,13 +930,13 @@ generate_expr! {
|
|||
/// `'@use' '(' String ')'`
|
||||
Mod {
|
||||
pos: Pos,
|
||||
id: FileId,
|
||||
id: Module,
|
||||
path: &'a str,
|
||||
},
|
||||
/// `'@use' '(' String ')'`
|
||||
Embed {
|
||||
pos: Pos,
|
||||
id: FileId,
|
||||
id: Global,
|
||||
path: &'a str,
|
||||
},
|
||||
}
|
||||
|
@ -952,14 +961,14 @@ impl Expr<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn find_pattern_path<T, F: FnOnce(&Expr) -> T>(
|
||||
pub fn find_pattern_path<T, F: FnOnce(&Expr, bool) -> T>(
|
||||
&self,
|
||||
ident: Ident,
|
||||
target: &Expr,
|
||||
mut with_final: F,
|
||||
) -> Result<T, F> {
|
||||
match *self {
|
||||
Self::Ident { id, .. } if id == ident => Ok(with_final(target)),
|
||||
Self::Ident { id, is_ct, .. } if id == ident => Ok(with_final(target, is_ct)),
|
||||
Self::Ctor { fields, .. } => {
|
||||
for &CtorField { name, value, pos } in fields {
|
||||
match value.find_pattern_path(
|
||||
|
@ -1226,7 +1235,7 @@ impl Default for Ast {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[repr(packed)]
|
||||
pub struct ExprRef(NonNull<Expr<'static>>);
|
||||
|
||||
|
|
|
@ -1,150 +1,2 @@
|
|||
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,
|
||||
}
|
||||
|
|
420
lang/src/son.rs
420
lang/src/son.rs
File diff suppressed because it is too large
Load diff
1138
lang/src/son/hbvm.rs
1138
lang/src/son/hbvm.rs
File diff suppressed because it is too large
Load diff
903
lang/src/son/hbvm/my_regalloc.rs
Normal file
903
lang/src/son/hbvm/my_regalloc.rs
Normal file
|
@ -0,0 +1,903 @@
|
|||
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,
|
||||
}
|
1007
lang/src/son/hbvm/their_regalloc.rs
Normal file
1007
lang/src/son/hbvm/their_regalloc.rs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -5,6 +5,7 @@ use {
|
|||
alloc::Layout,
|
||||
fmt::Debug,
|
||||
hint::unreachable_unchecked,
|
||||
marker::PhantomData,
|
||||
mem::MaybeUninit,
|
||||
ops::{Deref, DerefMut, Not},
|
||||
ptr::Unique,
|
||||
|
@ -62,6 +63,12 @@ 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 }
|
||||
}
|
||||
|
@ -526,3 +533,97 @@ 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;
|
||||
|
|
|
@ -1,53 +1,44 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -32d
|
||||
ST r31, r254, 0a, 32h
|
||||
ADDI64 r254, r254, -24d
|
||||
ST r31, r254, 0a, 24h
|
||||
LI32 r32, 1148846080w
|
||||
CP r2, r32
|
||||
JAL r31, r0, :sin
|
||||
FMUL32 r33, r1, r32
|
||||
FTI32 r34, r33, 1b
|
||||
ANDI r1, r34, 4294967295d
|
||||
LD r31, r254, 0a, 32h
|
||||
ADDI64 r254, r254, 32d
|
||||
FTI32 r1, r33, 1b
|
||||
LD r31, r254, 0a, 24h
|
||||
ADDI64 r254, r254, 24d
|
||||
JALA r0, r31, 0a
|
||||
sin:
|
||||
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
|
||||
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
|
||||
FSUB32 r11, r2, r8
|
||||
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
|
||||
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
|
||||
JALA r0, r31, 0a
|
||||
code size: 1370
|
||||
ret: 1000000
|
||||
code size: 1303
|
||||
ret: 826
|
||||
status: Ok(())
|
||||
|
|
6
lang/tests/son_tests_constants.txt
Normal file
6
lang/tests/son_tests_constants.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
main:
|
||||
LI64 r1, 69d
|
||||
JALA r0, r31, 0a
|
||||
code size: 29
|
||||
ret: 69
|
||||
status: Ok(())
|
|
@ -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 r42, r40
|
||||
JNE r42, r1, :3
|
||||
CP r41, r40
|
||||
JNE r41, r1, :3
|
||||
JMP :4
|
||||
3: CP r40, r42
|
||||
LD r36, r41, 8a, 8h
|
||||
3: CP r40, r41
|
||||
CP r42, r36
|
||||
ST r38, r42, 16a, 8h
|
||||
LD r36, r42, 8a, 8h
|
||||
MULI64 r43, r36, 8d
|
||||
LD r44, r41, 0a, 8h
|
||||
LD r44, r42, 0a, 8h
|
||||
ADD64 r45, r44, r43
|
||||
CP r46, r40
|
||||
9: LD r2, r41, 0a, 8h
|
||||
LD r47, r41, 8a, 8h
|
||||
9: LD r2, r42, 0a, 8h
|
||||
LD r47, r42, 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, r41, 0a, 8h
|
||||
7: ST r1, r42, 0a, 8h
|
||||
JMP :8
|
||||
5: CP r1, r40
|
||||
CP r4, r39
|
||||
ADDI64 r48, r46, 8d
|
||||
ADDI64 r42, r44, 8d
|
||||
ADDI64 r41, r46, 8d
|
||||
ADDI64 r48, r44, 8d
|
||||
LD r49, r44, 0a, 8h
|
||||
ST r49, r46, 0a, 8h
|
||||
CP r44, r42
|
||||
CP r46, r48
|
||||
CP r44, r48
|
||||
CP r46, r41
|
||||
JMP :9
|
||||
0: CP r41, r36
|
||||
8: LD r50, r41, 8a, 8h
|
||||
0: CP r42, r36
|
||||
8: LD r50, r42, 8a, 8h
|
||||
MULI64 r51, r50, 8d
|
||||
LD r52, r41, 0a, 8h
|
||||
LD r52, r42, 0a, 8h
|
||||
ADD64 r1, r52, r51
|
||||
CP r3, r32
|
||||
ST r3, r1, 0a, 8h
|
||||
LD r53, r41, 8a, 8h
|
||||
LD r53, r42, 8a, 8h
|
||||
ADD64 r54, r53, r33
|
||||
ST r54, r41, 8a, 8h
|
||||
ST r54, r42, 8a, 8h
|
||||
4: LD r31, r254, 0a, 192h
|
||||
ADDI64 r254, r254, 192d
|
||||
JALA r0, r31, 0a
|
||||
|
|
34
lang/tests/son_tests_null_check_in_the_loop.txt
Normal file
34
lang/tests/son_tests_null_check_in_the_loop.txt
Normal file
|
@ -0,0 +1,34 @@
|
|||
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(())
|
|
@ -1,7 +1,5 @@
|
|||
get_ptr:
|
||||
ADDI64 r254, r254, -8d
|
||||
ADDI64 r1, r254, 0d
|
||||
ADDI64 r254, r254, 8d
|
||||
LI64 r1, 0d
|
||||
JALA r0, r31, 0a
|
||||
main:
|
||||
ADDI64 r254, r254, -40d
|
||||
|
@ -21,6 +19,6 @@ main:
|
|||
1: LD r31, r254, 0a, 40h
|
||||
ADDI64 r254, r254, 40d
|
||||
JALA r0, r31, 0a
|
||||
code size: 208
|
||||
ret: 10
|
||||
code size: 185
|
||||
ret: 0
|
||||
status: Ok(())
|
||||
|
|
65
lang/tests/son_tests_nullable_structure.txt
Normal file
65
lang/tests/son_tests_nullable_structure.txt
Normal file
|
@ -0,0 +1,65 @@
|
|||
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(())
|
|
@ -2,8 +2,8 @@ decide:
|
|||
LI8 r1, 1b
|
||||
JALA r0, r31, 0a
|
||||
main:
|
||||
ADDI64 r254, r254, -224d
|
||||
ST r31, r254, 80a, 144h
|
||||
ADDI64 r254, r254, -232d
|
||||
ST r31, r254, 80a, 152h
|
||||
JAL r31, r0, :decide
|
||||
LI64 r32, 0d
|
||||
ADDI64 r2, r254, 72d
|
||||
|
@ -13,52 +13,54 @@ main:
|
|||
CP r34, r32
|
||||
JMP :1
|
||||
0: CP r34, r33
|
||||
1: JNE r34, r32, :2
|
||||
1: LI64 r35, 1d
|
||||
ST r35, r254, 72a, 8h
|
||||
JNE r34, r32, :2
|
||||
LI64 r1, 9001d
|
||||
JMP :3
|
||||
2: JAL r31, r0, :decide
|
||||
LI8 r35, 0b
|
||||
LI8 r36, 0b
|
||||
ANDI r1, r1, 255d
|
||||
JNE r1, r0, :4
|
||||
LI8 r36, 1b
|
||||
ST r36, r254, 56a, 1h
|
||||
LD r36, r34, 0a, 8h
|
||||
ST r36, r254, 64a, 8h
|
||||
LI8 r37, 1b
|
||||
ST r37, r254, 56a, 1h
|
||||
LD r37, r34, 0a, 8h
|
||||
ST r37, r254, 64a, 8h
|
||||
JMP :5
|
||||
4: ST r35, r254, 56a, 1h
|
||||
5: LD r37, r254, 56a, 1h
|
||||
ANDI r37, r37, 255d
|
||||
ANDI r35, r35, 255d
|
||||
JEQ r37, r35, :6
|
||||
4: ST r36, r254, 56a, 1h
|
||||
5: LD r38, r254, 56a, 1h
|
||||
ANDI r38, r38, 255d
|
||||
ANDI r36, r36, 255d
|
||||
JEQ r38, r36, :6
|
||||
LI64 r1, 42d
|
||||
JMP :3
|
||||
6: JAL r31, r0, :decide
|
||||
LI32 r38, 0w
|
||||
LI32 r39, 0w
|
||||
ANDI r1, r1, 255d
|
||||
JNE r1, r0, :7
|
||||
CP r39, r38
|
||||
CP r40, r39
|
||||
JMP :8
|
||||
7: LI32 r39, 8388609w
|
||||
8: ANDI r39, r39, 4294967295d
|
||||
ANDI r38, r38, 4294967295d
|
||||
JNE r39, r38, :9
|
||||
7: LI32 r40, 2147483649w
|
||||
8: ANDI r40, r40, 4294967295d
|
||||
ANDI r39, r39, 4294967295d
|
||||
JNE r40, r39, :9
|
||||
LI64 r1, 69d
|
||||
JMP :3
|
||||
9: ADDI64 r3, r254, 40d
|
||||
CP r40, r3
|
||||
CP r41, r3
|
||||
JAL r31, r0, :new_foo
|
||||
ST r1, r254, 40a, 16h
|
||||
LI64 r32, 0d
|
||||
LD r41, r254, 40a, 8h
|
||||
JNE r41, r32, :10
|
||||
LI64 r42, 0d
|
||||
LD r43, r254, 40a, 8h
|
||||
JNE r43, r42, :10
|
||||
LI64 r1, 999d
|
||||
JMP :3
|
||||
10: LRA r4, r0, :"foo\0"
|
||||
CP r3, r40
|
||||
CP r3, r41
|
||||
CP r2, r3
|
||||
LD r2, r2, 0a, 16h
|
||||
JAL r31, r0, :use_foo
|
||||
ADDI64 r42, r254, 0d
|
||||
ADDI64 r44, r254, 0d
|
||||
JAL r31, r0, :no_foo
|
||||
ST r1, r254, 0a, 16h
|
||||
JAL r31, r0, :decide
|
||||
|
@ -68,11 +70,9 @@ main:
|
|||
JMP :12
|
||||
11: CP r2, r33
|
||||
ST r2, r254, 0a, 8h
|
||||
LI64 r43, 1d
|
||||
ST r43, r254, 8a, 8h
|
||||
ST r43, r254, 72a, 8h
|
||||
12: LD r44, r254, 0a, 8h
|
||||
JNE r44, r32, :13
|
||||
ST r35, r254, 8a, 8h
|
||||
12: LD r45, r254, 0a, 8h
|
||||
JNE r45, r42, :13
|
||||
LI64 r1, 34d
|
||||
JMP :3
|
||||
13: ADDI64 r1, r254, 16d
|
||||
|
@ -81,24 +81,29 @@ main:
|
|||
ANDI r1, r1, 255d
|
||||
JNE r1, r0, :14
|
||||
JMP :15
|
||||
14: ST r35, r254, 16a, 1h
|
||||
15: LD r45, r254, 16a, 1h
|
||||
ANDI r45, r45, 255d
|
||||
ANDI r35, r35, 255d
|
||||
JEQ r45, r35, :16
|
||||
14: ST r36, r254, 16a, 1h
|
||||
15: LD r46, r254, 16a, 1h
|
||||
ANDI r46, r46, 255d
|
||||
ANDI r36, r36, 255d
|
||||
JEQ r46, r36, :16
|
||||
LI64 r1, 420d
|
||||
JMP :3
|
||||
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
|
||||
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
|
||||
JALA r0, r31, 0a
|
||||
new_bar:
|
||||
ADDI64 r254, r254, -24d
|
||||
ADDI64 r5, r254, 0d
|
||||
BMC r1, r1, 24h
|
||||
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 r254, r254, 24d
|
||||
JALA r0, r31, 0a
|
||||
new_foo:
|
||||
|
@ -125,6 +130,6 @@ use_foo:
|
|||
ADDI64 r2, r254, 0d
|
||||
ADDI64 r254, r254, 16d
|
||||
JALA r0, r31, 0a
|
||||
code size: 1091
|
||||
code size: 1143
|
||||
ret: 0
|
||||
status: Ok(())
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
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
|
||||
^
|
||||
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)
|
||||
|
|
31
lang/tests/son_tests_returning_optional_issues.txt
Normal file
31
lang/tests/son_tests_returning_optional_issues.txt
Normal file
|
@ -0,0 +1,31 @@
|
|||
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(())
|
6
lang/tests/son_tests_stack_provenance.txt
Normal file
6
lang/tests/son_tests_stack_provenance.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
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
|
||||
^
|
Loading…
Reference in a new issue