diff --git a/Cargo.lock b/Cargo.lock index c2c4475..8224e83 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,18 +2,6 @@ # 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 = "aho-corasick" version = "1.1.3" @@ -23,6 +11,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + [[package]] name = "anstream" version = "0.6.15" @@ -73,10 +67,13 @@ dependencies = [ ] [[package]] -name = "cfg-if" -version = "1.0.0" +name = "bumpalo" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +dependencies = [ + "allocator-api2", +] [[package]] name = "colorchoice" @@ -112,9 +109,6 @@ name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", -] [[package]] name = "hbbytecode" @@ -188,40 +182,16 @@ 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" +source = "git+https://github.com/jakubDoka/regalloc2?branch=reuse-allocations#4100af4e24bc2921c0931c901651a969c898f852" dependencies = [ + "allocator-api2", + "bumpalo", "hashbrown", "log", "rustc-hash", - "slice-group-by", "smallvec", ] @@ -260,47 +230,18 @@ 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 = "unicode-ident" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" - [[package]] name = "utf8parse" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - [[package]] name = "windows-sys" version = "0.52.0" @@ -377,23 +318,3 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[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 c89edf6..681f712 100644 --- a/hblang/Cargo.toml +++ b/hblang/Cargo.toml @@ -10,8 +10,7 @@ path = "src/main.rs" [dependencies] hbbytecode = { version = "0.1.0", path = "../hbbytecode" } hbvm = { path = "../hbvm", features = ["nightly"] } -regalloc2 = "0.10.2" -#regalloc2 = { path = "../../regalloc2/" }#git = "https://github.com/jakubDoka/regalloc2", branch = "reuse-allocations", features = [] } +regalloc2 = { git = "https://github.com/jakubDoka/regalloc2", branch = "reuse-allocations", features = [] } [dev-dependencies] env_logger = "0.11.5" diff --git a/hblang/README.md b/hblang/README.md index 8f6529b..cdff655 100644 --- a/hblang/README.md +++ b/hblang/README.md @@ -133,7 +133,6 @@ main := fn(): int { b := &a modify(b) drop(a) - stack_reclamation_edge_case := 0 return *b - 2 } @@ -508,226 +507,6 @@ main := fn(): int { if y == width break } } - - width += 1 - - loop { - if x < height { - //t += set_pixel(x, y, height) - x += 1 - i += 1 - } else { - x = 0 - y += 1 - if set_pixel(x, y, height) != i return 0 - if y == width break - } - } - - width += 1 - - loop { - if x < height { - //t += set_pixel(x, y, height) - x += 1 - i += 1 - } else { - x = 0 - y += 1 - if set_pixel(x, y, height) != i return 0 - if y == width break - } - } - - width += 1 - - loop { - if x < height { - //t += set_pixel(x, y, height) - x += 1 - i += 1 - } else { - x = 0 - y += 1 - if set_pixel(x, y, height) != i return 0 - if y == width break - } - } - - loop { - if x < height { - //t += set_pixel(x, y, height) - x += 1 - i += 1 - } else { - x = 0 - y += 1 - if set_pixel(x, y, height) != i return 0 - if y == width break - } - } - - width += 1 - - loop { - if x < height { - //t += set_pixel(x, y, height) - x += 1 - i += 1 - } else { - x = 0 - y += 1 - if set_pixel(x, y, height) != i return 0 - if y == width break - } - } - - width += 1 - - loop { - if x < height { - //t += set_pixel(x, y, height) - x += 1 - i += 1 - } else { - x = 0 - y += 1 - if set_pixel(x, y, height) != i return 0 - if y == width break - } - } - - width += 1 - - loop { - if x < height { - //t += set_pixel(x, y, height) - x += 1 - i += 1 - } else { - x = 0 - y += 1 - if set_pixel(x, y, height) != i return 0 - if y == width break - } - } - - loop { - if x < height { - //t += set_pixel(x, y, height) - x += 1 - i += 1 - } else { - x = 0 - y += 1 - if set_pixel(x, y, height) != i return 0 - if y == width break - } - } - - width += 1 - - loop { - if x < height { - //t += set_pixel(x, y, height) - x += 1 - i += 1 - } else { - x = 0 - y += 1 - if set_pixel(x, y, height) != i return 0 - if y == width break - } - } - - width += 1 - - loop { - if x < height { - //t += set_pixel(x, y, height) - x += 1 - i += 1 - } else { - x = 0 - y += 1 - if set_pixel(x, y, height) != i return 0 - if y == width break - } - } - - width += 1 - - loop { - if x < height { - //t += set_pixel(x, y, height) - x += 1 - i += 1 - } else { - x = 0 - y += 1 - if set_pixel(x, y, height) != i return 0 - if y == width break - } - } - - loop { - if x < height { - //t += set_pixel(x, y, height) - x += 1 - i += 1 - } else { - x = 0 - y += 1 - if set_pixel(x, y, height) != i return 0 - if y == width break - } - } - - width += 1 - - loop { - if x < height { - //t += set_pixel(x, y, height) - x += 1 - i += 1 - } else { - x = 0 - y += 1 - if set_pixel(x, y, height) != i return 0 - if y == width break - } - } - - width += 1 - - loop { - if x < height { - //t += set_pixel(x, y, height) - x += 1 - i += 1 - } else { - x = 0 - y += 1 - if set_pixel(x, y, height) != i return 0 - if y == width break - } - } - - width += 1 - - loop { - if x < height { - //t += set_pixel(x, y, height) - x += 1 - i += 1 - } else { - x = 0 - y += 1 - if set_pixel(x, y, height) != i return 0 - if y == width break - } - } - return i } ``` diff --git a/hblang/src/lib.rs b/hblang/src/lib.rs index 7d44727..938ce58 100644 --- a/hblang/src/lib.rs +++ b/hblang/src/lib.rs @@ -817,7 +817,7 @@ impl Types { match ty.expand() { ty::Kind::Ptr(_) => 8, ty::Kind::Builtin(ty::VOID) => 0, - ty::Kind::Builtin(ty::NEVER) => unreachable!(), + ty::Kind::Builtin(ty::NEVER) => 0, ty::Kind::Builtin(ty::INT | ty::UINT) => 8, ty::Kind::Builtin(ty::I32 | ty::U32 | ty::TYPE) => 4, ty::Kind::Builtin(ty::I16 | ty::U16) => 2, diff --git a/hblang/src/son.rs b/hblang/src/son.rs index 35310b0..5fe73d6 100644 --- a/hblang/src/son.rs +++ b/hblang/src/son.rs @@ -12,7 +12,7 @@ use { }, task, ty::{self}, - Field, Func, Offset, Reloc, Sig, Size, Struct, SymKey, TypedReloc, Types, + Field, Func, HashMap, Offset, Reloc, Sig, Size, Struct, SymKey, TypedReloc, Types, }, core::fmt, regalloc2::VReg, @@ -20,6 +20,7 @@ use { assert_matches::debug_assert_matches, cell::RefCell, collections::hash_map, + convert::identity, fmt::{Debug, Display, Write}, hash::{Hash as _, Hasher}, mem::{self, MaybeUninit}, @@ -32,6 +33,8 @@ const VC_SIZE: usize = 16; const INLINE_ELEMS: usize = VC_SIZE / 2 - 1; const VOID: Nid = 0; const NEVER: Nid = 1; +const ENTRY: Nid = 2; +const MEM: Nid = 3; union Vc { inline: InlineVc, @@ -702,12 +705,12 @@ impl Nodes { Kind::Call { func } => { write!(out, "call: {func} {} ", self[node].depth) } - Kind::Ctrl { index: u32::MAX } => write!(out, "ctrl: {:<5}", "entry"), - Kind::Ctrl { index: 0 } => write!(out, "ctrl: {:<5}", "then"), - Kind::Ctrl { index: 1 } => write!(out, "ctrl: {:<5}", "else"), - Kind::Stck { size } => write!(out, "stck: {size:<5}"), - Kind::Load { offset } => write!(out, "load: {offset:<5}"), - Kind::Stre { offset } => write!(out, "stre: {offset:<5}"), + Kind::Entry => write!(out, "ctrl: {:<5}", "entry"), + Kind::Then => write!(out, "ctrl: {:<5}", "then"), + Kind::Else => write!(out, "ctrl: {:<5}", "else"), + Kind::Stck => write!(out, "stck: "), + Kind::Load => write!(out, "load"), + Kind::Stre => write!(out, "stre"), _ => unreachable!(), }?; @@ -776,7 +779,7 @@ impl Nodes { Kind::Return => { node = self[node].outputs[0]; } - Kind::Ctrl { .. } => { + Kind::Then | Kind::Else | Kind::Entry => { writeln!( out, "b{node}: {} {} {:?}", @@ -806,16 +809,7 @@ impl Nodes { } node = cfg_index; } - Kind::Arg { .. } - | Kind::Stck { .. } - | Kind::Load { .. } - | Kind::Stre { .. } - | Kind::CInt { .. } - | Kind::Phi - | Kind::BinOp { .. } - | Kind::UnOp { .. } => { - unreachable!() - } + _ => unreachable!(), } } @@ -1024,9 +1018,10 @@ pub enum Kind { index: u32, }, // [ctrl] - Ctrl { - index: u32, - }, + Entry, + Mem, + Then, + Else, // [ctrl, oper] UnOp { op: lexer::TokenKind, @@ -1040,22 +1035,21 @@ pub enum Kind { func: ty::Func, }, // [ctrl] - Stck { - size: Size, - }, + Stck, + // [ctrl, memory] - Load { + Offset { offset: Offset, }, // [ctrl, memory] - Stre { - offset: Offset, - }, + Load, + // [ctrl, memory] + Stre, } impl Kind { fn is_pinned(&self) -> bool { - self.is_cfg() || matches!(self, Self::Phi) + self.is_cfg() || matches!(self, Self::Phi | Self::Mem) } fn is_cfg(&self) -> bool { @@ -1064,7 +1058,9 @@ impl Kind { Self::Start | Self::End | Self::Return - | Self::Ctrl { .. } + | Self::Entry + | Self::Then + | Self::Else | Self::Arg { .. } | Self::Call { .. } | Self::If @@ -1078,7 +1074,16 @@ impl Kind { } fn starts_basic_block(&self) -> bool { - matches!(self, Self::Start | Self::End | Self::Ctrl { .. } | Self::Region | Self::Loop) + matches!( + self, + Self::Start + | Self::End + | Self::Entry + | Self::Then + | Self::Else + | Self::Region + | Self::Loop + ) } } @@ -1086,9 +1091,9 @@ impl fmt::Display for Kind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Kind::CInt { value } => write!(f, "#{value}"), - Kind::Ctrl { index: u32::MAX } => write!(f, "ctrl[entry]"), - Kind::Ctrl { index: 0 } => write!(f, "ctrl[then]"), - Kind::Ctrl { index: 1 } => write!(f, "ctrl[else]"), + Kind::Entry => write!(f, "ctrl[entry]"), + Kind::Then => write!(f, "ctrl[then]"), + Kind::Else => write!(f, "ctrl[else]"), Kind::BinOp { op } => write!(f, "{op}"), Kind::Call { func, .. } => write!(f, "call {func}"), slf => write!(f, "{slf:?}"), @@ -1099,15 +1104,15 @@ impl fmt::Display for Kind { #[derive(Debug, Default, Clone)] //#[repr(align(64))] struct Node { + kind: Kind, inputs: Vc, outputs: Vc, - kind: Kind, + ty: ty::Id, + offset: Offset, ralloc_backref: RallocBRef, depth: IDomDepth, lock_rc: LockRc, - ty: ty::Id, loop_depth: LoopDepth, - offset: Offset, } impl Node { @@ -1149,6 +1154,13 @@ struct ColorMeta { loc: Loc, } +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +struct MemKey { + region: Nid, + offset: u32, + node: Nid, +} + #[derive(Default)] struct ItemCtx { file: FileId, @@ -1165,8 +1177,10 @@ struct ItemCtx { filled: Vec, delayed_frees: Vec, + stack_size: Size, loops: Vec, vars: Vec, + memories: Vec, ret_relocs: Vec, relocs: Vec, jump_relocs: Vec<(Nid, Reloc)>, @@ -1221,6 +1235,33 @@ struct Pool { cis: Vec, } +struct Regalloc { + env: regalloc2::MachineEnv, + ctx: regalloc2::Ctx, +} + +impl Default for Regalloc { + fn default() -> Self { + Self { + 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(), + }, + ctx: Default::default(), + } + } +} + #[derive(Default)] pub struct Codegen { pub files: Vec, @@ -1229,10 +1270,62 @@ pub struct Codegen { tys: Types, ci: ItemCtx, pool: Pool, + ralloc: Regalloc, errors: RefCell, } impl Codegen { + fn mem_op(&mut self, region: Nid, offset: Offset, kind: Kind, ty: ty::Id, mut inps: Vc) -> Nid { + let size = self.tys.size_of(ty); + let insert_start = self + .ci + .memories + .binary_search_by_key(&(region, offset), |k| (k.region, k.offset)) + .unwrap_or_else(identity); + let insert_end = self + .ci + .memories + .binary_search_by(|k| (k.region, k.offset).cmp(&(region, offset + size))) + .unwrap_or_else(identity); + + for mk in &self.ci.memories[insert_start..insert_end] { + debug_assert_eq!(mk.region, region); + debug_assert!(mk.offset >= offset); + debug_assert!(mk.offset < offset + size); + if matches!(kind, Kind::Load | Kind::Offset { .. }) + || !self.ci.nodes.unlock_remove(mk.node) + { + inps.push(mk.node); + } + } + + if insert_start == insert_end { + inps.push(region); + } + + let new_op = self.ci.nodes.new_node(ty, kind, inps); + if !matches!(kind, Kind::Offset { .. }) { + self.ci.memories.splice( + insert_start..insert_end, + std::iter::once(MemKey { node: new_op, region, offset }), + ); + self.ci.nodes.lock(new_op); + } + new_op + } + + fn store_mem(&mut self, region: Nid, offset: Offset, value: Nid) -> Nid { + self.mem_op(region, offset, Kind::Stre, self.tof(value), [VOID, value].into()) + } + + fn load_mem(&mut self, region: Nid, offset: Offset, ty: ty::Id) -> Nid { + self.mem_op(region, offset, Kind::Load, ty, [VOID].into()) + } + + fn ref_mem(&mut self, region: Nid, offset: Offset, ty: ty::Id) -> Nid { + self.mem_op(region, offset, Kind::Offset { offset }, ty, [VOID].into()) + } + pub fn generate(&mut self) { self.find_or_declare(0, 0, None, "main"); self.make_func_reachable(0); @@ -1303,7 +1396,32 @@ impl Codegen { self.ci.nodes.unlock_remove(prev); Some(VOID) } - Expr::BinOp { left, op, right } => { + Expr::BinOp { + left: &Expr::UnOp { pos, op: TokenKind::Mul, val }, + op: TokenKind::Assign, + right, + } => { + let ctx = Ctx { ty: ctx.ty.map(|ty| self.tys.make_ptr(ty)) }; + let val = self.expr_ctx(val, ctx)?; + let base = match self.tof(val).expand() { + ty::Kind::Ptr(p) => self.tys.ptrs[p as usize].base, + _ => { + self.report( + pos, + format_args!( + "the '{}' can not be dereferneced", + self.ty_display(self.tof(val)) + ), + ); + ty::NEVER.into() + } + }; + let value = self.expr(right)?; + _ = self.assert_ty(right.pos(), self.tof(value), base, true, "stored value"); + self.store_mem(val, 0, value); + Some(VOID) + } + Expr::BinOp { left, op, right } if op != TokenKind::Assign => { let lhs = self.expr_ctx(left, ctx)?; self.ci.nodes.lock(lhs); let rhs = self.expr_ctx(right, Ctx::default().with_ty(self.tof(lhs))); @@ -1319,10 +1437,46 @@ impl Codegen { let inps = [VOID, lhs, rhs]; Some(self.ci.nodes.new_node(ty::bin_ret(ty, op), Kind::BinOp { op }, inps)) } - Expr::UnOp { pos: _, op: TokenKind::Band, val: _ } => { - todo!() + Expr::UnOp { op: TokenKind::Band, val, .. } => { + let ctx = Ctx { + ty: ctx.ty.and_then(|ty| match ty.expand() { + ty::Kind::Ptr(p) => Some(self.tys.ptrs[p as usize].base), + _ => None, + }), + }; + + let mut val = self.expr_ctx(val, ctx)?; + let ty = self.tof(val); + if !matches!(self.ci.nodes[val].kind, Kind::Stck | Kind::Offset { .. }) { + let stck = self.ci.nodes.new_node_nop(ty, Kind::Stck, [VOID, MEM]); + self.ci.nodes[stck].offset = self.ci.stack_size; + self.ci.stack_size += self.tys.size_of(ty); + self.store_mem(stck, 0, val); + val = stck; + } + + let ptr = self.tys.make_ptr(ty); + Some(self.ref_mem(val, 0, ptr)) } - Expr::UnOp { pos, op, val } => { + Expr::UnOp { op: TokenKind::Mul, val, pos } => { + let ctx = Ctx { ty: ctx.ty.map(|ty| self.tys.make_ptr(ty)) }; + let val = self.expr_ctx(val, ctx)?; + let base = match self.tof(val).expand() { + ty::Kind::Ptr(p) => self.tys.ptrs[p as usize].base, + _ => { + self.report( + pos, + format_args!( + "the '{}' can not be dereferneced", + self.ty_display(self.tof(val)) + ), + ); + ty::NEVER.into() + } + }; + Some(self.load_mem(val, 0, base)) + } + Expr::UnOp { pos, op: op @ TokenKind::Sub, val } => { let val = self.expr_ctx(val, ctx)?; if !self.tof(val).is_integer() { self.report( @@ -1360,11 +1514,11 @@ impl Codegen { self.ci.nodes.lock(el.value); } - self.ci.ctrl = self.ci.nodes.new_node(ty::VOID, Kind::Ctrl { index: 0 }, [if_node]); + self.ci.ctrl = self.ci.nodes.new_node(ty::VOID, Kind::Then, [if_node]); let lcntrl = self.expr(then).map_or(Nid::MAX, |_| self.ci.ctrl); let mut then_scope = std::mem::replace(&mut self.ci.vars, else_scope); - self.ci.ctrl = self.ci.nodes.new_node(ty::VOID, Kind::Ctrl { index: 1 }, [if_node]); + self.ci.ctrl = self.ci.nodes.new_node(ty::VOID, Kind::Else, [if_node]); let rcntrl = if let Some(else_) = else_ { self.expr(else_).map_or(Nid::MAX, |_| self.ci.ctrl) } else { @@ -1568,7 +1722,10 @@ impl Codegen { VOID }; - let inps = [self.ci.ctrl, value]; + let mut inps = Vc::from([self.ci.ctrl, value]); + for m in self.ci.memories.iter() { + inps.push(m.node); + } let out = &mut String::new(); self.report_log_to(pos, "returning here", out); @@ -1718,7 +1875,10 @@ impl Codegen { debug_assert_eq!(start, VOID); let end = self.ci.nodes.new_node(ty::NEVER, Kind::End, []); debug_assert_eq!(end, NEVER); - self.ci.ctrl = self.ci.nodes.new_node(ty::VOID, Kind::Ctrl { index: u32::MAX }, [VOID]); + self.ci.ctrl = self.ci.nodes.new_node(ty::VOID, Kind::Entry, [VOID]); + debug_assert_eq!(self.ci.ctrl, ENTRY); + let mem = self.ci.nodes.new_node(ty::VOID, Kind::Mem, [VOID]); + debug_assert_eq!(mem, MEM); let Expr::BinOp { left: Expr::Ident { .. }, @@ -1745,6 +1905,9 @@ impl Codegen { self.report(body.pos(), "expected all paths in the fucntion to return"); } + for mem in self.ci.memories.drain(..) { + self.ci.nodes.unlock_remove(mem.node); + } for var in self.ci.vars.drain(..) { self.ci.nodes.unlock(var.value); } @@ -1790,7 +1953,7 @@ impl Codegen { '_close_function: { let pushed = (saved as i64 + 1) * 8; - let stack = 0; + let stack = std::mem::take(&mut self.ci.stack_size) as i64; write_reloc(&mut self.ci.code, 3, -(pushed + stack), 8); write_reloc(&mut self.ci.code, 3 + 8 + 3, stack, 8); @@ -1813,153 +1976,216 @@ impl Codegen { let mut nodes = std::mem::take(&mut self.ci.nodes); let func = Function::new(&mut nodes, &self.tys, sig); - let mut env = regalloc2::MachineEnv { - preferred_regs_by_class: [ - (1..5).map(|i| regalloc2::PReg::new(i, regalloc2::RegClass::Int)).collect(), - vec![], - vec![], - ], - non_preferred_regs_by_class: [ - (12..16).map(|i| regalloc2::PReg::new(i, regalloc2::RegClass::Int)).collect(), - vec![], - vec![], - ], - scratch_by_class: Default::default(), - fixed_stack_slots: Default::default(), - }; if self.ci.call_count != 0 { - std::mem::swap(&mut env.preferred_regs_by_class, &mut env.non_preferred_regs_by_class); + std::mem::swap( + &mut self.ralloc.env.preferred_regs_by_class, + &mut self.ralloc.env.non_preferred_regs_by_class, + ); }; - let options = regalloc2::RegallocOptions { verbose_log: false, validate_ssa: false }; - let iters = std::hint::black_box(10000); - //let mut ctx = regalloc2::Ctx::default(); - //regalloc2::run_with_ctx(&func, &env, &options, &mut ctx) - // .unwrap_or_else(|err| panic!("{err}")); - //let now = std::time::Instant::now(); - //for _ in 0..iters { - // regalloc2::run_with_ctx(&func, &env, &options, &mut ctx) - // .unwrap_or_else(|err| panic!("{err}")); - //} - //eprintln!("took: {:?}", now.elapsed().checked_div(iters).unwrap()); + let options = regalloc2::RegallocOptions { + verbose_log: false, + validate_ssa: false, + algorithm: regalloc2::Algorithm::Ion, + }; + regalloc2::run_with_ctx(&func, &self.ralloc.env, &options, &mut self.ralloc.ctx) + .unwrap_or_else(|err| panic!("{err}")); - let now = std::time::Instant::now(); - for _ in 0..iters { - regalloc2::run(&func, &env, &options).unwrap_or_else(|err| panic!("{err}")); + if self.ci.call_count != 0 { + std::mem::swap( + &mut self.ralloc.env.preferred_regs_by_class, + &mut self.ralloc.env.non_preferred_regs_by_class, + ); + }; + + 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; + } + 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 self.ralloc.ctx.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; + } + }; + + let nid = func.instrs[inst.index()].nid; + if nid == NEVER { + continue; + }; + let allocs = self.ralloc.ctx.output.inst_allocs(inst); + let node = &func.nodes[nid]; + match node.kind { + 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!() + } + } + 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::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!() }; + + 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)); + } + Kind::Stck => todo!(), + Kind::Load => { + let mut region = node.inputs[1]; + let mut offset = 0; + let size = self.tys.size_of(node.ty); + debug_assert_eq!(size, 8, "TODO"); + let (base, offset) = loop { + match func.nodes[region].kind { + Kind::Stck => { + break ( + reg::STACK_PTR, + self.ci.stack_size - func.nodes[region].offset + offset + - size, + ) + } + Kind::Offset { offset: o } => { + offset = o; + region = func.nodes[region].inputs[1] + } + Kind::Stre => region = func.nodes[region].inputs[2], + k => unreachable!("{k:?}"), + }; + }; + + let &[dst] = allocs else { unreachable!() }; + self.ci.emit(instrs::ld(atr(dst), base, offset as _, size as _)); + } + Kind::Stre => { + let mut region = node.inputs[2]; + let mut offset = 0; + let size = self.tys.size_of(node.ty); + debug_assert_eq!(size, 8, "TODO"); + let (base, offset, src) = loop { + match func.nodes[region].kind { + Kind::Stck => { + break ( + reg::STACK_PTR, + self.ci.stack_size - func.nodes[region].offset + offset + - size, + allocs[0], + ); + } + Kind::Arg { .. } => { + break (atr(allocs[0]), 0, allocs[1]); + } + Kind::Offset { offset: o } => { + offset = o; + region = func.nodes[region].inputs[1] + } + Kind::Stre => region = func.nodes[region].inputs[2], + k => unreachable!("{k:?}"), + }; + }; + + self.ci.emit(instrs::st(atr(src), base, offset as _, size as _)); + } + Kind::Offset { mut offset } => { + let mut region = node.inputs[1]; + let size = self.tys.size_of(node.ty); + debug_assert_eq!(size, 8, "TODO"); + let (base, offset) = loop { + match func.nodes[region].kind { + Kind::Stck => { + break ( + reg::STACK_PTR, + self.ci.stack_size - func.nodes[region].offset + offset + - size, + ) + } + Kind::Offset { offset: o } => { + offset = o; + region = func.nodes[region].inputs[1] + } + Kind::Stre => region = func.nodes[region].inputs[2], + k => unreachable!("{k:?}"), + }; + }; + let &[dst] = allocs else { unreachable!() }; + self.ci.emit(instrs::addi64(atr(dst), base, offset as _)); + } + _ => unreachable!(), + } + } } - eprintln!("took: {:?}", now.elapsed().checked_div(iters).unwrap()); - 0 + self.ci.nodes = nodes; - //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; - // } - // 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 ctx.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; - // } - // }; - - // let nid = func.instrs[inst.index()].nid; - // if nid == NEVER { - // continue; - // }; - // let allocs = ctx.output.inst_allocs(inst); - // let node = &func.nodes[nid]; - // match node.kind { - // 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!() - // } - // } - // 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::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!() }; - - // 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)); - // } - // Kind::Start | Kind::End | Kind::Phi | Kind::Arg { .. } | Kind::Ctrl { .. } => { - // unreachable!() - // } - // Kind::Stck { .. } => todo!(), - // Kind::Load { .. } => todo!(), - // Kind::Stre { .. } => todo!(), - // } - // } - //} - - //self.ci.nodes = nodes; - - //saved_regs.len() + saved_regs.len() } // TODO: sometimes its better to do this in bulk fn ty(&mut self, expr: &Expr) -> ty::Id { match *expr { + Expr::UnOp { op: TokenKind::Xor, val, .. } => { + let base = self.ty(val); + self.tys.make_ptr(base) + } Expr::Ident { id, .. } if ident::is_null(id) => id.into(), ref e => { self.report_unhandled_ast(e, "type"); @@ -2287,9 +2513,7 @@ impl<'a> Function<'a> { let node = self.nodes[nid].clone(); match node.kind { Kind::Start => { - debug_assert_matches!(self.nodes[node.outputs[0]].kind, Kind::Ctrl { - index: u32::MAX - }); + debug_assert_matches!(self.nodes[node.outputs[0]].kind, Kind::Entry); self.emit_node(node.outputs[0], VOID) } Kind::End => {} @@ -2360,12 +2584,12 @@ impl<'a> Function<'a> { self.add_instr(nid, ops); } } - Kind::Ctrl { index: u32::MAX } => { + Kind::Entry => { self.nodes[nid].ralloc_backref = self.add_block(nid); 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()) + self.nodes[VOID].clone().outputs.into_iter().skip(2).zip(self.sig.args.range()) { let ty = self.tys.args[ti]; match self.tys.size_of(ty) { @@ -2385,7 +2609,7 @@ impl<'a> Function<'a> { self.emit_node(o, nid); } } - Kind::Ctrl { .. } => { + Kind::Then | Kind::Else => { self.nodes[nid].ralloc_backref = self.add_block(nid); self.bridge(prev, nid); for o in node.outputs.into_iter().rev() { @@ -2448,10 +2672,46 @@ impl<'a> Function<'a> { } } } - Kind::Phi | Kind::Arg { .. } => {} - Kind::Stck { .. } => todo!(), - Kind::Load { .. } => todo!(), - Kind::Stre { .. } => todo!(), + Kind::Offset { .. } => { + let mut region = node.inputs[1]; + let ops = loop { + match self.nodes[region].kind { + Kind::Stck => break vec![self.drg(nid)], + Kind::Offset { .. } => region = self.nodes[region].inputs[1], + Kind::Stre => region = self.nodes[region].inputs[2], + k => unreachable!("{k:?}"), + }; + }; + self.add_instr(nid, ops); + } + Kind::Stck | Kind::Phi | Kind::Arg { .. } | Kind::Mem => {} + Kind::Load => { + let mut region = node.inputs[1]; + let ops = loop { + match self.nodes[region].kind { + Kind::Stck => break vec![self.drg(nid)], + Kind::Offset { .. } => region = self.nodes[region].inputs[1], + Kind::Stre => region = self.nodes[region].inputs[2], + k => unreachable!("{k:?}"), + }; + }; + + self.add_instr(nid, ops); + } + Kind::Stre => { + let mut region = node.inputs[2]; + let ops = loop { + match self.nodes[region].kind { + Kind::Stck => break vec![self.urg(node.inputs[1])], + Kind::Arg { .. } => break vec![self.urg(region), self.urg(node.inputs[1])], + Kind::Offset { .. } => region = self.nodes[region].inputs[1], + Kind::Stre => region = self.nodes[region].inputs[2], + k => unreachable!("{k:?}"), + }; + }; + + self.add_instr(nid, ops); + } } } @@ -2506,7 +2766,7 @@ impl<'a> regalloc2::Function for Function<'a> { fn is_branch(&self, insn: regalloc2::Inst) -> bool { matches!( self.nodes[self.instrs[insn.index()].nid].kind, - Kind::If | Kind::Ctrl { .. } | Kind::Loop | Kind::Region + Kind::If | Kind::Then | Kind::Else | Kind::Entry | Kind::Loop | Kind::Region ) } @@ -2531,7 +2791,7 @@ impl<'a> regalloc2::Function for Function<'a> { 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 { + for i in 2..13 { set.add(regalloc2::PReg::new(i, regalloc2::RegClass::Int)); } set @@ -2559,13 +2819,17 @@ fn loop_depth(target: Nid, nodes: &mut Nodes) -> LoopDepth { } nodes[target].loop_depth = match nodes[target].kind { - Kind::Ctrl { .. } | Kind::Call { .. } | Kind::Return | Kind::If => { - loop_depth(nodes[target].inputs[0], nodes) + Kind::Entry | Kind::Then | Kind::Else | Kind::Call { .. } | Kind::Return | Kind::If => { + let dpth = loop_depth(nodes[target].inputs[0], nodes); + if nodes[target].loop_depth != 0 { + return nodes[target].loop_depth; + } + dpth } Kind::Region => { let l = loop_depth(nodes[target].inputs[0], nodes); let r = loop_depth(nodes[target].inputs[1], nodes); - debug_assert_eq!(r, l); + debug_assert_eq!(l, r); l } Kind::Loop => { @@ -2581,11 +2845,11 @@ fn loop_depth(target: Nid, nodes: &mut Nodes) -> LoopDepth { idom(nodes, cursor) }; debug_assert_ne!(next, VOID); - if let Kind::Ctrl { index: index @ ..=1 } = nodes[cursor].kind { + if matches!(nodes[cursor].kind, Kind::Then | Kind::Else) { let other = *nodes[next] .outputs .iter() - .find(|&&n| nodes[n].kind != Kind::Ctrl { index }) + .find(|&&n| nodes[n].kind != nodes[cursor].kind) .unwrap(); if nodes[other].loop_depth == 0 { nodes[other].loop_depth = depth - 1; @@ -2599,10 +2863,6 @@ fn loop_depth(target: Nid, nodes: &mut Nodes) -> LoopDepth { _ => unreachable!(), }; - if target == 19 { - //panic!("{}", nodes[target].loop_depth); - } - nodes[target].loop_depth } diff --git a/hblang/tests/son_tests_exhaustive_loop_testing.txt b/hblang/tests/son_tests_exhaustive_loop_testing.txt index 0e1c8f1..3895fb6 100644 --- a/hblang/tests/son_tests_exhaustive_loop_testing.txt +++ b/hblang/tests/son_tests_exhaustive_loop_testing.txt @@ -20,54 +20,54 @@ continue_and_state_change: ADDI64 r254, r254, 40d JALA r0, r31, 0a main: - ADDI64 r254, r254, -72d - ST r31, r254, 0a, 72h - LI64 r12, 0d - CP r2, r12 + ADDI64 r254, r254, -80d + ST r31, r254, 0a, 80h + LI64 r32, 0d + CP r2, r32 JAL r31, r0, :multiple_breaks - CP r32, r12 - CP r33, r1 + CP r33, r32 + CP r34, r1 LI64 r1, 3d - JEQ r33, r1, :0 + JEQ r34, r1, :0 LI64 r1, 1d JMP :1 - 0: CP r34, r1 - LI64 r35, 4d - CP r2, r35 + 0: CP r35, r1 + LI64 r36, 4d + CP r2, r36 JAL r31, r0, :multiple_breaks - CP r36, r35 - LI64 r37, 10d - JEQ r1, r37, :2 + CP r37, r36 + LI64 r38, 10d + JEQ r1, r38, :2 LI64 r1, 2d JMP :1 - 2: CP r2, r32 + 2: CP r2, r33 JAL r31, r0, :state_change_in_break - CP r38, r1 - CP r1, r32 - JEQ r38, r1, :3 - CP r1, r34 + CP r39, r1 + CP r1, r33 + JEQ r39, r1, :3 + CP r1, r35 JMP :1 - 3: CP r32, r1 - CP r2, r36 + 3: CP r33, r1 + CP r2, r37 JAL r31, r0, :state_change_in_break - JEQ r1, r37, :4 - CP r1, r36 + JEQ r1, r38, :4 + CP r1, r37 JMP :1 - 4: CP r2, r37 + 4: CP r2, r38 JAL r31, r0, :continue_and_state_change - JEQ r1, r37, :5 + JEQ r1, r38, :5 LI64 r1, 5d JMP :1 - 5: CP r2, r34 + 5: CP r2, r35 JAL r31, r0, :continue_and_state_change - CP r39, r1 - CP r1, r32 - JEQ r39, r1, :6 + CP r40, r1 + CP r1, r33 + JEQ r40, r1, :6 LI64 r1, 6d JMP :1 - 6: CP r1, r32 - 1: LD r31, r254, 0a, 72h - ADDI64 r254, r254, 72d + 6: CP r1, r33 + 1: LD r31, r254, 0a, 80h + ADDI64 r254, r254, 80d JALA r0, r31, 0a multiple_breaks: ADDI64 r254, r254, -24d diff --git a/hblang/tests/son_tests_pointers.txt b/hblang/tests/son_tests_pointers.txt new file mode 100644 index 0000000..149f2b1 --- /dev/null +++ b/hblang/tests/son_tests_pointers.txt @@ -0,0 +1,31 @@ +drop: + 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, -32d + ST r31, r254, 8a, 24h + LI64 r32, 1d + ST r32, r254, 0a, 8h + ADDI64 r2, r254, 0d + JAL r31, r0, :modify + CP r2, r32 + JAL r31, r0, :drop + LD r33, r254, 0a, 8h + ADDI64 r1, r33, -2d + LD r31, r254, 8a, 24h + ADDI64 r254, r254, 32d + JALA r0, r31, 0a +modify: + ADDI64 r254, r254, -16d + ST r31, r254, 0a, 16h + LI64 r32, 2d + ST r32, r2, 0a, 8h + LD r31, r254, 0a, 16h + ADDI64 r254, r254, 16d + JALA r0, r31, 0a +code size: 283 +ret: 0 +status: Ok(()) diff --git a/hblang/text-prj/main.hb b/hblang/text-prj/main.hb deleted file mode 100644 index e652298..0000000 --- a/hblang/text-prj/main.hb +++ /dev/null @@ -1,11 +0,0 @@ -foo := 0; - -.{global, fib, Structa, create_window, WindowID} := @use("pkg.hb") - -main := fn(a: int): int { - g := global - - win := create_window() - - return fib(g + Structa.(0, 0).foo) -} diff --git a/hblang/text-prj/pkg.hb b/hblang/text-prj/pkg.hb deleted file mode 100644 index 7348fa0..0000000 --- a/hblang/text-prj/pkg.hb +++ /dev/null @@ -1,19 +0,0 @@ -global := 10 - -Structa := struct { - foo: int, - goo: int, -} - -create_window := fn(): WindowID { - return WindowID.(1, 2) -} - -WindowID := struct { - host_id: int, - window_id: int, -} - -fib := fn(n: int): int { - return n + 1 -}