Merge remote-tracking branch 'holey-bytes-main/trunk' into trunk

This commit is contained in:
peony 2024-11-07 21:32:24 +01:00
commit 5964857423
53 changed files with 3780 additions and 2201 deletions

1
.gitignore vendored
View file

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

52
Cargo.lock generated
View file

@ -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",

View file

@ -6,7 +6,7 @@ use {
alloc::{string::String, vec::Vec},
hblang::{
parser::FileId,
son::{Codegen, CodegenCtx},
son::{hbvm::HbvmBackend, Codegen, CodegenCtx},
},
};
@ -55,8 +55,9 @@ unsafe fn compile_and_run(mut fuel: usize) {
files
};
let mut ctx = CodegenCtx::default();
let files = {
let mut ctx = hblang::parser::Ctx::default();
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),
@ -69,7 +70,7 @@ unsafe fn compile_and_run(mut fuel: usize) {
f.path,
// since 'free' does nothing this is fine
String::from_raw_parts(f.code.as_mut_ptr(), f.code.len(), f.code.len()),
&mut ctx,
&mut ctx.parser,
&mut loader,
)
})
@ -77,9 +78,15 @@ unsafe fn compile_and_run(mut fuel: usize) {
};
let mut ct = {
let mut ctx = CodegenCtx::default();
let mut c = Codegen::new(&files, &mut ctx);
c.generate(root as FileId);
let mut backend = HbvmBackend::default();
Codegen::new(&mut backend, &files, &mut ctx).generate(root as FileId);
if !ctx.parser.errors.borrow().is_empty() {
log::error!("{}", ctx.parser.errors.borrow());
return;
}
let mut c = Codegen::new(&mut backend, &files, &mut ctx);
c.assemble_comptime()
};

47
f.txt
View file

@ -1,47 +0,0 @@
start: 0
46-c65535: +: [0, 38, 45] [47]
45-c65535: &: [0, 43, 44] [46]
44-c65535: cint: #255 [0] [45]
43-c65535: load: [0, 42, 23] [45]
42-c65535: +: [0, 22, 18] [43]
39-c65535: stre: [0, 0, 36, 37] [47]
39-c65535: stre: [0, 0, 36, 37] [47]
37-c65535: stre: [0, 35, 36, 3] [38, 39]
36-c65535: stck: [0, 3] [37, 38, 39]
35-c65535: load: [0, 24, 34] [37]
34-c65535: stre: [0, 31, 33, 30] [35, 47]
33-c65535: +: [0, 24, 32] [34]
32-c65535: cint: #16 [0] [33]
31-c65535: cint: #4 [0] [34]
30-c65535: stre: [0, 27, 29, 26] [34]
29-c65535: +: [0, 24, 28] [30]
28-c65535: cint: #8 [0] [29]
27-c65535: cint: #2 [0] [30]
26-c65535: stre: [0, 25, 24, 3] [30]
25-c65535: cint: #1 [0] [26]
24-c65535: stck: [0, 3] [26, 29, 33, 35]
23-c65535: stre: [0, 21, 22, 3] [43, 47]
22-c65535: stck: [0, 3] [23, 42]
21-c65535: load: [0, 6, 20] [23]
20-c65535: stre: [0, 17, 19, 14] [21, 47]
19-c65535: +: [0, 6, 18] [20]
18-c65535: cint: #3 [0] [19, 42]
17-c65535: cint: #1 [0] [20]
14-c65535: stre: [0, 5, 13, 11] [20]
13-c65535: +: [0, 6, 12] [14]
12-c65535: cint: #2 [0] [13]
11-c65535: stre: [0, 7, 10, 8] [14]
10-c65535: +: [0, 6, 9] [11]
9-c65535: cint: #1 [0] [10]
8-c65535: stre: [0, 7, 6, 3] [11]
7-c65535: cint: #0 [0] [8, 11]
6-c65535: stck: [0, 3] [8, 10, 13, 19, 21]
5-c65535: cint: #511 [0] [14]
4-c65535: loops: [0] []
3-c65535: mem: [0] [6, 8, 22, 23, 24, 26, 36, 37, 47]
2-c65535: ctrl: entry [0] [38]
b2: 0 0 [38]
38-c65535: call: 1 0 [2, 36, 37] [46, 47]
47-c65535: ret: [38, 46, 3, 20, 23, 34, 39] [1]

38
f1.txt
View file

