diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index cfee929b..00000000 --- a/Cargo.lock +++ /dev/null @@ -1,283 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "ahash" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy", -] - -[[package]] -name = "arbitrary" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" - -[[package]] -name = "bumpalo" -version = "3.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "cranelift-bforest" -version = "0.113.0" -source = "git+https://github.com/jakubDoka/wasmtime.git#969c85a481985be2fa2d555cdb338454f77213c6" -dependencies = [ - "cranelift-entity", -] - -[[package]] -name = "cranelift-bitset" -version = "0.113.0" -source = "git+https://github.com/jakubDoka/wasmtime.git#969c85a481985be2fa2d555cdb338454f77213c6" - -[[package]] -name = "cranelift-codegen" -version = "0.113.0" -source = "git+https://github.com/jakubDoka/wasmtime.git#969c85a481985be2fa2d555cdb338454f77213c6" -dependencies = [ - "bumpalo", - "cranelift-bforest", - "cranelift-bitset", - "cranelift-codegen-meta", - "cranelift-codegen-shared", - "cranelift-control", - "cranelift-entity", - "cranelift-isle", - "hashbrown", - "log", - "regalloc2", - "rustc-hash", - "smallvec", - "target-lexicon", -] - -[[package]] -name = "cranelift-codegen-meta" -version = "0.113.0" -source = "git+https://github.com/jakubDoka/wasmtime.git#969c85a481985be2fa2d555cdb338454f77213c6" -dependencies = [ - "cranelift-codegen-shared", -] - -[[package]] -name = "cranelift-codegen-shared" -version = "0.113.0" -source = "git+https://github.com/jakubDoka/wasmtime.git#969c85a481985be2fa2d555cdb338454f77213c6" - -[[package]] -name = "cranelift-control" -version = "0.113.0" -source = "git+https://github.com/jakubDoka/wasmtime.git#969c85a481985be2fa2d555cdb338454f77213c6" -dependencies = [ - "arbitrary", -] - -[[package]] -name = "cranelift-entity" -version = "0.113.0" -source = "git+https://github.com/jakubDoka/wasmtime.git#969c85a481985be2fa2d555cdb338454f77213c6" -dependencies = [ - "cranelift-bitset", -] - -[[package]] -name = "cranelift-isle" -version = "0.113.0" -source = "git+https://github.com/jakubDoka/wasmtime.git#969c85a481985be2fa2d555cdb338454f77213c6" - -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", -] - -[[package]] -name = "hbbytecode" -version = "0.1.0" - -[[package]] -name = "hbcb" -version = "0.1.0" -dependencies = [ - "cranelift-codegen", - "cranelift-codegen-meta", - "cranelift-control", - "cranelift-isle", - "log", - "regalloc2", - "smallvec", - "target-lexicon", -] - -[[package]] -name = "hbjit" -version = "0.1.0" - -[[package]] -name = "hblang" -version = "0.1.0" -dependencies = [ - "hbvm", -] - -[[package]] -name = "hbvm" -version = "0.1.0" -dependencies = [ - "hbbytecode", -] - -[[package]] -name = "hbxrt" -version = "0.1.0" -dependencies = [ - "hbvm", - "memmap2", -] - -[[package]] -name = "libc" -version = "0.2.158" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" - -[[package]] -name = "log" -version = "0.4.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" - -[[package]] -name = "memmap2" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" -dependencies = [ - "libc", -] - -[[package]] -name = "once_cell" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" - -[[package]] -name = "proc-macro2" -version = "1.0.86" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "regalloc2" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12908dbeb234370af84d0579b9f68258a0f67e201412dd9a2814e6f45b2fc0f0" -dependencies = [ - "hashbrown", - "log", - "rustc-hash", - "slice-group-by", - "smallvec", -] - -[[package]] -name = "rustc-hash" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" - -[[package]] -name = "slice-group-by" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" - -[[package]] -name = "smallvec" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" - -[[package]] -name = "syn" -version = "2.0.77" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "target-lexicon" -version = "0.12.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" - -[[package]] -name = "unicode-ident" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "xtask" -version = "0.1.0" - -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] diff --git a/hblang/Cargo.toml b/hblang/Cargo.toml index ee487b48..90a0c45a 100644 --- a/hblang/Cargo.toml +++ b/hblang/Cargo.toml @@ -9,3 +9,4 @@ path = "src/main.rs" [dependencies] hbvm = { path = "../hbvm", features = ["nightly"] } +regalloc2 = { git = "https://github.com/jakubDoka/regalloc2" } diff --git a/hblang/README.md b/hblang/README.md index d58e63f6..3db5fe30 100644 --- a/hblang/README.md +++ b/hblang/README.md @@ -237,7 +237,7 @@ main := fn(): int { size_of_Type_in_bytes := @sizeof(foo.Type) align_of_Type_in_bytes := @alignof(foo.Type) hardcoded_pointer := @as(^u8, @bitcast(10)) - ecall_that_returns_int := @eca(int, 1, foo.Type.(10, 20), 5, 6) + ecall_that_returns_int := @as(int, @eca(1, foo.Type.(10, 20), 5, 6)) return @inline(foo.foo) } @@ -356,6 +356,29 @@ main := fn(): int { } ``` +#### wide_ret +```hb +OemIdent := struct { + dos_version: [u8; 8], + dos_version_name: [u8; 8], +} + +Stru := struct { + a: u16, + b: u16, +} + +small_struct := fn(): Stru { + return .{a: 0, b: 0} +} + +main := fn(major: int, minor: int): OemIdent { + small_struct() + ver := [u8].(0, 0, 0, 0, 0, 0, 0, 0) + return OemIdent.(ver, ver) +} +``` + ### Incomplete Examples #### comptime_pointers @@ -376,8 +399,8 @@ modify := fn($num: ^int): void { MALLOC_SYS_CALL := 69 FREE_SYS_CALL := 96 -malloc := fn(size: uint, align: uint): ^void return @eca(^void, MALLOC_SYS_CALL, size, align) -free := fn(ptr: ^void, size: uint, align: uint): void return @eca(void, FREE_SYS_CALL, ptr, size, align) +malloc := fn(size: uint, align: uint): ^void return @eca(MALLOC_SYS_CALL, size, align) +free := fn(ptr: ^void, size: uint, align: uint): void return @eca(FREE_SYS_CALL, ptr, size, align) Vec := fn($Elem: type): type { return struct { @@ -750,7 +773,7 @@ screenidx := fn(orange: int): int { // in module: random.hb integer := fn(min: int, max: int): int { - rng := @eca(int, 3, 4) + rng := @as(int, @eca(3, 4)) if min != 0 | max != 0 { return rng % (max - min + 1) + min @@ -781,7 +804,7 @@ main := fn(): void { // in module: random.hb integer_range := fn(min: uint, max: int): uint { - return @eca(uint, 3, 4) % (@bitcast(max) - min + 1) + min + return @eca(3, 4) % (@bitcast(max) - min + 1) + min } ``` @@ -876,7 +899,7 @@ request_page := fn(page_count: u8): ^u8 { msg := "\{00}\{01}xxxxxxxx\0" msg_page_count := msg + 1; *msg_page_count = page_count - return @eca(^u8, 3, 2, msg, 12) + return @eca(3, 2, msg, 12) } create_back_buffer := fn(total_pages: int): ^u32 { diff --git a/hblang/src/codegen.rs b/hblang/src/codegen.rs index 669921d8..ce4b1c07 100644 --- a/hblang/src/codegen.rs +++ b/hblang/src/codegen.rs @@ -898,13 +898,21 @@ impl Codegen { E::Directive { name: "TypeOf", args: [expr], .. } => { Some(Value::ty(self.infer_type(expr))) } - E::Directive { name: "eca", args: [ret_ty, args @ ..], .. } => { - let ty = self.ty(ret_ty); + E::Directive { name: "eca", args, pos } => { + let Some(ty) = ctx.ty else { + self.report( + pos, + "type to return form eca is unknown, use `@as(, @eca(...))`", + ); + }; let mut parama = self.tys.parama(ty); let base = self.pool.arg_locs.len(); for arg in args { let arg = self.expr(arg)?; + if arg.ty == ty::Id::from(ty::TYPE) { + self.report(pos, "na na na nana, no passing types to ecas"); + } self.pass_arg(&arg, &mut parama); self.pool.arg_locs.push(arg.loc); } @@ -932,7 +940,7 @@ impl Codegen { let Some(ty) = ctx.ty else { self.report( expr.pos(), - "type to cast to is unknown, use `@as(, )`", + "type to cast to is unknown, use `@as(, @intcast())`", ); }; let mut val = self.expr(val)?; @@ -1086,7 +1094,7 @@ impl Codegen { Some(Value::new(self.tys.make_ptr(ty::U8.into()), reg)) } E::Ctor { pos, ty, fields, .. } => { - let (ty, loc) = self.prepare_struct_ctor(pos, ctx, ty, fields.len()); + let (ty, loc) = self.prepare_struct_ctor(pos, &mut ctx, ty, fields.len()); let ty::Kind::Struct(stru) = ty.expand() else { self.report( @@ -1103,10 +1111,16 @@ impl Codegen { let value = self.expr_ctx(value, Ctx::default().with_loc(loc).with_ty(ty))?; self.ci.free_loc(value.loc); } - return Some(Value { ty, loc }); + + if let Some(dst_loc) = ctx.loc { + self.store_typed(loc, &dst_loc, ty); + return Some(Value { ty, loc: dst_loc }); + } else { + return Some(Value { ty, loc }); + } } E::Tupl { pos, ty, fields, .. } => { - let (ty, loc) = self.prepare_struct_ctor(pos, ctx, ty, fields.len()); + let (ty, loc) = self.prepare_struct_ctor(pos, &mut ctx, ty, fields.len()); match ty.expand() { ty::Kind::Struct(stru) => { @@ -1141,7 +1155,12 @@ impl Codegen { ), } - return Some(Value { ty, loc }); + if let Some(dst_loc) = ctx.loc { + self.store_typed(loc, &dst_loc, ty); + return Some(Value { ty, loc: dst_loc }); + } else { + return Some(Value { ty, loc }); + } } E::Field { target, name: field } => { let checkpoint = self.ci.snap(); @@ -1369,24 +1388,24 @@ impl Codegen { tk => Some(Value::ty(tk.compress())), }, E::Return { pos, val, .. } => { - let ty = if let Some(val) = val { - let size = self.ci.ret.map_or(17, |ty| self.tys.size_of(ty)); - let loc = match size { - _ if self.ci.inline_ret_loc != Loc::default() => { - Some(self.ci.inline_ret_loc.as_ref()) - } - 0 => None, - 1..=16 => Some(Loc::reg(1)), - _ => Some(Loc::reg(self.ci.ret_reg.as_ref()).into_derefed()), - }; - self.expr_ctx(val, Ctx { ty: self.ci.ret, loc })?.ty + let size = self.ci.ret.map_or(17, |ty| self.tys.size_of(ty)); + let loc = match size { + _ if self.ci.inline_ret_loc != Loc::default() => { + Some(self.ci.inline_ret_loc.as_ref()) + } + 0 => None, + 1..=16 => Some(Loc::reg(1)), + _ => Some(Loc::reg(self.ci.ret_reg.as_ref()).into_derefed()), + }; + let value = if let Some(val) = val { + self.expr_ctx(val, Ctx { ty: self.ci.ret, loc })? } else { - ty::VOID.into() + Value::void() }; match self.ci.ret { - None => self.ci.ret = Some(ty), - Some(ret) => _ = self.assert_ty(pos, ty, ret, "return type"), + None => self.ci.ret = Some(value.ty), + Some(ret) => _ = self.assert_ty(pos, value.ty, ret, "return type"), } self.ci.ret_relocs.push(Reloc::new(self.ci.code.len(), 1, 4)); @@ -1891,7 +1910,7 @@ impl Codegen { fn prepare_struct_ctor( &mut self, pos: Pos, - ctx: Ctx, + ctx: &mut Ctx, ty: Option<&Expr>, field_len: usize, ) -> (ty::Id, Loc) { @@ -1927,8 +1946,11 @@ impl Codegen { } let size = self.tys.size_of(ty); - let loc = ctx.loc.unwrap_or_else(|| Loc::stack(self.ci.stack.allocate(size))); - (ty, loc) + if ctx.loc.as_ref().map_or(true, |l| l.is_reg()) { + (ty, Loc::stack(self.ci.stack.allocate(size))) + } else { + (ty, ctx.loc.take().unwrap_or_else(|| Loc::stack(self.ci.stack.allocate(size)))) + } } fn struct_op( @@ -2278,6 +2300,25 @@ impl Codegen { self.ci.emit(cp(dst.get(), src.get())); } } + //(lpat!(false, src, 0, None), lpat!(false, dst, off, None)) => { + // assert!(size <= 8); + // let off_rem = 8 * (off % 8); + // let freg = dst.get() + (off / 8) as u8; + // if size < 8 { + // let mask = !(((1u64 << (8 * size)) - 1) << off_rem); + // self.ci.emit(andi(freg, freg, mask)); + // if off_rem == 0 { + // self.ci.emit(or(freg, freg, src.get())); + // } else { + // let tmp = self.ci.regs.allocate(); + // self.ci.emit(slui64(tmp.get(), src.get(), off_rem as _)); + // self.ci.emit(or(freg, freg, src.get())); + // self.ci.regs.free(tmp); + // } + // } else { + // self.ci.emit(cp(freg, src.get())); + // } + //} (lpat!(true, src, soff, ref ssta), lpat!(false, dst, 0, None)) => { if size < 8 { self.ci.emit(cp(dst.get(), 0)); @@ -2740,5 +2781,6 @@ mod tests { writing_into_string => README; request_page => README; tests_ptr_to_ptr_copy => README; + wide_ret => README; } } diff --git a/hblang/src/lexer.rs b/hblang/src/lexer.rs index d7d5c1cf..99a6115c 100644 --- a/hblang/src/lexer.rs +++ b/hblang/src/lexer.rs @@ -371,6 +371,10 @@ impl<'a> Lexer<'a> { Self { pos, bytes: input.as_bytes() } } + pub fn source(&self) -> &'a str { + unsafe { std::str::from_utf8_unchecked(self.bytes) } + } + pub fn slice(&self, tok: std::ops::Range) -> &'a str { unsafe { std::str::from_utf8_unchecked(&self.bytes[tok]) } } @@ -511,10 +515,6 @@ impl<'a> Lexer<'a> { false } } - - pub fn line_col(&self, pos: u32) -> (usize, usize) { - line_col(self.bytes, pos) - } } pub fn line_col(bytes: &[u8], pos: u32) -> (usize, usize) { diff --git a/hblang/src/lib.rs b/hblang/src/lib.rs index 0ff925f1..4bc6835b 100644 --- a/hblang/src/lib.rs +++ b/hblang/src/lib.rs @@ -30,7 +30,7 @@ use { parser::Ast, std::{ collections::{hash_map, BTreeMap, VecDeque}, - io::{self, Read}, + io, ops::Range, path::{Path, PathBuf}, rc::Rc, @@ -601,6 +601,19 @@ impl ParamAlloc { } } +#[repr(packed)] +#[allow(dead_code)] +struct AbleOsExecutableHeader { + magic_number: [u8; 3], + executable_version: u32, + + code_length: u64, + data_length: u64, + debug_length: u64, + config_length: u64, + metadata_length: u64, +} + #[derive(Default)] struct Types { syms: HashMap, @@ -613,15 +626,24 @@ struct Types { arrays: Vec, } +const HEADER_SIZE: usize = std::mem::size_of::(); + impl Types { fn assemble(&mut self, to: &mut Vec) { + to.extend([0u8; HEADER_SIZE]); + emit(to, instrs::jal(reg::RET_ADDR, reg::ZERO, 0)); emit(to, instrs::tx()); - self.dump_reachable(0, to); - Reloc::new(0, 3, 4).apply_jump(to, self.funcs[0].offset, 0); + let exe = self.dump_reachable(0, to); + Reloc::new(HEADER_SIZE, 3, 4).apply_jump(to, self.funcs[0].offset, 0); + + unsafe { *to.as_mut_ptr().cast::() = exe } } - fn dump_reachable(&mut self, from: ty::Func, to: &mut Vec) { + fn dump_reachable(&mut self, from: ty::Func, to: &mut Vec) -> AbleOsExecutableHeader { + let mut used_funcs = vec![]; + let mut used_globals = vec![]; + let mut frontier = vec![ty::Kind::Func(from).compress()]; while let Some(itm) = frontier.pop() { @@ -631,8 +653,8 @@ impl Types { if task::is_done(fuc.offset) { continue; } - fuc.offset = to.len() as _; - to.extend(&fuc.code); + fuc.offset = 0; + used_funcs.push(func); frontier.extend(fuc.relocs.iter().map(|r| r.target)); } ty::Kind::Global(glob) => { @@ -640,18 +662,31 @@ impl Types { if task::is_done(glb.offset) { continue; } - glb.offset = to.len() as _; - to.extend(&glb.data); + glb.offset = 0; + used_globals.push(glob); } _ => unreachable!(), } } - for fuc in &self.funcs { - if !task::is_done(fuc.offset) { - continue; - } + for &func in &used_funcs { + let fuc = &mut self.funcs[func as usize]; + fuc.offset = to.len() as _; + to.extend(&fuc.code); + } + let code_length = to.len(); + + for &global in &used_globals { + let global = &mut self.globals[global as usize]; + global.offset = to.len() as _; + to.extend(&global.data); + } + + let data_length = to.len() - code_length; + + for func in used_funcs { + let fuc = &self.funcs[func as usize]; for rel in &fuc.relocs { let offset = match rel.target.expand() { ty::Kind::Func(fun) => self.funcs[fun as usize].offset, @@ -661,6 +696,16 @@ impl Types { rel.reloc.apply_jump(to, offset, fuc.offset); } } + + AbleOsExecutableHeader { + magic_number: [0x15, 0x91, 0xD2], + executable_version: 0, + code_length: (code_length - HEADER_SIZE) as _, + data_length: data_length as _, + debug_length: 0, + config_length: 0, + metadata_length: 0, + } } pub fn disasm( @@ -892,7 +937,10 @@ fn disasm( *binary = prev; } - '_dump: for (&off, &(name, len, kind)) in functions.iter() { + let mut ordered = functions.iter().collect::>(); + ordered.sort_unstable_by_key(|(_, (name, _, _))| name); + + '_dump: for (&off, &(name, len, kind)) in ordered { if matches!(kind, DisasmItem::Global) { continue; } @@ -1070,107 +1118,24 @@ impl TaskQueueInner { } pub fn parse_from_fs(extra_threads: usize, root: &str) -> io::Result> { - const GIT_DEPS_DIR: &str = "git-deps"; + fn resolve(path: &str, from: &str) -> Result { + let path = match Path::new(from).parent() { + Some(parent) => PathBuf::from_iter([parent, Path::new(path)]), + None => PathBuf::from(path), + }; - enum Chk<'a> { - Branch(&'a str), - Rev(&'a str), - Tag(&'a str), - } - - enum ImportPath<'a> { - Rel { path: &'a str }, - Git { link: &'a str, path: &'a str, chk: Option> }, - } - - impl<'a> TryFrom<&'a str> for ImportPath<'a> { - type Error = ParseImportError; - - fn try_from(value: &'a str) -> Result { - let (prefix, path) = value.split_once(':').unwrap_or(("", value)); - - match prefix { - "rel" | "" => Ok(Self::Rel { path }), - "git" => { - let (link, path) = - path.split_once(':').ok_or(ParseImportError::ExpectedPath)?; - let (link, params) = link.split_once('?').unwrap_or((link, "")); - let chk = params.split('&').filter_map(|s| s.split_once('=')).find_map( - |(key, value)| match key { - "branch" => Some(Chk::Branch(value)), - "rev" => Some(Chk::Rev(value)), - "tag" => Some(Chk::Tag(value)), - _ => None, - }, - ); - Ok(Self::Git { link, path, chk }) - } - _ => Err(ParseImportError::InvalidPrefix), - } - } - } - - fn preprocess_git(link: &str) -> &str { - let link = link.strip_prefix("https://").unwrap_or(link); - link.strip_suffix(".git").unwrap_or(link) - } - - impl<'a> ImportPath<'a> { - fn resolve(&self, from: &str) -> Result { - let path = match self { - Self::Rel { path } => match Path::new(from).parent() { - Some(parent) => PathBuf::from_iter([parent, Path::new(path)]), - None => PathBuf::from(path), - }, - Self::Git { path, link, .. } => { - let link = preprocess_git(link); - PathBuf::from_iter([GIT_DEPS_DIR, link, path]) - } - }; - - path.canonicalize().map_err(|source| CantLoadFile { - path, - from: PathBuf::from(from), - source, - }) - } - } - - #[derive(Debug)] - enum ParseImportError { - ExpectedPath, - InvalidPrefix, - } - - impl std::fmt::Display for ParseImportError { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - match self { - Self::ExpectedPath => "expected path".fmt(f), - Self::InvalidPrefix => "invalid prefix, expected one of rel, \ - git or none followed by colon" - .fmt(f), - } - } - } - - impl std::error::Error for ParseImportError {} - - impl From for io::Error { - fn from(e: ParseImportError) -> Self { - io::Error::new(io::ErrorKind::InvalidInput, e) - } + path.canonicalize().map_err(|source| CantLoadFile { path, source }) } #[derive(Debug)] struct CantLoadFile { path: PathBuf, - from: PathBuf, source: io::Error, } impl std::fmt::Display for CantLoadFile { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "can't load file: {} (from: {})", self.path.display(), self.from.display(),) + write!(f, "can't load file: {}", parser::display_rel_path(&self.path),) } } @@ -1186,37 +1151,22 @@ pub fn parse_from_fs(extra_threads: usize, root: &str) -> io::Result> { } } - #[derive(Debug)] - struct InvalidFileData(std::str::Utf8Error); - - impl std::fmt::Display for InvalidFileData { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "invalid file data") - } - } - - impl std::error::Error for InvalidFileData { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - Some(&self.0) - } - } - - impl From for io::Error { - fn from(e: InvalidFileData) -> Self { - io::Error::new(io::ErrorKind::InvalidData, e) - } - } - - type Task = (u32, PathBuf, Option); + type Task = (u32, PathBuf); let seen = Mutex::new(HashMap::::default()); let tasks = TaskQueue::::new(extra_threads + 1); let ast = Mutex::new(Vec::>::new()); let loader = |path: &str, from: &str| { - let path = ImportPath::try_from(path)?; + if path.starts_with("rel:") { + return Err(io::Error::new( + io::ErrorKind::Other, + "`rel:` prefix was removed and is now equivalent to no prefix (remove it)" + .to_string(), + )); + } - let physiscal_path = path.resolve(from)?; + let physiscal_path = resolve(path, from)?; let id = { let mut seen = seen.lock().unwrap(); @@ -1232,63 +1182,30 @@ pub fn parse_from_fs(extra_threads: usize, root: &str) -> io::Result> { } }; - let command = if !physiscal_path.exists() { - let ImportPath::Git { link, chk, .. } = path else { - return Err(io::Error::new( - io::ErrorKind::NotFound, - format!("can't find file: {}", physiscal_path.display()), - )); - }; + if !physiscal_path.exists() { + return Err(io::Error::new( + io::ErrorKind::NotFound, + format!("can't find file: {}", parser::display_rel_path(&physiscal_path)), + )); + } - let root = PathBuf::from_iter([GIT_DEPS_DIR, preprocess_git(link)]); - - let mut command = std::process::Command::new("git"); - command.args(["clone", "--depth", "1"]); - if let Some(chk) = chk { - command.args(match chk { - Chk::Branch(b) => ["--branch", b], - Chk::Tag(t) => ["--tag", t], - Chk::Rev(r) => ["--rev", r], - }); - } - command.arg(link).arg(root); - Some(command) - } else { - None - }; - - tasks.push((id, physiscal_path, command)); + tasks.push((id, physiscal_path)); Ok(id) }; - let execute_task = |(_, path, command): Task, buffer: &mut Vec| { - if let Some(mut command) = command { - let output = command.output()?; - if !output.status.success() { - let msg = - format!("git command failed: {}", String::from_utf8_lossy(&output.stderr)); - return Err(io::Error::new(io::ErrorKind::Other, msg)); - } - } - + let execute_task = |(_, path): Task| { let path = path.to_str().ok_or_else(|| { io::Error::new( io::ErrorKind::InvalidData, - format!("path contains invalid characters: {}", path.display()), + format!("path contains invalid characters: {}", parser::display_rel_path(&path)), ) })?; - let mut file = std::fs::File::open(path)?; - file.read_to_end(buffer)?; - let src = std::str::from_utf8(buffer).map_err(InvalidFileData)?; - Ok(Ast::new(path, src.to_owned(), &loader)) + Ok(Ast::new(path, std::fs::read_to_string(path)?, &loader)) }; let thread = || { - let mut buffer = Vec::new(); while let Some(task @ (indx, ..)) = tasks.pop() { - let res = execute_task(task, &mut buffer); - buffer.clear(); - + let res = execute_task(task); let mut ast = ast.lock().unwrap(); let len = ast.len().max(indx as usize + 1); ast.resize_with(len, || Err(io::ErrorKind::InvalidData.into())); @@ -1298,7 +1215,7 @@ pub fn parse_from_fs(extra_threads: usize, root: &str) -> io::Result> { let path = Path::new(root).canonicalize()?; seen.lock().unwrap().insert(path.clone(), 0); - tasks.push((0, path, None)); + tasks.push((0, path)); if extra_threads == 0 { thread(); @@ -1467,7 +1384,7 @@ fn test_run_vm(out: &[u8], output: &mut String) { let mut vm = unsafe { hbvm::Vm::<_, { 1024 * 100 }>::new( LoggedMem::default(), - hbvm::mem::Address::new(out.as_ptr() as u64), + hbvm::mem::Address::new(out.as_ptr() as u64).wrapping_add(HEADER_SIZE), ) }; @@ -1506,7 +1423,7 @@ fn test_run_vm(out: &[u8], output: &mut String) { } }; - writeln!(output, "code size: {}", out.len()).unwrap(); + writeln!(output, "code size: {}", out.len() - HEADER_SIZE).unwrap(); writeln!(output, "ret: {:?}", vm.read_reg(1).0).unwrap(); writeln!(output, "status: {:?}", stat).unwrap(); } diff --git a/hblang/src/parser.rs b/hblang/src/parser.rs index 8a192bdb..82677d8e 100644 --- a/hblang/src/parser.rs +++ b/hblang/src/parser.rs @@ -5,8 +5,10 @@ use { }, std::{ cell::{Cell, UnsafeCell}, + ffi::OsStr, fmt, io, ops::{Deref, Not}, + path::PathBuf, ptr::NonNull, sync::atomic::AtomicUsize, }, @@ -95,20 +97,20 @@ impl<'a, 'b> Parser<'a, 'b> { let f = self.collect_list(TokenKind::Semi, TokenKind::Eof, |s| s.expr_low(true)); self.pop_scope(0); - let has_undeclared = !self.idents.is_empty(); + let mut errors = String::new(); for id in self.idents.drain(..) { - let (line, col) = self.lexer.line_col(ident::pos(id.ident)); - eprintln!( - "{}:{}:{} => undeclared identifier: {}", + report_to( + self.lexer.source(), self.path, - line, - col, - self.lexer.slice(ident::range(id.ident)) + id.ident, + format_args!("undeclared identifier: {}", self.lexer.slice(ident::range(id.ident))), + &mut errors, ); } - if has_undeclared { + if !errors.is_empty() { // TODO: we need error recovery + eprintln!("{errors}"); unreachable!(); } @@ -193,7 +195,7 @@ impl<'a, 'b> Parser<'a, 'b> { self.declare_rec(value, top_level) } } - _ => self.report_pos(expr.pos(), "cant declare this shit (yet)"), + _ => self.report(expr.pos(), "cant declare this shit (yet)"), } } @@ -201,7 +203,7 @@ impl<'a, 'b> Parser<'a, 'b> { if let Some(index) = index_to_check && index != 0 { - self.report_pos( + self.report( pos, format_args!( "out of order declaration not allowed: {}", @@ -212,7 +214,7 @@ impl<'a, 'b> Parser<'a, 'b> { let index = self.idents.binary_search_by_key(&id, |s| s.ident).expect("fck up"); if std::mem::replace(&mut self.idents[index].declared, true) { - self.report_pos( + self.report( pos, format_args!("redeclaration of identifier: {}", self.lexer.slice(ident::range(id))), ) @@ -276,7 +278,9 @@ impl<'a, 'b> Parser<'a, 'b> { path: self.arena.alloc_str(path), id: match (self.loader)(path, self.path) { Ok(id) => id, - Err(e) => self.report(format_args!("error loading dependency: {e:#}")), + Err(e) => { + self.report(str.start, format_args!("error loading dependency: {e:#}")) + } }, } } @@ -408,7 +412,7 @@ impl<'a, 'b> Parser<'a, 'b> { pos, value: match u64::from_str_radix(slice, radix as u32) { Ok(value) => value, - Err(e) => self.report(format_args!("invalid number: {e}")), + Err(e) => self.report(token.start, format_args!("invalid number: {e}")), } as i64, radix, } @@ -419,7 +423,7 @@ impl<'a, 'b> Parser<'a, 'b> { expr } T::Comment => Expr::Comment { pos, literal: self.move_str(token) }, - tok => self.report(format_args!("unexpected token: {tok:?}")), + tok => self.report(token.start, format_args!("unexpected token: {tok:?}")), }; loop { @@ -497,7 +501,10 @@ impl<'a, 'b> Parser<'a, 'b> { if matches!(self.token.kind, TokenKind::Ident | TokenKind::CtIdent) { self.next() } else { - self.report(format_args!("expected identifier, found {:?}", self.token.kind)) + self.report( + self.token.start, + format_args!("expected identifier, found {:?}", self.token.kind), + ) } } @@ -552,20 +559,19 @@ impl<'a, 'b> Parser<'a, 'b> { fn expect_advance(&mut self, kind: TokenKind) -> Token { if self.token.kind != kind { - self.report(format_args!("expected {:?}, found {:?}", kind, self.token.kind)); + self.report( + self.token.start, + format_args!("expected {:?}, found {:?}", kind, self.token.kind), + ); } self.next() } #[track_caller] - fn report(&self, msg: impl fmt::Display) -> ! { - self.report_pos(self.token.start, msg) - } - - #[track_caller] - fn report_pos(&self, pos: Pos, msg: impl fmt::Display) -> ! { - let (line, col) = self.lexer.line_col(pos); - eprintln!("{}:{}:{} => {}", self.path, line, col, msg); + fn report(&self, pos: Pos, msg: impl fmt::Display) -> ! { + let mut str = String::new(); + report_to(self.lexer.source(), self.path, pos, msg, &mut str); + eprintln!("{str}"); unreachable!(); } @@ -1231,19 +1237,34 @@ impl AstInner<[Symbol]> { } pub fn report_to(&self, pos: Pos, msg: impl fmt::Display, out: &mut impl fmt::Write) { - let str = &self.file; - let (line, mut col) = lexer::line_col(str.as_bytes(), pos); - _ = writeln!(out, "{}:{}:{}: {}", self.path, line, col, msg); - - let line = &str[str[..pos as usize].rfind('\n').map_or(0, |i| i + 1) - ..str[pos as usize..].find('\n').unwrap_or(str.len()) + pos as usize]; - col += line.matches('\t').count() * 3; - - _ = writeln!(out, "{}", line.replace("\t", " ")); - _ = writeln!(out, "{}^", " ".repeat(col - 1)); + report_to(&self.file, &self.path, pos, msg, out); } } +pub fn display_rel_path(path: &(impl AsRef + ?Sized)) -> std::path::Display { + static CWD: std::sync::LazyLock = + std::sync::LazyLock::new(|| std::env::current_dir().unwrap_or_default()); + std::path::Path::new(path).strip_prefix(&*CWD).unwrap_or(std::path::Path::new(path)).display() +} + +pub fn report_to( + file: &str, + path: &str, + pos: Pos, + msg: impl fmt::Display, + out: &mut impl fmt::Write, +) { + let (line, mut col) = lexer::line_col(file.as_bytes(), pos); + _ = writeln!(out, "{}:{}:{}: {}", display_rel_path(path), line, col, msg); + + let line = &file[file[..pos as usize].rfind('\n').map_or(0, |i| i + 1) + ..file[pos as usize..].find('\n').unwrap_or(file.len()) + pos as usize]; + col += line.matches('\t').count() * 3; + + _ = writeln!(out, "{}", line.replace("\t", " ")); + _ = writeln!(out, "{}^", " ".repeat(col - 1)); +} + #[derive(PartialEq, Eq, Hash)] pub struct Ast(NonNull>); diff --git a/hblang/src/son.rs b/hblang/src/son.rs index 938b6a73..51da797e 100644 --- a/hblang/src/son.rs +++ b/hblang/src/son.rs @@ -2,7 +2,7 @@ use { crate::{ ident::{self, Ident}, - instrs::{self, jal}, + instrs::{self}, lexer::{self, TokenKind}, log, parser::{ @@ -10,9 +10,12 @@ use { idfl::{self}, CommentOr, Expr, ExprRef, FileId, Pos, StructField, }, - task, ty, Field, Func, Reloc, Sig, Struct, SymKey, TypedReloc, Types, + task, + ty::{self}, + Field, Func, HashMap, Reloc, Sig, Struct, SymKey, TypedReloc, Types, }, core::fmt, + regalloc2::VReg, std::{ cell::RefCell, collections::hash_map, @@ -24,21 +27,6 @@ use { }, }; -macro_rules! node_loc { - ($self:expr, $value:expr) => { - $self.ci.colors[$self.ci.nodes[$value].color as usize - 1].loc - //$self.ci.colors[dbg!(&$self.ci.nodes[$value]).color as usize - 1].loc - }; -} - -struct Drom(&'static str); - -impl Drop for Drom { - fn drop(&mut self) { - log::inf!("{}", self.0); - } -} - const VC_SIZE: usize = 16; const INLINE_ELEMS: usize = VC_SIZE / 2 - 1; const VOID: Nid = 0; @@ -84,10 +72,6 @@ impl Vc { } } - fn find(&self, needle: Nid) -> usize { - self.iter().position(|&n| n == needle).unwrap() - } - fn len_mut(&mut self) -> &mut Nid { unsafe { if self.is_inline() { @@ -321,20 +305,6 @@ impl BitSet { self.data[data_idx] |= 1 << sub_idx; prev == 0 } - - fn unset(&mut self, idx: Nid) { - let idx = idx as usize; - let data_idx = idx / Self::ELEM_SIZE; - let sub_idx = idx % Self::ELEM_SIZE; - self.data[data_idx] &= !(1 << sub_idx); - } - - fn get(&self, idx: Nid) -> bool { - let idx = idx as usize; - let data_idx = idx / Self::ELEM_SIZE; - let sub_idx = idx % Self::ELEM_SIZE; - self.data[data_idx] & (1 << sub_idx) == 1 - } } type Nid = u16; @@ -346,70 +316,6 @@ pub mod reg { pub const RET_ADDR: Reg = 31; pub type Reg = u8; - - #[derive(Default)] - struct AllocMeta { - rc: u16, - depth: u16, - #[cfg(debug_assertions)] - allocated_at: Option, - } - - struct Metas([AllocMeta; 256]); - - impl Default for Metas { - fn default() -> Self { - Metas(std::array::from_fn(|_| AllocMeta::default())) - } - } - - #[derive(Default)] - pub struct Alloc { - meta: Metas, - free: Vec, - max_used: Reg, - } - - impl Alloc { - pub fn init(&mut self) { - self.free.clear(); - self.free.extend((32..=253).rev()); - self.max_used = RET_ADDR; - } - - pub fn allocate(&mut self, depth: u16) -> Reg { - let reg = self.free.pop().expect("TODO: we need to spill"); - self.max_used = self.max_used.max(reg); - self.meta.0[reg as usize] = AllocMeta { - depth, - rc: 1, - #[cfg(debug_assertions)] - allocated_at: Some(std::backtrace::Backtrace::capture()), - }; - reg - } - - pub fn dup(&mut self, reg: Reg) { - self.meta.0[reg as usize].rc += 1; - } - - pub fn free(&mut self, reg: Reg) { - if self.meta.0[reg as usize].rc == 1 { - self.free.push(reg); - self.meta.0[reg as usize] = Default::default(); - } else { - self.meta.0[reg as usize].rc -= 1; - } - } - - pub fn pushed_size(&self) -> usize { - ((self.max_used as usize).saturating_sub(RET_ADDR as usize) + 1) * 8 - } - - pub fn mark_leaked(&mut self, reg: u8) { - self.meta.0[reg as usize].rc = u16::MAX; - } - } } struct LookupEntry { @@ -480,7 +386,8 @@ impl Nodes { fn new_node_nop(&mut self, ty: impl Into, kind: Kind, inps: impl Into) -> Nid { let ty = ty.into(); - let node = Node { inputs: inps.into(), kind, ty, ..Default::default() }; + let node = + Node { ralloc_backref: u16::MAX, inputs: inps.into(), kind, ty, ..Default::default() }; let mut lookup_meta = None; if !node.is_lazy_phi() { @@ -784,7 +691,7 @@ impl Nodes { #[allow(clippy::format_in_format_args)] fn basic_blocks_instr(&mut self, out: &mut String, node: Nid) -> std::fmt::Result { if self[node].kind != Kind::Loop && self[node].kind != Kind::Region { - write!(out, " {node:>2}-c{:>2}: ", self[node].color)?; + write!(out, " {node:>2}-c{:>2}: ", self[node].ralloc_backref)?; } match self[node].kind { Kind::Start => unreachable!(), @@ -1064,6 +971,10 @@ impl Nodes { dominated = idom(self, dominated); } } + + fn iter_mut(&mut self) -> impl Iterator { + self.values.iter_mut().flat_map(Result::as_mut) + } } impl ops::Index for Nodes { @@ -1110,29 +1021,29 @@ pub enum Kind { impl Kind { fn is_pinned(&self) -> bool { - self.is_cfg() || matches!(self, Kind::Phi) + self.is_cfg() || matches!(self, Self::Phi) } fn is_cfg(&self) -> bool { matches!( self, - Kind::Start - | Kind::End - | Kind::Return - | Kind::Tuple { .. } - | Kind::Call { .. } - | Kind::If - | Kind::Region - | Kind::Loop + Self::Start + | Self::End + | Self::Return + | Self::Tuple { .. } + | Self::Call { .. } + | Self::If + | Self::Region + | Self::Loop ) } fn ends_basic_block(&self) -> bool { - matches!(self, Kind::Return | Kind::If | Kind::End) + matches!(self, Self::Return | Self::If | Self::End) } fn starts_basic_block(&self) -> bool { - matches!(self, Kind::Start | Kind::End | Kind::Tuple { .. } | Kind::Region | Kind::Loop) + matches!(self, Self::Start | Self::End | Self::Tuple { .. } | Self::Region | Self::Loop) } } @@ -1154,7 +1065,7 @@ struct Node { inputs: Vc, outputs: Vc, kind: Kind, - color: Color, + ralloc_backref: RallocBRef, depth: IDomDepth, lock_rc: LockRc, ty: ty::Id, @@ -1178,7 +1089,7 @@ impl Node { type Offset = u32; type Size = u32; -type Color = u16; +type RallocBRef = u16; type LoopDepth = u16; type CallCount = u16; type LockRc = u16; @@ -1215,14 +1126,12 @@ struct ItemCtx { ctrl: Nid, loop_depth: LoopDepth, - colors: Vec, call_count: u16, filled: Vec, - delayed_frees: Vec, + delayed_frees: Vec, loops: Vec, vars: Vec, - regs: reg::Alloc, ret_relocs: Vec, relocs: Vec, jump_relocs: Vec<(Nid, Reloc)>, @@ -1230,81 +1139,9 @@ struct ItemCtx { } impl ItemCtx { - fn next_color(&mut self) -> Color { - self.colors.push(ColorMeta { rc: 0, depth: self.loop_depth, loc: Default::default() }); - self.colors.len() as _ // leave out 0 (sentinel) - } - - fn set_next_color(&mut self, node: Nid) -> Color { - let color = self.next_color(); - self.set_color(node, color); - color - } - - fn set_color(&mut self, node: Nid, color: Color) { - if self.nodes[node].color == color { - return; - } - if self.nodes[node].color != 0 { - self.colors[self.nodes[node].color as usize - 1].rc -= 1; - } - self.nodes[node].color = color; - self.colors[color as usize - 1].rc += 1; - } - - fn recolor(&mut self, node: Nid, from: Color, to: Color) { - if from == to { - return; - } - - if self.nodes[node].color != from { - return; - } - - self.set_color(node, to); - - for i in 0..self.nodes[node].inputs.len() { - self.recolor(self.nodes[node].inputs[i], from, to); - } - } - - fn check_color_integrity(&self) { - let node_count = self - .nodes - .values - .iter() - .filter(|v| { - matches!( - v, - Ok(Node { - kind: Kind::BinOp { .. } - | Kind::Call { .. } - | Kind::Phi - | Kind::CInt { .. }, - color: 1.., - .. - }) - ) || matches!( - v, - Ok(Node { kind: Kind::Tuple { index: 1.. }, - color: 1.., - inputs, .. }) if inputs.first() == Some(&VOID) - ) - }) - .count(); - let color_count = self.colors.iter().map(|c| c.rc).sum::(); - debug_assert_eq!(node_count, color_count as usize); - } - fn emit(&mut self, instr: (usize, [u8; instrs::MAX_SIZE])) { crate::emit(&mut self.code, instr); } - - fn free_loc(&mut self, loc: impl Into>) { - if let Some(loc) = loc.into() { - self.regs.free(loc.reg); - } - } } fn write_reloc(doce: &mut [u8], offset: usize, value: i64, size: u16) { @@ -1364,7 +1201,7 @@ impl Codegen { pub fn generate(&mut self) { self.find_or_declare(0, 0, None, "main"); self.make_func_reachable(0); - self.complete_call_graph_low(); + self.complete_call_graph(); } fn make_func_reachable(&mut self, func: ty::Func) { @@ -1803,10 +1640,6 @@ impl Codegen { } fn complete_call_graph(&mut self) { - self.complete_call_graph_low(); - } - - fn complete_call_graph_low(&mut self) { while self.ci.task_base < self.tasks.len() && let Some(task_slot) = self.tasks.pop() { @@ -1879,53 +1712,12 @@ impl Codegen { self.ci.emit(instrs::st(reg::RET_ADDR, reg::STACK_PTR, 0, 0)); } - self.ci.regs.init(); - - self.ci.nodes.basic_blocks(); - - self.ci.nodes.visited.clear(self.ci.nodes.values.len()); - self.color_node(NEVER); - - let call_count = self.ci.call_count; - let mut parama = self.tys.parama(sig.ret); - '_color_args: { - for (var, ti) in orig_vars.iter().zip(sig.args.range()) { - let ty = self.tys.args[ti]; - let mut col = self.ci.nodes[var.value].color; - if col == 0 { - col = self.ci.set_next_color(var.value); - } - match self.tys.size_of(ty) { - 0 => continue, - 1..=8 => { - let loc = Loc { reg: parama.next() }; - let slot = &mut self.ci.colors[col as usize - 1].loc; - - if *slot != Loc::default() { - self.emit_pass_low(loc, var.value); - } else if check_no_calls(var.value, &mut self.ci.nodes) { - *slot = loc; - } else { - *slot = Loc { reg: self.ci.regs.allocate(0) }; - self.emit_pass_low(loc, var.value); - } - } - _ => todo!(), - } - } - } - + //self.ci.nodes.basic_blocks(); //self.ci.nodes.graphviz(); - #[cfg(debug_assertions)] - { - self.ci.check_color_integrity(); - } - self.ci.vars = orig_vars; - self.ci.call_count = call_count; self.ci.nodes.visited.clear(self.ci.nodes.values.len()); - self.emit_node(VOID, VOID); + let saved = self.emit_body(sig); self.ci.vars.clear(); if let Some(last_ret) = self.ci.ret_relocs.last() @@ -1947,7 +1739,7 @@ impl Codegen { } '_close_function: { - let pushed = self.ci.regs.pushed_size() as i64; + let pushed = (saved as i64 + 1) * 8; let stack = 0; write_reloc(&mut self.ci.code, 3, -(pushed + stack), 8); @@ -1963,356 +1755,133 @@ impl Codegen { self.tys.funcs[id as usize].code.append(&mut self.ci.code); self.tys.funcs[id as usize].relocs.append(&mut self.ci.relocs); self.ci.nodes.clear(); - self.ci.colors.clear(); self.ci.filled.clear(); self.pool.cis.push(std::mem::replace(&mut self.ci, prev_ci)); } - fn color_node(&mut self, ctrl: Nid) -> Option { - if !self.ci.nodes.visited.set(ctrl) { - return None; - } + fn emit_body(&mut self, sig: Sig) -> usize { + let mut nodes = std::mem::take(&mut self.ci.nodes); - let node = self.ci.nodes[ctrl].clone(); - match node.kind { - Kind::Start => None, - Kind::End => { - for &i in node.inputs.iter() { - self.color_node(i); - } - None + let func = Function::new(&mut nodes, &self.tys, sig); + let env = regalloc2::MachineEnv { + preferred_regs_by_class: [ + (1..13).map(|i| regalloc2::PReg::new(i, regalloc2::RegClass::Int)).collect(), + vec![], + vec![], + ], + non_preferred_regs_by_class: [ + (13..64).map(|i| regalloc2::PReg::new(i, regalloc2::RegClass::Int)).collect(), + vec![], + vec![], + ], + scratch_by_class: Default::default(), + fixed_stack_slots: Default::default(), + }; + let options = regalloc2::RegallocOptions { verbose_log: false, validate_ssa: true }; + let output = regalloc2::run(&func, &env, &options).unwrap_or_else(|err| panic!("{err}")); + + let mut saved_regs = HashMap::::default(); + let mut atr = |allc: regalloc2::Allocation| { + debug_assert!(allc.is_reg()); + let hvenc = regalloc2::PReg::from_index(allc.index()).hw_enc() as u8; + if hvenc <= 12 { + return hvenc; } - Kind::If => { - let &[pred, _] = node.inputs.as_slice() else { unreachable!() }; - self.color_node(pred); - None - } - Kind::Region => { - for o in node.outputs { - self.color_node(o); - } - self.color_node(node.inputs[0]); - self.color_node(node.inputs[1]); - None - } - Kind::Loop => { - self.color_node(node.inputs[1]); - for o in node.outputs { - self.color_node(o); - } - self.color_node(node.inputs[0]); - None - } - Kind::Return => { - if node.inputs[1] != VOID { - let col = self.ci.set_next_color(node.inputs[1]); - if check_no_calls(node.inputs[1], &mut self.ci.nodes) { - self.ci.colors[col as usize - 1].loc = Loc { reg: 1 }; + let would_insert = saved_regs.len() as u8 + reg::RET_ADDR + 1; + *saved_regs.entry(hvenc).or_insert(would_insert) + }; + + for (i, block) in func.blocks.iter().enumerate() { + let blk = regalloc2::Block(i as _); + func.nodes[block.nid].offset = self.ci.code.len() as _; + for instr_or_edit in output.block_insts_and_edits(&func, blk) { + let inst = match instr_or_edit { + regalloc2::InstOrEdit::Inst(inst) => inst, + regalloc2::InstOrEdit::Edit(®alloc2::Edit::Move { from, to }) => { + self.ci.emit(instrs::cp(atr(to), atr(from))); + continue; } - } + }; - self.color_node(node.inputs[0]); - None - } - Kind::CInt { .. } => { - if node.color == 0 && node.lock_rc as usize != node.outputs.len() { - self.ci.set_next_color(ctrl); - } - - None - } - Kind::Phi => { - if node.color == 0 { - self.ci.set_next_color(ctrl); - } - - fn is_tangled_phy( - target: Nid, - phi: Nid, - min_idepth: IDomDepth, - nodes: &mut Nodes, - ) -> bool { - for i in nodes[target].inputs.clone() { - if nodes.is_cfg(i) { - continue; - } - - if idepth(nodes, i) < min_idepth { - continue; - } - - if i == phi { - continue; - } - - if nodes[i].kind == Kind::Phi && nodes[i].inputs[0] == nodes[phi].inputs[0] - { - return true; - } - - if is_tangled_phy(i, phi, min_idepth, nodes) { - return true; - } - } - - false - } - - let is_region = self.ci.nodes[node.inputs[0]].kind == Kind::Region; - let is_independent_phy = is_region - || !is_tangled_phy( - ctrl, - ctrl, - idepth(&mut self.ci.nodes, node.inputs[0]), - &mut self.ci.nodes, - ); - let &[_, l, r] = node.inputs.as_slice() else { unreachable!() }; - if is_independent_phy && is_last_branch_use(l, ctrl, &mut self.ci.nodes) { - self.ci.set_color(l, self.ci.nodes[ctrl].color); - } - if is_independent_phy && is_last_branch_use(r, ctrl, &mut self.ci.nodes) { - self.ci.set_color(r, self.ci.nodes[ctrl].color); - } - - None - } - Kind::Tuple { index } => { - if (self.ci.nodes[node.inputs[0]].kind == Kind::Start && index == 0) - || (self.ci.nodes[node.inputs[0]].kind == Kind::If && index < 2) - { - for o in node.outputs { - self.color_node(o); - } - self.color_node(node.inputs[0]); - } - - None - } - Kind::BinOp { .. } => { - let can_optimize = matches!(node.kind, Kind::BinOp { op } if op.cond_op(false).is_some()) - && matches!(node.outputs.as_slice(), &[stmt] if self.ci.nodes[stmt].kind == Kind::If); - - if node.color == 0 && !can_optimize { - self.ci.set_next_color(ctrl); - } - - let &[_, lhs, rhs] = node.inputs.as_slice() else { unreachable!() }; - - if !self.ci.nodes.is_const(rhs) || can_optimize { - self.color_node(rhs); - } else { - self.ci.nodes[rhs].lock_rc += 1; - } - - self.color_node(lhs); - - None - } - Kind::UnOp { .. } => { - if node.color == 0 { - self.ci.set_next_color(ctrl); - } - self.color_node(node.inputs[1]); - None - } - Kind::Call { func } => { - if node.color == 0 { - self.ci.set_next_color(ctrl); - } - - let func = self.tys.funcs[func as usize].sig.unwrap(); - let mut parama = self.tys.parama(func.ret); - for (&i, ti) in node.inputs[1..].iter().zip(func.args.range()) { - let ty = self.tys.args[ti]; - match self.tys.size_of(ty) { - 0 => continue, - 1..=8 => { - let col = self.ci.set_next_color(i); - let loc = Loc { reg: parama.next() }; - if check_no_calls(i, &mut self.ci.nodes) { - self.ci.colors[col as usize - 1].loc = loc; - } - } - _ => todo!(), - } - } - - for o in node.outputs { - self.color_node(o); - } - - self.color_node(node.inputs[0]); - None - } - } - } - - fn emit_node(&mut self, ctrl: Nid, prev: Nid) -> Option { - if matches!(self.ci.nodes[ctrl].kind, Kind::Region | Kind::Loop) { - let node = self.ci.nodes[ctrl].clone(); - - let idx = 1 + node.inputs.iter().position(|&i| i == prev).unwrap(); - - for ph in node.outputs { - if self.ci.nodes[ph].kind != Kind::Phi { + let nid = func.instrs[inst.index()].nid; + if nid == NEVER { continue; - } - - self.emit_pass(self.ci.nodes[ph].inputs[idx], ph); - } - - match (self.ci.nodes[ctrl].kind, self.ci.nodes.visited.set(ctrl)) { - (Kind::Region, true) | (Kind::Loop, false) => { - self.ci.jump_relocs.push((ctrl, Reloc::new(self.ci.code.len(), 1, 4))); - self.ci.emit(instrs::jmp(0)); - return None; - } - _ => {} - } - } else if !self.ci.nodes.visited.set(ctrl) { - return None; - } - - let node = self.ci.nodes[ctrl].clone(); - match node.kind { - Kind::Start => self.emit_node(node.outputs[0], VOID), - Kind::End => None, - 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.ci.nodes[cond].kind - && let Some((op, swapped)) = op.cond_op(node.ty.is_signed()) - { - self.ci.nodes[cond].offset = self.ci.code.len() as _; - if swapped { - std::mem::swap(&mut then, &mut else_); - } - let &[_, lhs, rhs] = self.ci.nodes[cond].inputs.as_slice() else { - unreachable!() - }; - self.ci.emit(op(node_loc!(self, lhs).reg, node_loc!(self, rhs).reg, 0)); - } else { - todo!() - } - - self.emit_node(then, ctrl); - let jump = self.ci.code.len() as i64 - self.ci.nodes[cond].offset as i64; - self.emit_node(else_, ctrl); - write_reloc(&mut self.ci.code, self.ci.nodes[cond].offset as usize + 3, jump, 2); - - None - } - Kind::Region | Kind::Loop => { - self.ci.nodes[ctrl].offset = self.ci.code.len() as _; - for o in node.outputs.into_iter().rev() { - self.emit_node(o, ctrl); - } - None - } - Kind::Return => { - if node.inputs[1] != VOID { - let loc = Loc { reg: 1 }; - if node_loc!(self, node.inputs[1]) != loc { - self.ci.emit(instrs::cp(loc.reg, node_loc!(self, node.inputs[1]).reg)); - } - } - self.ci.ret_relocs.push(Reloc::new(self.ci.code.len(), 1, 4)); - self.ci.emit(instrs::jmp(0)); - self.emit_node(node.outputs[0], ctrl); - None - } - Kind::CInt { value } => { - if node.color != 0 { - if node_loc!(self, ctrl) == Loc::default() { - node_loc!(self, ctrl) = Loc { reg: self.ci.regs.allocate(0) }; - } - // TODO: respect size - self.ci.emit(instrs::li64(node_loc!(self, ctrl).reg, value as _)); - } - None - } - Kind::Phi => None, - Kind::Tuple { index } => { - if (self.ci.nodes[node.inputs[0]].kind == Kind::Start && index == 0) - || (self.ci.nodes[node.inputs[0]].kind == Kind::If && index < 2) - { - for o in node.outputs.into_iter().rev() { - self.emit_node(o, ctrl); - } - } else { - todo!(); - } - - None - } - Kind::BinOp { op } => { - if node.color != 0 { - let &[_, lhs, rhs] = node.inputs.as_slice() else { unreachable!() }; - - self.lazy_init(ctrl); - if self.ci.nodes[rhs].color == 0 - && let Kind::CInt { value } = self.ci.nodes[rhs].kind - && let Some(op) = - op.imm_binop(node.ty.is_signed(), self.tys.size_of(node.ty)) - { - self.ci.emit(op( - node_loc!(self, ctrl).reg, - node_loc!(self, lhs).reg, - value as _, - )); - } else if let Some(op) = - op.binop(node.ty.is_signed(), self.tys.size_of(node.ty)) - { - self.ci.emit(op( - node_loc!(self, ctrl).reg, - node_loc!(self, lhs).reg, - node_loc!(self, rhs).reg, - )); - } else { - todo!() - } - } - None - } - Kind::UnOp { op } => { - if node.color != 0 { - let op = op.unop().expect("TODO: unsupported unary operator"); - self.lazy_init(ctrl); - self.ci - .emit(op(node_loc!(self, ctrl).reg, node_loc!(self, node.inputs[1]).reg)); - } - None - } - Kind::Call { func } => { - let fuc = self.tys.funcs[func as usize].sig.unwrap(); - let mut parama = self.tys.parama(fuc.ret); - for (&i, ti) in node.inputs[1..].iter().zip(fuc.args.range()) { - let ty = self.tys.args[ti]; - match self.tys.size_of(ty) { - 0 => continue, - 1..=8 => { - let loc = Loc { reg: parama.next() }; - if node_loc!(self, i) != loc { - self.ci.emit(instrs::cp(loc.reg, node_loc!(self, i).reg)); - } + }; + let allocs = output.inst_allocs(inst); + let node = &func.nodes[nid]; + match node.kind { + Kind::Start => todo!(), + Kind::End => todo!(), + Kind::If => { + let &[_, cond] = node.inputs.as_slice() else { unreachable!() }; + if let Kind::BinOp { op } = func.nodes[cond].kind + && let Some((op, swapped)) = op.cond_op(node.ty.is_signed()) + { + let rel = Reloc::new(self.ci.code.len(), 3, 2); + self.ci.jump_relocs.push((node.outputs[!swapped as usize], rel)); + let &[lhs, rhs] = allocs else { unreachable!() }; + self.ci.emit(op(atr(lhs), atr(rhs), 0)); + } else { + todo!() } - _ => todo!(), } - } + Kind::Loop | Kind::Region => { + if node.ralloc_backref as usize != i + 1 { + let rel = Reloc::new(self.ci.code.len(), 1, 4); + self.ci.jump_relocs.push((nid, rel)); + self.ci.emit(instrs::jmp(0)); + } + } + Kind::Return => { + if i != func.blocks.len() - 1 { + let rel = Reloc::new(self.ci.code.len(), 1, 4); + self.ci.ret_relocs.push(rel); + self.ci.emit(instrs::jmp(0)); + } + } + Kind::CInt { value } => { + self.ci.emit(instrs::li64(atr(allocs[0]), value as _)); + } + Kind::Phi => todo!(), + Kind::Tuple { .. } => todo!(), + Kind::UnOp { op } => { + let op = op.unop().expect("TODO: unary operator not supported"); + let &[dst, oper] = allocs else { unreachable!() }; + self.ci.emit(op(atr(dst), atr(oper))); + } + Kind::BinOp { op } => { + let &[.., rhs] = node.inputs.as_slice() else { unreachable!() }; - let reloc = Reloc::new(self.ci.code.len(), 3, 4); - self.ci.relocs.push(TypedReloc { target: ty::Kind::Func(func).compress(), reloc }); - self.ci.emit(jal(reg::RET_ADDR, reg::ZERO, 0)); - self.emit_pass_low(Loc { reg: 1 }, ctrl); - for o in node.outputs.into_iter().rev() { - if self.ci.nodes[o].inputs[0] == ctrl { - self.emit_node(o, ctrl); + if let Kind::CInt { value } = func.nodes[rhs].kind + && let Some(op) = + op.imm_binop(node.ty.is_signed(), func.tys.size_of(node.ty)) + { + let &[dst, lhs] = allocs else { unreachable!() }; + self.ci.emit(op(atr(dst), atr(lhs), value as _)); + } else if let Some(op) = + op.binop(node.ty.is_signed(), func.tys.size_of(node.ty)) + { + let &[dst, lhs, rhs] = allocs else { unreachable!() }; + self.ci.emit(op(atr(dst), atr(lhs), atr(rhs))); + } else if op.cond_op(node.ty.is_signed()).is_some() { + } else { + todo!() + } + } + Kind::Call { func } => { + self.ci.relocs.push(TypedReloc { + target: ty::Kind::Func(func).compress(), + reloc: Reloc::new(self.ci.code.len(), 3, 4), + }); + self.ci.emit(instrs::jal(reg::RET_ADDR, reg::ZERO, 0)); } } - None } } + + self.ci.nodes = nodes; + + saved_regs.len() } // TODO: sometimes its better to do this in bulk @@ -2501,35 +2070,6 @@ impl Codegen { ty::Tuple::new(sp, len) } - fn emit_pass(&mut self, src: Nid, dst: Nid) { - if self.ci.nodes[src].color == self.ci.nodes[dst].color { - return; - } - self.emit_pass_low(node_loc!(self, src), dst); - } - - fn emit_pass_low(&mut self, src: Loc, dst: Nid) { - let loc = &mut node_loc!(self, dst); - if src != *loc { - if *loc == Loc::default() { - let reg = self.ci.regs.allocate(0); - *loc = Loc { reg }; - } - let inst = instrs::cp(loc.reg, src.reg); - self.ci.emit(inst); - } - } - - fn lazy_init(&mut self, expr: Nid) -> bool { - let loc = &mut node_loc!(self, expr); - if *loc == Loc::default() { - let reg = self.ci.regs.allocate(0); - *loc = Loc { reg }; - return true; - } - false - } - fn fatal_report(&self, pos: Pos, msg: impl Display) -> ! { self.report(pos, msg); eprintln!("{}", self.errors.borrow()); @@ -2545,6 +2085,392 @@ impl Codegen { } } +// FIXME: make this more efficient (allocated with arena) + +#[derive(Debug)] +struct Block { + nid: Nid, + preds: Vec, + succs: Vec, + instrs: regalloc2::InstRange, + params: Vec, + branch_blockparams: Vec, +} + +#[derive(Debug)] +struct Instr { + nid: Nid, + ops: Vec, +} + +struct Function<'a> { + sig: Sig, + nodes: &'a mut Nodes, + tys: &'a Types, + blocks: Vec, + instrs: Vec, +} + +impl Debug for Function<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for (i, block) in self.blocks.iter().enumerate() { + writeln!(f, "sb{i}{:?}-{:?}:", block.params, block.preds)?; + + for inst in block.instrs.iter() { + let instr = &self.instrs[inst.index()]; + writeln!(f, "{}: i{:?}:{:?}", inst.index(), self.nodes[instr.nid].kind, instr.ops)?; + } + + writeln!(f, "eb{i}{:?}-{:?}:", block.branch_blockparams, block.succs)?; + } + Ok(()) + } +} + +impl<'a> Function<'a> { + fn new(nodes: &'a mut Nodes, tys: &'a Types, sig: Sig) -> Self { + let mut s = + Self { nodes, tys, sig, blocks: Default::default(), instrs: Default::default() }; + s.nodes.visited.clear(s.nodes.values.len()); + s.emit_node(VOID, VOID); + s.add_block(0); + s.blocks.pop(); + s + } + + fn add_block(&mut self, nid: Nid) -> RallocBRef { + if let Some(prev) = self.blocks.last_mut() { + prev.instrs = regalloc2::InstRange::new( + prev.instrs.first(), + regalloc2::Inst::new(self.instrs.len()), + ); + } + + self.blocks.push(Block { + nid, + preds: Default::default(), + succs: Default::default(), + instrs: regalloc2::InstRange::new( + regalloc2::Inst::new(self.instrs.len()), + regalloc2::Inst::new(self.instrs.len() + 1), + ), + params: Default::default(), + branch_blockparams: Default::default(), + }); + self.blocks.len() as RallocBRef - 1 + } + + fn add_instr(&mut self, nid: Nid, ops: Vec) { + self.instrs.push(Instr { nid, ops }); + } + + fn urg(&mut self, nid: Nid) -> regalloc2::Operand { + regalloc2::Operand::reg_use(self.rg(nid)) + } + + fn def_nid(&mut self, _nid: Nid) {} + + fn drg(&mut self, nid: Nid) -> regalloc2::Operand { + self.def_nid(nid); + regalloc2::Operand::reg_def(self.rg(nid)) + } + + fn rg(&self, nid: Nid) -> VReg { + regalloc2::VReg::new(nid as _, regalloc2::RegClass::Int) + } + + fn emit_node(&mut self, nid: Nid, prev: Nid) { + if matches!(self.nodes[nid].kind, Kind::Region | Kind::Loop) { + let prev_bref = self.nodes[prev].ralloc_backref; + let node = self.nodes[nid].clone(); + + let idx = 1 + node.inputs.iter().position(|&i| i == prev).unwrap(); + + for ph in node.outputs { + if self.nodes[ph].kind != Kind::Phi { + continue; + } + + let rg = self.rg(self.nodes[ph].inputs[idx]); + self.blocks[prev_bref as usize].branch_blockparams.push(rg); + } + + self.add_instr(nid, vec![]); + + match (self.nodes[nid].kind, self.nodes.visited.set(nid)) { + (Kind::Loop, false) => { + for i in node.inputs { + self.bridge(i, nid); + } + return; + } + (Kind::Region, true) => return, + _ => {} + } + } else if !self.nodes.visited.set(nid) { + return; + } + + let node = self.nodes[nid].clone(); + match node.kind { + Kind::Start => self.emit_node(node.outputs[0], VOID), + Kind::End => {} + Kind::If => { + self.nodes[nid].ralloc_backref = self.nodes[prev].ralloc_backref; + + 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.is_signed()) + { + if swapped { + std::mem::swap(&mut then, &mut else_); + } + let &[_, lhs, rhs] = self.nodes[cond].inputs.as_slice() else { unreachable!() }; + let ops = vec![self.urg(lhs), self.urg(rhs)]; + self.add_instr(nid, ops); + } else { + todo!() + } + + self.emit_node(then, nid); + self.emit_node(else_, nid); + } + Kind::Region | Kind::Loop => { + self.nodes[nid].ralloc_backref = self.add_block(nid); + if node.kind == Kind::Region { + for i in node.inputs { + self.bridge(i, nid); + } + } + let mut block = vec![]; + for ph in node.outputs.clone() { + if self.nodes[ph].kind != Kind::Phi { + continue; + } + self.def_nid(ph); + block.push(self.rg(ph)); + } + self.blocks[self.nodes[nid].ralloc_backref as usize].params = block; + for o in node.outputs.into_iter().rev() { + self.emit_node(o, nid); + } + } + Kind::Return => { + let ops = if node.inputs[1] != VOID { + vec![regalloc2::Operand::reg_fixed_use( + self.rg(node.inputs[1]), + regalloc2::PReg::new(1, regalloc2::RegClass::Int), + )] + } else { + vec![] + }; + self.add_instr(nid, ops); + self.emit_node(node.outputs[0], nid); + } + Kind::CInt { .. } => { + let ops = vec![self.drg(nid)]; + self.add_instr(nid, ops); + } + Kind::Phi => {} + Kind::Tuple { index } => { + let is_start = self.nodes[node.inputs[0]].kind == Kind::Start && index == 0; + if is_start || (self.nodes[node.inputs[0]].kind == Kind::If && index < 2) { + self.nodes[nid].ralloc_backref = self.add_block(nid); + self.bridge(prev, nid); + + if is_start { + let mut parama = self.tys.parama(self.sig.ret); + for (arg, ti) in self.nodes[VOID] + .clone() + .outputs + .into_iter() + .skip(1) + .zip(self.sig.args.range()) + { + let ty = self.tys.args[ti]; + match self.tys.size_of(ty) { + 0 => continue, + 1..=8 => { + self.def_nid(arg); + self.add_instr(NEVER, vec![regalloc2::Operand::reg_fixed_def( + self.rg(arg), + regalloc2::PReg::new( + parama.next() as _, + regalloc2::RegClass::Int, + ), + )]); + } + _ => todo!(), + } + } + } + + for o in node.outputs.into_iter().rev() { + self.emit_node(o, nid); + } + } else { + todo!(); + } + } + Kind::BinOp { op } => { + let &[_, lhs, rhs] = node.inputs.as_slice() else { unreachable!() }; + + let ops = if let Kind::CInt { .. } = self.nodes[rhs].kind + && op.imm_binop(node.ty.is_signed(), 8).is_some() + { + vec![self.drg(nid), self.urg(lhs)] + } else if op.binop(node.ty.is_signed(), 8).is_some() { + vec![self.drg(nid), self.urg(lhs), self.urg(rhs)] + } else if op.cond_op(node.ty.is_signed()).is_some() { + return; + } else { + todo!("{op}") + }; + self.add_instr(nid, ops); + } + Kind::UnOp { .. } => { + let ops = vec![self.drg(nid), self.urg(node.inputs[1])]; + self.add_instr(nid, ops); + } + Kind::Call { func } => { + self.nodes[nid].ralloc_backref = self.nodes[prev].ralloc_backref; + let mut ops = vec![]; + + let fuc = self.tys.funcs[func as usize].sig.unwrap(); + if self.tys.size_of(fuc.ret) != 0 { + self.def_nid(nid); + ops.push(regalloc2::Operand::reg_fixed_def( + self.rg(nid), + regalloc2::PReg::new(1, regalloc2::RegClass::Int), + )); + } + + let mut parama = self.tys.parama(fuc.ret); + for (&i, ti) in node.inputs[1..].iter().zip(fuc.args.range()) { + let ty = self.tys.args[ti]; + match self.tys.size_of(ty) { + 0 => continue, + 1..=8 => { + ops.push(regalloc2::Operand::reg_fixed_use( + self.rg(i), + regalloc2::PReg::new(parama.next() as _, regalloc2::RegClass::Int), + )); + } + _ => todo!(), + } + } + + self.add_instr(nid, ops); + + for o in node.outputs.into_iter().rev() { + if self.nodes[o].inputs[0] == nid { + self.emit_node(o, nid); + } + } + } + } + } + + fn bridge(&mut self, pred: u16, succ: u16) { + if self.nodes[pred].ralloc_backref == u16::MAX + || self.nodes[succ].ralloc_backref == u16::MAX + { + return; + } + self.blocks[self.nodes[pred].ralloc_backref as usize] + .succs + .push(regalloc2::Block::new(self.nodes[succ].ralloc_backref as usize)); + self.blocks[self.nodes[succ].ralloc_backref as usize] + .preds + .push(regalloc2::Block::new(self.nodes[pred].ralloc_backref as usize)); + } +} + +impl<'a> regalloc2::Function for Function<'a> { + fn num_insts(&self) -> usize { + self.instrs.len() + } + + fn num_blocks(&self) -> usize { + self.blocks.len() + } + + fn entry_block(&self) -> regalloc2::Block { + regalloc2::Block(0) + } + + fn block_insns(&self, block: regalloc2::Block) -> regalloc2::InstRange { + self.blocks[block.index()].instrs + } + + fn block_succs(&self, block: regalloc2::Block) -> impl Iterator { + self.blocks[block.index()].succs.iter().copied() + } + + fn block_preds(&self, block: regalloc2::Block) -> impl Iterator { + self.blocks[block.index()].preds.iter().copied() + } + + fn block_params(&self, block: regalloc2::Block) -> impl Iterator { + self.blocks[block.index()].params.iter().copied() + } + + fn is_ret(&self, insn: regalloc2::Inst) -> bool { + self.nodes[self.instrs[insn.index()].nid].kind == Kind::Return + } + + fn is_branch(&self, insn: regalloc2::Inst) -> bool { + matches!( + self.nodes[self.instrs[insn.index()].nid].kind, + Kind::If | Kind::Tuple { .. } | Kind::Loop | Kind::Region + ) + } + + fn branch_blockparams( + &self, + block: regalloc2::Block, + _insn: regalloc2::Inst, + _succ_idx: usize, + ) -> impl Iterator { + debug_assert!( + self.blocks[block.index()].succs.len() == 1 + || self.blocks[block.index()].branch_blockparams.is_empty() + ); + + self.blocks[block.index()].branch_blockparams.iter().copied() + } + + fn inst_operands(&self, insn: regalloc2::Inst) -> impl Iterator { + self.instrs[insn.index()].ops.iter().copied() + } + + fn inst_clobbers(&self, insn: regalloc2::Inst) -> regalloc2::PRegSet { + if matches!(self.nodes[self.instrs[insn.index()].nid].kind, Kind::Call { .. }) { + let mut set = regalloc2::PRegSet::default(); + for i in 2..12 { + set.add(regalloc2::PReg::new(i, regalloc2::RegClass::Int)); + } + set + } else { + regalloc2::PRegSet::default() + } + } + + fn num_vregs(&self) -> usize { + self.nodes.values.len() + } + + fn spillslot_size(&self, regclass: regalloc2::RegClass) -> usize { + match regclass { + regalloc2::RegClass::Int => 1, + regalloc2::RegClass::Float => unreachable!(), + regalloc2::RegClass::Vector => unreachable!(), + } + } +} + fn loop_depth(target: Nid, nodes: &mut Nodes) -> LoopDepth { if nodes[target].loop_depth != 0 { return nodes[target].loop_depth; @@ -2777,106 +2703,6 @@ fn common_dom(mut a: Nid, mut b: Nid, nodes: &mut Nodes) -> Nid { a } -fn scan_idom( - nodes: &mut Nodes, - target: Nid, - pred: &mut impl FnMut(&mut Nodes, Nid) -> bool, -) -> Option { - if !pred(nodes, target) { - return None; - } - Some(match nodes[target].kind { - Kind::Start => VOID, - Kind::End => unreachable!(), - Kind::Loop - | Kind::CInt { .. } - | Kind::BinOp { .. } - | Kind::UnOp { .. } - | Kind::Call { .. } - | Kind::Phi - | Kind::Tuple { .. } - | Kind::Return - | Kind::If => nodes[target].inputs[0], - Kind::Region => { - let &[mut a, mut b] = nodes[target].inputs.as_slice() else { unreachable!() }; - while a != b { - let [ldepth, rdepth] = [idepth(nodes, a), idepth(nodes, b)]; - if ldepth >= rdepth { - a = scan_idom(nodes, a, pred)?; - } - if ldepth <= rdepth { - b = scan_idom(nodes, b, pred)?; - } - } - a - } - }) -} - -fn walk_use_doms( - target: Nid, - nodes: &mut Nodes, - mut all: impl FnMut(&mut Nodes, Nid) -> bool, -) -> bool { - let dom = - if matches!(nodes[target].kind, Kind::Call { .. }) { target } else { idom(nodes, target) }; - for mut out in nodes[target].outputs.clone().into_iter().skip(1) { - out = use_block(target, out, nodes); - while out != dom { - debug_assert!(idepth(nodes, out) > idepth(nodes, dom),); - debug_assert_ne!(out, VOID); - out = match scan_idom(nodes, out, &mut all) { - Some(next) => next, - None => return false, - }; - } - } - - true -} - -fn check_no_calls(target: Nid, nodes: &mut Nodes) -> bool { - walk_use_doms(target, nodes, |nd, ud| !matches!(nd[ud].kind, Kind::Call { .. } | Kind::Loop)) -} - -fn is_last_branch_use(target: Nid, us: Nid, nodes: &mut Nodes) -> bool { - if nodes[target].outputs.len() == 1 { - return true; - } - - let usdom = if matches!(nodes[us].kind, Kind::Call { .. }) { us } else { idom(nodes, us) }; - let tadom = - if matches!(nodes[target].kind, Kind::Call { .. }) { target } else { idom(nodes, target) }; - if loop_depth(usdom, nodes) > loop_depth(tadom, nodes) { - return false; - } - - let outputs = nodes[target].outputs.clone(); - 'o: for o in outputs { - if o == us || idepth(nodes, o) < idepth(nodes, us) { - continue; - } - - let mut odom = if matches!(nodes[o].kind, Kind::Call { .. }) { o } else { idom(nodes, o) }; - - if odom == usdom { - return nodes[usdom].outputs.find(us) < nodes[odom].outputs.find(o); - } - - while odom != usdom { - odom = idom(nodes, odom); - if idepth(nodes, odom) < idepth(nodes, us) { - continue 'o; - } - debug_assert_ne!(odom, VOID); - } - - return false; - } - - true -} - #[cfg(test)] mod tests { use std::fmt::Write; @@ -2908,7 +2734,7 @@ mod tests { return; } - println!("{output}"); + //println!("{output}"); crate::test_run_vm(&out, output); } diff --git a/hblang/tests/codegen_tests_comments.txt b/hblang/tests/codegen_tests_comments.txt index 74809d0c..33f3145a 100644 --- a/hblang/tests/codegen_tests_comments.txt +++ b/hblang/tests/codegen_tests_comments.txt @@ -1,3 +1,9 @@ +foo: + ADDI64 r254, r254, -8d + ST r31, r254, 0a, 8h + LD r31, r254, 0a, 8h + ADDI64 r254, r254, 8d + JALA r0, r31, 0a main: ADDI64 r254, r254, -8d ST r31, r254, 0a, 8h @@ -6,12 +12,6 @@ main: LD r31, r254, 0a, 8h ADDI64 r254, r254, 8d JALA r0, r31, 0a -foo: - ADDI64 r254, r254, -8d - ST r31, r254, 0a, 8h - LD r31, r254, 0a, 8h - ADDI64 r254, r254, 8d - JALA r0, r31, 0a code size: 143 ret: 0 status: Ok(()) diff --git a/hblang/tests/codegen_tests_functions.txt b/hblang/tests/codegen_tests_functions.txt index 524845dc..49b789d8 100644 --- a/hblang/tests/codegen_tests_functions.txt +++ b/hblang/tests/codegen_tests_functions.txt @@ -1,3 +1,19 @@ +add_one: + ADDI64 r254, r254, -16d + ST r31, r254, 0a, 16h + CP r32, r2 + ADDI64 r1, r32, 1d + LD r31, r254, 0a, 16h + ADDI64 r254, r254, 16d + JALA r0, r31, 0a +add_two: + ADDI64 r254, r254, -16d + ST r31, r254, 0a, 16h + CP r32, r2 + ADDI64 r1, r32, 2d + LD r31, r254, 0a, 16h + ADDI64 r254, r254, 16d + JALA r0, r31, 0a main: ADDI64 r254, r254, -24d ST r31, r254, 0a, 24h @@ -11,22 +27,6 @@ main: LD r31, r254, 0a, 24h ADDI64 r254, r254, 24d JALA r0, r31, 0a -add_two: - ADDI64 r254, r254, -16d - ST r31, r254, 0a, 16h - CP r32, r2 - ADDI64 r1, r32, 2d - LD r31, r254, 0a, 16h - ADDI64 r254, r254, 16d - JALA r0, r31, 0a -add_one: - ADDI64 r254, r254, -16d - ST r31, r254, 0a, 16h - CP r32, r2 - ADDI64 r1, r32, 1d - LD r31, r254, 0a, 16h - ADDI64 r254, r254, 16d - JALA r0, r31, 0a code size: 257 ret: 33 status: Ok(()) diff --git a/hblang/tests/codegen_tests_generic_functions.txt b/hblang/tests/codegen_tests_generic_functions.txt index 69eb94f4..0997f123 100644 --- a/hblang/tests/codegen_tests_generic_functions.txt +++ b/hblang/tests/codegen_tests_generic_functions.txt @@ -1,20 +1,3 @@ -main: - ADDI64 r254, r254, -32d - ST r31, r254, 0a, 32h - LI64 r2, 2d - LI64 r3, 2d - JAL r31, r0, :add - CP r32, r1 - LI64 r2, 1d - LI64 r3, 3d - JAL r31, r0, :add - CP r33, r1 - CP r34, r32 - SXT32 r34, r34 - SUB64 r1, r34, r33 - LD r31, r254, 0a, 32h - ADDI64 r254, r254, 32d - JALA r0, r31, 0a add: ADDI64 r254, r254, -24d ST r31, r254, 0a, 24h @@ -33,6 +16,23 @@ add: LD r31, r254, 0a, 24h ADDI64 r254, r254, 24d JALA r0, r31, 0a +main: + ADDI64 r254, r254, -32d + ST r31, r254, 0a, 32h + LI64 r2, 2d + LI64 r3, 2d + JAL r31, r0, :add + CP r32, r1 + LI64 r2, 1d + LI64 r3, 3d + JAL r31, r0, :add + CP r33, r1 + CP r34, r32 + SXT32 r34, r34 + SUB64 r1, r34, r33 + LD r31, r254, 0a, 32h + ADDI64 r254, r254, 32d + JALA r0, r31, 0a code size: 275 ret: 0 status: Ok(()) diff --git a/hblang/tests/codegen_tests_generic_types.txt b/hblang/tests/codegen_tests_generic_types.txt index 1cec9c81..b15058dc 100644 --- a/hblang/tests/codegen_tests_generic_types.txt +++ b/hblang/tests/codegen_tests_generic_types.txt @@ -1,3 +1,33 @@ +deinit: + ADDI64 r254, r254, -24d + ST r31, r254, 0a, 24h + CP r32, r2 + LD r2, r32, 0a, 8h + LD r33, r32, 16a, 8h + MULI64 r33, r33, 8d + CP r3, r33 + LI64 r4, 8d + JAL r31, r0, :free + CP r1, r32 + JAL r31, r0, :new + LD r31, r254, 0a, 24h + ADDI64 r254, r254, 24d + JALA r0, r31, 0a +free: + ADDI64 r254, r254, -40d + ST r31, r254, 0a, 40h + CP r32, r2 + CP r33, r3 + CP r34, r4 + LRA r35, r0, :FREE_SYS_CALL + LD r2, r35, 0a, 8h + CP r3, r32 + CP r4, r33 + CP r5, r34 + ECA + LD r31, r254, 0a, 40h + ADDI64 r254, r254, 40d + JALA r0, r31, 0a main: ADDI64 r254, r254, -48d ST r31, r254, 24a, 24h @@ -17,20 +47,18 @@ main: LD r31, r254, 24a, 24h ADDI64 r254, r254, 48d JALA r0, r31, 0a -deinit: - ADDI64 r254, r254, -24d - ST r31, r254, 0a, 24h +malloc: + ADDI64 r254, r254, -32d + ST r31, r254, 0a, 32h CP r32, r2 - LD r2, r32, 0a, 8h - LD r33, r32, 16a, 8h - MULI64 r33, r33, 8d - CP r3, r33 - LI64 r4, 8d - JAL r31, r0, :free - CP r1, r32 - JAL r31, r0, :new - LD r31, r254, 0a, 24h - ADDI64 r254, r254, 24d + CP r33, r3 + LRA r34, r0, :MALLOC_SYS_CALL + LD r2, r34, 0a, 8h + CP r3, r32 + CP r4, r33 + ECA + LD r31, r254, 0a, 32h + ADDI64 r254, r254, 32d JALA r0, r31, 0a new: ADDI64 r254, r254, -24d @@ -45,21 +73,6 @@ new: LD r31, r254, 0a, 24h ADDI64 r254, r254, 24d JALA r0, r31, 0a -free: - ADDI64 r254, r254, -40d - ST r31, r254, 0a, 40h - CP r32, r2 - CP r33, r3 - CP r34, r4 - LRA r35, r0, :FREE_SYS_CALL - LD r2, r35, 0a, 8h - CP r3, r32 - CP r4, r33 - CP r5, r34 - ECA - LD r31, r254, 0a, 40h - ADDI64 r254, r254, 40d - JALA r0, r31, 0a push: ADDI64 r254, r254, -72d ST r31, r254, 0a, 72h @@ -124,19 +137,6 @@ push: 4: LD r31, r254, 0a, 72h ADDI64 r254, r254, 72d JALA r0, r31, 0a -malloc: - ADDI64 r254, r254, -32d - ST r31, r254, 0a, 32h - CP r32, r2 - CP r33, r3 - LRA r34, r0, :MALLOC_SYS_CALL - LD r2, r34, 0a, 8h - CP r3, r32 - CP r4, r33 - ECA - LD r31, r254, 0a, 32h - ADDI64 r254, r254, 32d - JALA r0, r31, 0a code size: 1201 ret: 69 status: Ok(()) diff --git a/hblang/tests/codegen_tests_if_statements.txt b/hblang/tests/codegen_tests_if_statements.txt index dcdada4c..b06c4674 100644 --- a/hblang/tests/codegen_tests_if_statements.txt +++ b/hblang/tests/codegen_tests_if_statements.txt @@ -1,11 +1,3 @@ -main: - ADDI64 r254, r254, -8d - ST r31, r254, 0a, 8h - LI64 r2, 10d - JAL r31, r0, :fib - LD r31, r254, 0a, 8h - ADDI64 r254, r254, 8d - JALA r0, r31, 0a fib: ADDI64 r254, r254, -24d ST r31, r254, 0a, 24h @@ -27,6 +19,14 @@ fib: 1: LD r31, r254, 0a, 24h ADDI64 r254, r254, 24d JALA r0, r31, 0a +main: + ADDI64 r254, r254, -8d + ST r31, r254, 0a, 8h + LI64 r2, 10d + JAL r31, r0, :fib + LD r31, r254, 0a, 8h + ADDI64 r254, r254, 8d + JALA r0, r31, 0a code size: 231 ret: 55 status: Ok(()) diff --git a/hblang/tests/codegen_tests_inline_test.txt b/hblang/tests/codegen_tests_inline_test.txt index 7f67cf1e..549680e6 100644 --- a/hblang/tests/codegen_tests_inline_test.txt +++ b/hblang/tests/codegen_tests_inline_test.txt @@ -1,45 +1,3 @@ -main: - ADDI64 r254, r254, -64d - ST r31, r254, 48a, 16h - LI64 r32, 0d - ST r32, r254, 0a, 8h - LI64 r32, 0d - ST r32, r254, 8a, 8h - LD r2, r254, 0a, 16h - LI64 r32, 0d - ST r32, r254, 16a, 8h - LI64 r32, 0d - ST r32, r254, 24a, 8h - LD r4, r254, 16a, 16h - LI64 r32, 0d - ST r32, r254, 32a, 8h - LI64 r32, 0d - ST r32, r254, 40a, 8h - LD r6, r254, 32a, 16h - LI64 r8, 10d - JAL r31, r0, :line - LI64 r32, 0d - ST r32, r254, 0a, 8h - LI64 r32, 0d - ST r32, r254, 8a, 8h - LD r2, r254, 0a, 16h - LI64 r32, 0d - ST r32, r254, 16a, 8h - LI64 r32, 0d - ST r32, r254, 24a, 8h - LD r4, r254, 16a, 16h - LI64 r32, 0d - ST r32, r254, 32a, 8h - LI64 r32, 0d - ST r32, r254, 40a, 8h - LD r6, r254, 32a, 16h - LI64 r8, 10d - JAL r31, r0, :rect_line - JAL r31, r0, :example - LI64 r1, 0d - LD r31, r254, 48a, 16h - ADDI64 r254, r254, 64d - JALA r0, r31, 0a example: ADDI64 r254, r254, -48d ST r31, r254, 0a, 48h @@ -103,6 +61,69 @@ integer: 1: LD r31, r254, 0a, 56h ADDI64 r254, r254, 56d JALA r0, r31, 0a +line: + ADDI64 r254, r254, -80d + ST r31, r254, 48a, 32h + ST r2, r254, 16a, 16h + ST r4, r254, 0a, 16h + ST r6, r254, 32a, 16h + CP r32, r8 + LI64 r33, 1d + JEQ r33, r0, :0 + LD r33, r254, 16a, 8h + LD r34, r254, 0a, 8h + JGTS r33, r34, :1 + JMP :1 + 1: JMP :2 + 0: LD r34, r254, 24a, 8h + LD r33, r254, 8a, 8h + JGTS r34, r33, :2 + JMP :2 + 2: LD r31, r254, 48a, 32h + ADDI64 r254, r254, 80d + JALA r0, r31, 0a +main: + ADDI64 r254, r254, -64d + ST r31, r254, 48a, 16h + LI64 r32, 0d + ST r32, r254, 0a, 8h + LI64 r32, 0d + ST r32, r254, 8a, 8h + LD r2, r254, 0a, 16h + LI64 r32, 0d + ST r32, r254, 16a, 8h + LI64 r32, 0d + ST r32, r254, 24a, 8h + LD r4, r254, 16a, 16h + LI64 r32, 0d + ST r32, r254, 32a, 8h + LI64 r32, 0d + ST r32, r254, 40a, 8h + LD r6, r254, 32a, 16h + LI64 r8, 10d + JAL r31, r0, :line + LI64 r32, 0d + ST r32, r254, 0a, 8h + LI64 r32, 0d + ST r32, r254, 8a, 8h + LD r2, r254, 0a, 16h + LI64 r32, 0d + ST r32, r254, 16a, 8h + LI64 r32, 0d + ST r32, r254, 24a, 8h + LD r4, r254, 16a, 16h + LI64 r32, 0d + ST r32, r254, 32a, 8h + LI64 r32, 0d + ST r32, r254, 40a, 8h + LD r6, r254, 32a, 16h + LI64 r8, 10d + JAL r31, r0, :rect_line + JAL r31, r0, :example + LI64 r1, 0d + LD r31, r254, 48a, 16h + ADDI64 r254, r254, 64d + JALA r0, r31, 0a rect_line: ADDI64 r254, r254, -112d ST r31, r254, 48a, 64h @@ -135,27 +156,6 @@ rect_line: 1: LD r31, r254, 48a, 64h ADDI64 r254, r254, 112d JALA r0, r31, 0a -line: - ADDI64 r254, r254, -80d - ST r31, r254, 48a, 32h - ST r2, r254, 16a, 16h - ST r4, r254, 0a, 16h - ST r6, r254, 32a, 16h - CP r32, r8 - LI64 r33, 1d - JEQ r33, r0, :0 - LD r33, r254, 16a, 8h - LD r34, r254, 0a, 8h - JGTS r33, r34, :1 - JMP :1 - 1: JMP :2 - 0: LD r34, r254, 24a, 8h - LD r33, r254, 8a, 8h - JGTS r34, r33, :2 - JMP :2 - 2: LD r31, r254, 48a, 32h - ADDI64 r254, r254, 80d - JALA r0, r31, 0a code size: 1400 ret: 0 status: Ok(()) diff --git a/hblang/tests/codegen_tests_integer_inference_issues.txt b/hblang/tests/codegen_tests_integer_inference_issues.txt index 36eb5ebc..db307838 100644 --- a/hblang/tests/codegen_tests_integer_inference_issues.txt +++ b/hblang/tests/codegen_tests_integer_inference_issues.txt @@ -1,13 +1,3 @@ -main: - ADDI64 r254, r254, -16d - ST r31, r254, 0a, 16h - LI64 r2, 0d - LI64 r3, 1000d - JAL r31, r0, :integer_range - CP r32, r1 - LD r31, r254, 0a, 16h - ADDI64 r254, r254, 16d - JALA r0, r31, 0a integer_range: ADDI64 r254, r254, -32d ST r31, r254, 0a, 32h @@ -24,6 +14,16 @@ integer_range: LD r31, r254, 0a, 32h ADDI64 r254, r254, 32d JALA r0, r31, 0a +main: + ADDI64 r254, r254, -16d + ST r31, r254, 0a, 16h + LI64 r2, 0d + LI64 r3, 1000d + JAL r31, r0, :integer_range + CP r32, r1 + LD r31, r254, 0a, 16h + ADDI64 r254, r254, 16d + JALA r0, r31, 0a code size: 210 ret: 42 status: Ok(()) diff --git a/hblang/tests/codegen_tests_loops.txt b/hblang/tests/codegen_tests_loops.txt index e82279fb..670509a4 100644 --- a/hblang/tests/codegen_tests_loops.txt +++ b/hblang/tests/codegen_tests_loops.txt @@ -1,11 +1,3 @@ -main: - ADDI64 r254, r254, -8d - ST r31, r254, 0a, 8h - LI64 r2, 10d - JAL r31, r0, :fib - LD r31, r254, 0a, 8h - ADDI64 r254, r254, 8d - JALA r0, r31, 0a fib: ADDI64 r254, r254, -40d ST r31, r254, 0a, 40h @@ -25,6 +17,14 @@ fib: LD r31, r254, 0a, 40h ADDI64 r254, r254, 40d JALA r0, r31, 0a +main: + ADDI64 r254, r254, -8d + ST r31, r254, 0a, 8h + LI64 r2, 10d + JAL r31, r0, :fib + LD r31, r254, 0a, 8h + ADDI64 r254, r254, 8d + JALA r0, r31, 0a code size: 218 ret: 55 status: Ok(()) diff --git a/hblang/tests/codegen_tests_pointers.txt b/hblang/tests/codegen_tests_pointers.txt index 8ad2d01d..8cde2255 100644 --- a/hblang/tests/codegen_tests_pointers.txt +++ b/hblang/tests/codegen_tests_pointers.txt @@ -1,3 +1,10 @@ +drop: + ADDI64 r254, r254, -16d + ST r31, r254, 0a, 16h + CP r32, r2 + LD r31, r254, 0a, 16h + ADDI64 r254, r254, 16d + JALA r0, r31, 0a main: ADDI64 r254, r254, -40d ST r31, r254, 8a, 32h @@ -14,13 +21,6 @@ main: LD r31, r254, 8a, 32h ADDI64 r254, r254, 40d JALA r0, r31, 0a -drop: - ADDI64 r254, r254, -16d - ST r31, r254, 0a, 16h - CP r32, r2 - LD r31, r254, 0a, 16h - ADDI64 r254, r254, 16d - JALA r0, r31, 0a modify: ADDI64 r254, r254, -24d ST r31, r254, 0a, 24h diff --git a/hblang/tests/codegen_tests_request_page.txt b/hblang/tests/codegen_tests_request_page.txt index 7a905e55..feb1b756 100644 --- a/hblang/tests/codegen_tests_request_page.txt +++ b/hblang/tests/codegen_tests_request_page.txt @@ -1,12 +1,3 @@ -main: - ADDI64 r254, r254, -16d - ST r31, r254, 0a, 16h - LI64 r2, 400d - JAL r31, r0, :create_back_buffer - CP r32, r1 - LD r31, r254, 0a, 16h - ADDI64 r254, r254, 16d - JALA r0, r31, 0a create_back_buffer: ADDI64 r254, r254, -32d ST r31, r254, 0a, 32h @@ -38,6 +29,15 @@ create_back_buffer: 1: LD r31, r254, 0a, 32h ADDI64 r254, r254, 32d JALA r0, r31, 0a +main: + ADDI64 r254, r254, -16d + ST r31, r254, 0a, 16h + LI64 r2, 400d + JAL r31, r0, :create_back_buffer + CP r32, r1 + LD r31, r254, 0a, 16h + ADDI64 r254, r254, 16d + JALA r0, r31, 0a request_page: ADDI64 r254, r254, -32d ST r31, r254, 0a, 32h diff --git a/hblang/tests/codegen_tests_struct_patterns.txt b/hblang/tests/codegen_tests_struct_patterns.txt index d599dbe6..616df108 100644 --- a/hblang/tests/codegen_tests_struct_patterns.txt +++ b/hblang/tests/codegen_tests_struct_patterns.txt @@ -1,43 +1,3 @@ -main: - ADDI64 r254, r254, -26d - ST r31, r254, 2a, 24h - LI64 r32, 10d - ST r32, r254, 0a, 1h - LI64 r32, 10d - ST r32, r254, 1a, 1h - CP r32, r0 - LD r32, r254, 0a, 1h - CP r33, r0 - LD r33, r254, 1a, 1h - CP r2, r32 - JAL r31, r0, :fib - CP r32, r1 - CP r2, r33 - JAL r31, r0, :fib_iter - CP r33, r1 - SUB64 r1, r32, r33 - LD r31, r254, 2a, 24h - ADDI64 r254, r254, 26d - JALA r0, r31, 0a -fib_iter: - ADDI64 r254, r254, -40d - ST r31, r254, 0a, 40h - CP r32, r2 - LI64 r33, 0d - LI64 r34, 1d - 2: LI64 r35, 0d - JNE r32, r35, :0 - JMP :1 - 0: CP r35, r33 - ADD64 r35, r35, r34 - CP r33, r34 - CP r34, r35 - ADDI64 r32, r32, -1d - JMP :2 - 1: CP r1, r33 - LD r31, r254, 0a, 40h - ADDI64 r254, r254, 40d - JALA r0, r31, 0a fib: ADDI64 r254, r254, -32d ST r31, r254, 0a, 32h @@ -60,6 +20,46 @@ fib: 1: LD r31, r254, 0a, 32h ADDI64 r254, r254, 32d JALA r0, r31, 0a +fib_iter: + ADDI64 r254, r254, -40d + ST r31, r254, 0a, 40h + CP r32, r2 + LI64 r33, 0d + LI64 r34, 1d + 2: LI64 r35, 0d + JNE r32, r35, :0 + JMP :1 + 0: CP r35, r33 + ADD64 r35, r35, r34 + CP r33, r34 + CP r34, r35 + ADDI64 r32, r32, -1d + JMP :2 + 1: CP r1, r33 + LD r31, r254, 0a, 40h + ADDI64 r254, r254, 40d + JALA r0, r31, 0a +main: + ADDI64 r254, r254, -26d + ST r31, r254, 2a, 24h + LI64 r32, 10d + ST r32, r254, 0a, 1h + LI64 r32, 10d + ST r32, r254, 1a, 1h + CP r32, r0 + LD r32, r254, 0a, 1h + CP r33, r0 + LD r33, r254, 1a, 1h + CP r2, r32 + JAL r31, r0, :fib + CP r32, r1 + CP r2, r33 + JAL r31, r0, :fib_iter + CP r33, r1 + SUB64 r1, r32, r33 + LD r31, r254, 2a, 24h + ADDI64 r254, r254, 26d + JALA r0, r31, 0a code size: 452 ret: 0 status: Ok(()) diff --git a/hblang/tests/codegen_tests_struct_return_from_module_function.txt b/hblang/tests/codegen_tests_struct_return_from_module_function.txt index bec02703..db9c72b8 100644 --- a/hblang/tests/codegen_tests_struct_return_from_module_function.txt +++ b/hblang/tests/codegen_tests_struct_return_from_module_function.txt @@ -1,3 +1,16 @@ +foo: + ADDI64 r254, r254, -32d + ST r31, r254, 16a, 16h + LI64 r32, 3d + ST r32, r254, 0a, 8h + LI64 r32, 2d + ST r32, r254, 8a, 4h + LI64 r32, 2d + ST r32, r254, 12a, 4h + LD r1, r254, 0a, 16h + LD r31, r254, 16a, 16h + ADDI64 r254, r254, 32d + JALA r0, r31, 0a main: ADDI64 r254, r254, -40d ST r31, r254, 16a, 24h @@ -21,17 +34,6 @@ main: LD r31, r254, 16a, 24h ADDI64 r254, r254, 40d JALA r0, r31, 0a -foo: - ADDI64 r254, r254, -8d - ST r31, r254, 0a, 8h - LI64 r1, 3d - ANDI r2, r2, -4294967296d - ORI r2, r2, 2d - ANDI r2, r2, 4294967295d - ORI r2, r2, 8589934592d - LD r31, r254, 0a, 8h - ADDI64 r254, r254, 8d - JALA r0, r31, 0a -code size: 313 +code size: 341 ret: 0 status: Ok(()) diff --git a/hblang/tests/codegen_tests_structs.txt b/hblang/tests/codegen_tests_structs.txt index cb7be515..b1bbd5e3 100644 --- a/hblang/tests/codegen_tests_structs.txt +++ b/hblang/tests/codegen_tests_structs.txt @@ -21,16 +21,6 @@ main: 1: LD r31, r254, 24a, 24h ADDI64 r254, r254, 48d JALA r0, r31, 0a -pass: - ADDI64 r254, r254, -32d - ST r31, r254, 0a, 32h - CP r32, r2 - LD r33, r32, 0a, 8h - LD r34, r32, 8a, 8h - SUB64 r1, r33, r34 - LD r31, r254, 0a, 32h - ADDI64 r254, r254, 32d - JALA r0, r31, 0a odher_pass: ADDI64 r254, r254, -32d ST r31, r254, 0a, 32h @@ -41,6 +31,16 @@ odher_pass: LD r31, r254, 0a, 32h ADDI64 r254, r254, 32d JALA r0, r31, 0a +pass: + ADDI64 r254, r254, -32d + ST r31, r254, 0a, 32h + CP r32, r2 + LD r33, r32, 0a, 8h + LD r34, r32, 8a, 8h + SUB64 r1, r33, r34 + LD r31, r254, 0a, 32h + ADDI64 r254, r254, 32d + JALA r0, r31, 0a code size: 394 ret: 3 status: Ok(()) diff --git a/hblang/tests/codegen_tests_wide_ret.txt b/hblang/tests/codegen_tests_wide_ret.txt new file mode 100644 index 00000000..a5c0e26a --- /dev/null +++ b/hblang/tests/codegen_tests_wide_ret.txt @@ -0,0 +1,45 @@ +main: + ADDI64 r254, r254, -48d + ST r31, r254, 16a, 32h + CP r32, r3 + CP r33, r4 + JAL r31, r0, :small_struct + CP r34, r1 + LI64 r34, 0d + ST r34, r254, 8a, 1h + LI64 r34, 0d + ST r34, r254, 9a, 1h + LI64 r34, 0d + ST r34, r254, 10a, 1h + LI64 r34, 0d + ST r34, r254, 11a, 1h + LI64 r34, 0d + ST r34, r254, 12a, 1h + LI64 r34, 0d + ST r34, r254, 13a, 1h + LI64 r34, 0d + ST r34, r254, 14a, 1h + LI64 r34, 0d + ST r34, r254, 15a, 1h + LD r34, r254, 8a, 8h + ST r34, r254, 0a, 8h + ST r34, r254, 8a, 8h + LD r1, r254, 0a, 16h + LD r31, r254, 16a, 32h + ADDI64 r254, r254, 48d + JALA r0, r31, 0a +small_struct: + ADDI64 r254, r254, -20d + ST r31, r254, 4a, 16h + LI64 r32, 0d + ST r32, r254, 0a, 2h + LI64 r32, 0d + ST r32, r254, 2a, 2h + CP r1, r0 + LD r1, r254, 0a, 4h + LD r31, r254, 4a, 16h + ADDI64 r254, r254, 20d + JALA r0, r31, 0a +code size: 440 +ret: 0 +status: Ok(()) diff --git a/hblang/tests/codegen_tests_writing_into_string.txt b/hblang/tests/codegen_tests_writing_into_string.txt index 87b8955a..44d026cd 100644 --- a/hblang/tests/codegen_tests_writing_into_string.txt +++ b/hblang/tests/codegen_tests_writing_into_string.txt @@ -1,3 +1,10 @@ +inl: + ADDI64 r254, r254, -16d + ST r31, r254, 0a, 16h + LRA r32, r0, :"luhahah\0" + LD r31, r254, 0a, 16h + ADDI64 r254, r254, 16d + JALA r0, r31, 0a main: ADDI64 r254, r254, -8d ST r31, r254, 0a, 8h @@ -6,13 +13,6 @@ main: LD r31, r254, 0a, 8h ADDI64 r254, r254, 8d JALA r0, r31, 0a -inl: - ADDI64 r254, r254, -16d - ST r31, r254, 0a, 16h - LRA r32, r0, :"luhahah\0" - LD r31, r254, 0a, 16h - ADDI64 r254, r254, 16d - JALA r0, r31, 0a outl: ADDI64 r254, r254, -16d ST r31, r254, 0a, 16h