diff --git a/Cargo.lock b/Cargo.lock index 805b971..a7d03f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,119 +2,11 @@ # 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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - -[[package]] -name = "anstream" -version = "0.6.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" - -[[package]] -name = "anstyle-parse" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" -dependencies = [ - "windows-sys", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" -dependencies = [ - "anstyle", - "windows-sys", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "colorchoice" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" - -[[package]] -name = "env_filter" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" -dependencies = [ - "log", - "regex", -] - -[[package]] -name = "env_logger" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" -dependencies = [ - "anstream", - "anstyle", - "env_filter", - "humantime", - "log", -] - [[package]] name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", -] [[package]] name = "hbbytecode" @@ -128,9 +20,7 @@ version = "0.1.0" name = "hblang" version = "0.1.0" dependencies = [ - "env_logger", "hbvm", - "log", "regalloc2", ] @@ -149,36 +39,12 @@ dependencies = [ "memmap2", ] -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - -[[package]] -name = "is_terminal_polyfill" -version = "1.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" - [[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 = "memchr" -version = "2.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" - [[package]] name = "memmap2" version = "0.9.5" @@ -188,212 +54,28 @@ 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#7e74b2fde4f022816cded93ab5685e46f8e3a159" dependencies = [ "hashbrown", - "log", "rustc-hash", - "slice-group-by", "smallvec", ] -[[package]] -name = "regex" -version = "1.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" - [[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 = "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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -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 5dab6dc..90a0c45 100644 --- a/hblang/Cargo.toml +++ b/hblang/Cargo.toml @@ -8,7 +8,5 @@ name = "hbc" path = "src/main.rs" [dependencies] -env_logger = "0.11.5" hbvm = { path = "../hbvm", features = ["nightly"] } -log = "0.4.22" -regalloc2 = { version = "0.10.2", features = ["trace-log"] } +regalloc2 = { git = "https://github.com/jakubDoka/regalloc2" } diff --git a/hblang/src/son.rs b/hblang/src/son.rs index c4e7d2a..51da797 100644 --- a/hblang/src/son.rs +++ b/hblang/src/son.rs @@ -1712,8 +1712,7 @@ impl Codegen { self.ci.emit(instrs::st(reg::RET_ADDR, reg::STACK_PTR, 0, 0)); } - self.ci.nodes.basic_blocks(); - + //self.ci.nodes.basic_blocks(); //self.ci.nodes.graphviz(); self.ci.vars = orig_vars; @@ -1761,412 +1760,6 @@ impl Codegen { } fn emit_body(&mut self, sig: Sig) -> usize { - // 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) -> &[regalloc2::Block] { - &self.blocks[block.index()].succs - } - - fn block_preds(&self, block: regalloc2::Block) -> &[regalloc2::Block] { - &self.blocks[block.index()].preds - } - - fn block_params(&self, block: regalloc2::Block) -> &[regalloc2::VReg] { - &self.blocks[block.index()].params - } - - 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, - ) -> &[regalloc2::VReg] { - debug_assert!( - self.blocks[block.index()].succs.len() == 1 - || self.blocks[block.index()].branch_blockparams.is_empty() - ); - - &self.blocks[block.index()].branch_blockparams - } - - fn inst_operands(&self, insn: regalloc2::Inst) -> &[regalloc2::Operand] { - &self.instrs[insn.index()].ops - } - - 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!(), - } - } - } - let mut nodes = std::mem::take(&mut self.ci.nodes); let func = Function::new(&mut nodes, &self.tys, sig); @@ -2492,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; @@ -2731,7 +2710,6 @@ mod tests { const README: &str = include_str!("../README.md"); fn generate(ident: &'static str, input: &'static str, output: &mut String) { - _ = env_logger::builder().is_test(true).try_init(); let mut codegen = super::Codegen { files: crate::test_parse_files(ident, input), ..Default::default() }; @@ -2756,7 +2734,7 @@ mod tests { return; } - println!("{output}"); + //println!("{output}"); crate::test_run_vm(&out, output); }