@ -1,38 +0,0 @@
start: 0
34-c65535: stre: [0, 31, 51, 30] [39, 47, 38]
51-c65535: +: [0, 36, 32] [34]
32-c65535: cint: #16 [0] [51]
31-c65535: cint: #4 [0] [34]
30-c65535: stre: [0, 27, 50, 26] [34]
50-c65535: +: [0, 36, 28] [30]
28-c65535: cint: #8 [0] [50]
27-c65535: cint: #2 [0] [30]
26-c65535: stre: [0, 25, 36, 3] [30]
25-c65535: cint: #1 [0] [26, 46]
39-c65535: stre: [0, 0, 36, 34] [47]
36-c65535: stck: [0, 3] [51, 38, 39, 26, 50]
22-c65535: stck: [0, 3] [49, 42, 8, 48]
39-c65535: stre: [0, 0, 36, 34] [47]
20-c65535: stre: [0, 17, 42, 14] [47, 47]
46-c65535: +: [0, 38, 25] [47]
18-c65535: cint: #3 [0] [42]
17-c65535: cint: #1 [0] [20]
14-c65535: stre: [0, 5, 49, 11] [20]
49-c65535: +: [0, 22, 12] [14]
12-c65535: cint: #2 [0] [49]
11-c65535: stre: [0, 7, 48, 8] [14]
48-c65535: +: [0, 22, 9] [11]
9-c65535: cint: #1 [0] [48]
8-c65535: stre: [0, 7, 22, 3] [11]
7-c65535: cint: #0 [0] [8, 11]
42-c65535: +: [0, 22, 18] [20]
5-c65535: cint: #511 [0] [14]
4-c65535: loops: [0] []
3-c65535: mem: [0] [47, 8, 22, 26, 36]
2-c65535: ctrl: entry [0] [38]
b2: 0 0 [38]
38-c65535: call: 1 0 [2, 36, 34] [46, 47]
47-c65535: ret: [38, 46, 3, 20, 20, 34, 39] [1]

39
f2.txt
View file

@ -1,39 +0,0 @@
start: 0
42-c65535: +: [0, 22, 18] [43, 20]
36-c65535: stck: [0, 3] [51, 38, 39, 26, 50]
39-c65535: stre: [0, 0, 36, 34] [47]
34-c65535: stre: [0, 31, 51, 30] [39, 47, 38]
51-c65535: +: [0, 36, 32] [34]
32-c65535: cint: #16 [0] [51]
31-c65535: cint: #4 [0] [34]
30-c65535: stre: [0, 27, 50, 26] [34]
50-c65535: +: [0, 36, 28] [30]
28-c65535: cint: #8 [0] [50]
27-c65535: cint: #2 [0] [30]
26-c65535: stre: [0, 25, 36, 3] [30]
25-c65535: cint: #1 [0] [26]
39-c65535: stre: [0, 0, 36, 34] [47]
45-c65535: &: [0, 43, 44] [46]
22-c65535: stck: [0, 3] [49, 42, 8, 48]
44-c65535: cint: #255 [0] [45]
20-c65535: stre: [0, 17, 42, 14] [47, 47, 43]
46-c65535: +: [0, 38, 45] [47]
18-c65535: cint: #3 [0] [42]
17-c65535: cint: #1 [0] [20]
14-c65535: stre: [0, 5, 49, 11] [20]
49-c65535: +: [0, 22, 12] [14]
12-c65535: cint: #2 [0] [49]
11-c65535: stre: [0, 7, 48, 8] [14]
48-c65535: +: [0, 22, 9] [11]
9-c65535: cint: #1 [0] [48]
8-c65535: stre: [0, 7, 22, 3] [11]
7-c65535: cint: #0 [0] [8, 11]
43-c65535: load: [0, 42, 20] [45]
5-c65535: cint: #511 [0] [14]
4-c65535: loops: [0] []
3-c65535: mem: [0] [47, 8, 22, 26, 36]
2-c65535: ctrl: entry [0] [38]
b2: 0 0 [38]
38-c65535: call: 1 0 [2, 36, 34] [46, 47]
47-c65535: ret: [38, 46, 3, 20, 20, 34, 39] [1]

File diff suppressed because one or more lines are too long

View file

@ -346,6 +346,7 @@ impl<'a> Formatter<'a> {
self.fmt(val, f)
}
Expr::Return { val: None, .. } => f.write_str("return"),
Expr::Wildcard { .. } => f.write_str("_"),
Expr::Ident { pos, is_ct, .. } => {
if is_ct {
f.write_str("$")?;

View file

@ -1,7 +1,7 @@
use {
crate::{
parser::{self, Ast, Ctx, FileKind},
son,
son::{self, hbvm::HbvmBackend},
},
alloc::{string::String, vec::Vec},
core::{fmt::Write, num::NonZeroUsize, ops::Deref},
@ -81,16 +81,27 @@ pub fn run_compiler(root_file: &str, options: Options, out: &mut Vec<u8>) -> std
}
if options.fmt {
if !parsed.errors.is_empty() {
*out = parsed.errors.into_bytes();
return Err(std::io::Error::other("parsing fialed"));
}
for parsed in parsed.ast {
format_ast(parsed)?;
}
} else if options.fmt_stdout {
if !parsed.errors.is_empty() {
*out = parsed.errors.into_bytes();
return Err(std::io::Error::other("parsing fialed"));
}
let ast = parsed.ast.into_iter().next().unwrap();
write!(out, "{ast}").unwrap();
} else {
let mut backend = HbvmBackend::default();
let mut ctx = crate::son::CodegenCtx::default();
*ctx.parser.errors.get_mut() = parsed.errors;
let mut codegen = son::Codegen::new(&parsed.ast, &mut ctx);
let mut codegen = son::Codegen::new(&mut backend, &parsed.ast, &mut ctx);
codegen.push_embeds(parsed.embeds);
codegen.generate(0);
@ -100,12 +111,12 @@ pub fn run_compiler(root_file: &str, options: Options, out: &mut Vec<u8>) -> std
return Err(std::io::Error::other("compilation faoled"));
}
codegen.assemble(out);
if options.dump_asm {
codegen
.disasm(unsafe { std::mem::transmute::<&mut Vec<u8>, &mut String>(out) })
.map_err(|e| io::Error::other(e.to_string()))?;
} else {
codegen.assemble(out);
let mut disasm = String::new();
codegen.disasm(&mut disasm, out).map_err(|e| io::Error::other(e.to_string()))?;
*out = disasm.into_bytes();
}
}

View file

@ -2,7 +2,7 @@ use {
crate::{
lexer::TokenKind,
parser,
son::{Codegen, CodegenCtx},
son::{hbvm::HbvmBackend, Codegen, CodegenCtx},
},
alloc::string::String,
core::{fmt::Write, hash::BuildHasher, ops::Range},
@ -133,7 +133,8 @@ pub fn fuzz(seed_range: Range<u64>) {
assert!(ctx.parser.errors.get_mut().is_empty());
let mut cdg = Codegen::new(core::slice::from_ref(&parsed), &mut ctx);
let mut backend = HbvmBackend::default();
let mut cdg = Codegen::new(&mut backend, core::slice::from_ref(&parsed), &mut ctx);
cdg.generate(0);
}
}

View file

@ -148,6 +148,7 @@ pub enum TokenKind {
RBrack = b']',
Xor = b'^',
Tick = b'`',
Under = b'_',
// Unused = a-z
LBrace = b'{',
Bor = b'|',
@ -308,6 +309,7 @@ gen_token_kind! {
Null = b"null",
Idk = b"idk",
Die = b"die",
Under = b"_",
#[punkt]
Ctor = ".{",
Tupl = ".(",

View file

@ -219,26 +219,6 @@ mod ctx_map {
}
}
mod task {
use super::Offset;
pub fn unpack(offset: Offset) -> Result<Offset, usize> {
if offset >> 31 != 0 {
Err((offset & !(1 << 31)) as usize)
} else {
Ok(offset)
}
}
pub fn is_done(offset: Offset) -> bool {
unpack(offset).is_ok()
}
pub fn id(index: usize) -> Offset {
1 << 31 | index as u32
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
pub struct Ident(u32);
@ -567,6 +547,7 @@ mod ty {
};)*
}
#[expect(dead_code)]
impl Id {
$(pub const $name: Self = Kind::Builtin($name).compress();)*
}
@ -758,7 +739,7 @@ pub enum SymKey<'a> {
}
#[derive(Clone, Copy)]
struct Sig {
pub struct Sig {
args: ty::Tuple,
ret: ty::Id,
}
@ -769,10 +750,7 @@ struct Func {
base: Option<ty::Func>,
expr: ExprRef,
sig: Option<Sig>,
offset: Offset,
// TODO: change to indices into common vec
relocs: Vec<TypedReloc>,
code: Vec<u8>,
comp_state: [CompState; 2],
}
impl Default for Func {
@ -783,13 +761,19 @@ impl Default for Func {
base: None,
expr: Default::default(),
sig: None,
offset: u32::MAX,
relocs: Default::default(),
code: Default::default(),
comp_state: Default::default(),
}
}
}
#[derive(Default, PartialEq, Eq)]
enum CompState {
#[default]
Dead,
Queued(usize),
Compiled,
}
#[derive(Clone, Copy)]
struct TypedReloc {
target: ty::Id,
@ -801,7 +785,6 @@ struct Global {
file: FileId,
name: Ident,
ty: ty::Id,
offset: Offset,
data: Vec<u8>,
}
@ -809,7 +792,6 @@ impl Default for Global {
fn default() -> Self {
Self {
ty: Default::default(),
offset: u32::MAX,
data: Default::default(),
file: u32::MAX,
name: Default::default(),
@ -947,9 +929,6 @@ impl IdentInterner {
#[derive(Default)]
struct TypesTmp {
fields: Vec<Field>,
frontier: Vec<ty::Id>,
globals: Vec<ty::Global>,
funcs: Vec<ty::Func>,
args: Vec<ty::Id>,
}
@ -968,6 +947,7 @@ pub struct TypeIns {
struct FTask {
file: FileId,
id: ty::Func,
ct: bool,
}
struct StringRef(ty::Global);
@ -982,7 +962,7 @@ impl ctx_map::CtxEntry for StringRef {
}
#[derive(Default)]
struct Types {
pub struct Types {
syms: ctx_map::CtxMap<ty::Id>,
names: IdentInterner,
strings: ctx_map::CtxMap<StringRef>,
@ -1222,12 +1202,6 @@ impl Types {
&self.ins.fields[self.struct_field_range(strct)]
}
fn reassemble(&mut self, buf: &mut Vec<u8>) {
self.ins.funcs.iter_mut().for_each(|f| f.offset = u32::MAX);
self.ins.globals.iter_mut().for_each(|g| g.offset = u32::MAX);
self.assemble(buf)
}
fn parama(&self, ret: impl Into<ty::Id>) -> (Option<PLoc>, ParamAlloc) {
let mut iter = ParamAlloc(1..12);
let ret = iter.next(ret.into(), self);
@ -1395,9 +1369,6 @@ impl Types {
self.ins.slices.clear();
debug_assert_eq!(self.tmp.fields.len(), 0);
debug_assert_eq!(self.tmp.frontier.len(), 0);
debug_assert_eq!(self.tmp.globals.len(), 0);
debug_assert_eq!(self.tmp.funcs.len(), 0);
debug_assert_eq!(self.tmp.args.len(), 0);
debug_assert_eq!(self.tasks.len(), 0);

View file

@ -392,6 +392,7 @@ impl<'a, 'b> Parser<'a, 'b> {
let (id, is_first) = self.resolve_ident(token);
E::Ident { pos, is_ct: token.kind == T::CtIdent, id, is_first }
}
T::Under => E::Wildcard { pos },
T::If => E::If {
pos,
cond: self.ptr_expr()?,
@ -800,6 +801,9 @@ generate_expr! {
pos: Pos,
val: Option<&'a Self>,
},
Wildcard {
pos: Pos,
},
/// note: ':unicode:' is any utf-8 character except ascii
/// `'[a-zA-Z_:unicode:][a-zA-Z0-9_:unicode:]*'`
Ident {
@ -1163,14 +1167,16 @@ fn report_to(file: &str, path: &str, pos: Pos, msg: &dyn fmt::Display, out: &mut
let line = &file[file[..pos as usize].rfind('\n').map_or(0, |i| i + 1)
..file[pos as usize..].find('\n').map_or(file.len(), |i| i + pos as usize)];
col += line.matches('\t').count() * 3;
col += line.chars().take_while(|c| c.is_whitespace()).filter(|&c| c == '\t').count() * 3;
let mut has_non_whitespace = false;
for char in line.chars() {
if char == '\t' {
if char == '\t' && !has_non_whitespace {
_ = out.write_str(" ");
} else {
_ = out.write_char(char);
}
has_non_whitespace |= !char.is_whitespace();
}
_ = out.write_char('\n');
for _ in 0..col - 1 {

View file

@ -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,
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View 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::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>) {