From 49387dbe16897c788751b5ad3f3318b6c81ab061 Mon Sep 17 00:00:00 2001 From: mlokr Date: Sun, 8 Sep 2024 03:12:57 +0200 Subject: [PATCH] if a phy does not depend of different phy in the same loop we can modify it in place saving a register copy --- hblang/README.md | 4 +- hblang/src/son.rs | 242 +++++++++++++++++++++++-------- hblang/tests/son_tests_loops.txt | 17 +-- 3 files changed, 195 insertions(+), 68 deletions(-) diff --git a/hblang/README.md b/hblang/README.md index ebdd2b4..a4d1b6e 100644 --- a/hblang/README.md +++ b/hblang/README.md @@ -794,7 +794,7 @@ main := fn(): int { multiple_breaks := fn(arg: int): int { loop if arg < 10 { arg += 1 - //if arg == 3 break + if arg == 3 break } else break return arg } @@ -809,7 +809,7 @@ multiple_breaks := fn(arg: int): int { // } else break // return arg //} -// + //continue_and_state_change := fn(arg: int): int { // loop if arg < 10 { // if arg == 2 { diff --git a/hblang/src/son.rs b/hblang/src/son.rs index f684208..c5a84b4 100644 --- a/hblang/src/son.rs +++ b/hblang/src/son.rs @@ -20,6 +20,7 @@ use { mem, ops::{self, Range}, rc::Rc, + usize, }, }; @@ -35,6 +36,37 @@ macro_rules! node_loc { }; } +struct Drom(&'static str); + +impl Drop for Drom { + fn drop(&mut self) { + log::inf!("{}", self.0); + } +} + +#[derive(Default)] +struct BitSet { + data: Vec, +} + +impl BitSet { + const ELEM_SIZE: usize = std::mem::size_of::() * 8; + + pub fn clear(&mut self, bit_size: usize) { + let new_len = (bit_size + Self::ELEM_SIZE - 1) / Self::ELEM_SIZE; + self.data.clear(); + self.data.resize(new_len, 0); + } + + pub fn set(&mut self, idx: usize) -> bool { + let data_idx = idx / Self::ELEM_SIZE; + let sub_idx = idx % Self::ELEM_SIZE; + let prev = self.data[data_idx] & (1 << sub_idx); + self.data[data_idx] |= 1 << sub_idx; + prev == 0 + } +} + type Nid = u32; mod reg { @@ -431,13 +463,19 @@ mod ty { struct Nodes { values: Vec, + visited: BitSet, free: u32, lookup: HashMap<(Kind, [Nid; MAX_INPUTS], ty::Id), Nid>, } impl Default for Nodes { fn default() -> Self { - Self { values: Default::default(), free: u32::MAX, lookup: Default::default() } + Self { + values: Default::default(), + free: u32::MAX, + lookup: Default::default(), + visited: Default::default(), + } } } @@ -721,6 +759,13 @@ impl Nodes { self.remove(id) } + fn iter(&self) -> impl DoubleEndedIterator { + self.values.iter().enumerate().filter_map(|(i, s)| match s { + PoolSlot::Value(v) => Some((i as _, v)), + PoolSlot::Next(_) => None, + }) + } + fn fmt(&self, f: &mut fmt::Formatter, node: Nid, rcs: &mut [usize]) -> fmt::Result { let mut is_ready = || { if rcs[node as usize] == 0 { @@ -803,6 +848,27 @@ impl Nodes { Ok(()) } + fn graphviz_low(&self, out: &mut String) -> std::fmt::Result { + use std::fmt::Write; + + for (i, node) in self.iter() { + let color = if self.is_cfg(i) { "yellow" } else { "white" }; + writeln!(out, "node{i}[label=\"{}\" color={color}]", node.kind)?; + for &o in &node.outputs { + let color = if self.is_cfg(i) && self.is_cfg(o) { "red" } else { "black" }; + writeln!(out, "node{o} -> node{i}[color={color}]",)?; + } + } + + Ok(()) + } + + fn graphviz(&self) { + let out = &mut String::new(); + _ = self.graphviz_low(out); + log::inf!("{out}"); + } + fn is_cfg(&self, o: Nid) -> bool { matches!( self[o].kind, @@ -819,49 +885,63 @@ impl Nodes { fn check_final_integrity(&self) { let mut failed = false; - for (slot, i) in self.values.iter().zip(0u32..) { - match slot { - PoolSlot::Value(node) => { - debug_assert_eq!(node.lock_rc, 0, "{:?}", node.kind); - if !matches!(node.kind, Kind::Return | Kind::End) && node.outputs.is_empty() { - log::err!("outputs are empry {i} {:?}", node.kind); - failed = true; - } + for (i, node) in self.iter() { + debug_assert_eq!(node.lock_rc, 0, "{:?}", node.kind); + if !matches!(node.kind, Kind::Return | Kind::End) && node.outputs.is_empty() { + log::err!("outputs are empry {i} {:?}", node.kind); + failed = true; + } - for &o in &node.outputs { - let mut occurs = 0; - let other = match &self.values[o as usize] { - PoolSlot::Value(other) => other, - PoolSlot::Next(_) => { - log::err!( - "the edge points to dropped node: {i} {:?} {o}", - node.kind, - ); - failed = true; - continue; - } - }; - if let Kind::Call { ref args, .. } = other.kind { - occurs = args.iter().filter(|&&el| el == i).count(); - } - occurs += self[o].inputs.iter().filter(|&&el| el == i).count(); - if occurs == 0 { - log::err!( - "the edge is not bidirectional: {i} {:?} {o} {:?}", - node.kind, - other.kind - ); - failed = true; - } + for &o in &node.outputs { + let mut occurs = 0; + let other = match &self.values[o as usize] { + PoolSlot::Value(other) => other, + PoolSlot::Next(_) => { + log::err!("the edge points to dropped node: {i} {:?} {o}", node.kind,); + failed = true; + continue; } + }; + if let Kind::Call { ref args, .. } = other.kind { + occurs = args.iter().filter(|&&el| el == i).count(); + } + occurs += self[o].inputs.iter().filter(|&&el| el == i).count(); + if occurs == 0 { + log::err!( + "the edge is not bidirectional: {i} {:?} {o} {:?}", + node.kind, + other.kind + ); + failed = true; } - PoolSlot::Next(_) => {} } } if failed { panic!() } } + + fn check_scope_integrity(&self, scope: &[Variable]) { + for v in scope { + debug_assert!(self[v.value].lock_rc > 0); + } + } + + fn climb(&mut self, from: Nid, mut for_each: impl FnMut(Nid, &Node) -> bool) -> bool { + fn climb_impl( + nodes: &mut Nodes, + from: Nid, + for_each: &mut impl FnMut(Nid, &Node) -> bool, + ) -> bool { + { nodes[from].inputs }.iter().any(|&n| { + n != Nid::MAX + && nodes.visited.set(n as usize) + && (for_each(n, &nodes[n]) || climb_impl(nodes, n, for_each)) + }) + } + self.visited.clear(self.values.len()); + climb_impl(self, from, &mut for_each) + } } impl ops::Index for Nodes { @@ -912,6 +992,18 @@ impl Kind { } } +impl fmt::Display for Kind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Kind::ConstInt { value } => write!(f, "#{value}"), + Kind::Tuple { index } => write!(f, "tupl[{index}]"), + Kind::BinOp { op } => write!(f, "{op}"), + Kind::Call { func, .. } => write!(f, "call {func}"), + slf => write!(f, "{slf:?}"), + } + } +} + const MAX_INPUTS: usize = 3; #[derive(Debug)] @@ -1534,6 +1626,8 @@ impl Codegen { loob.scope[index].value = phy; } + self.ci.nodes.check_scope_integrity(&self.ci.vars); + Some(self.ci.vars[index].value) } Expr::BinOp { left: &Expr::Ident { id, .. }, op: TokenKind::Decl, right } => { @@ -1617,17 +1711,20 @@ impl Codegen { for then_var in then_scope { self.ci.nodes.unlock_remove(then_var.value); } + self.ci.nodes.check_scope_integrity(&self.ci.vars); return None; } else if lcntrl == Nid::MAX { for then_var in then_scope { self.ci.nodes.unlock_remove(then_var.value); } + self.ci.nodes.check_scope_integrity(&self.ci.vars); return Some(0); } else if rcntrl == Nid::MAX { for else_var in &self.ci.vars { self.ci.nodes.unlock_remove(else_var.value); } self.ci.vars = then_scope; + self.ci.nodes.check_scope_integrity(&self.ci.vars); self.ci.ctrl = lcntrl; return Some(0); } @@ -1652,6 +1749,7 @@ impl Codegen { else_var.value = self.ci.nodes.new_node(ty, Kind::Phi, inps); self.ci.nodes.lock(else_var.value); } + self.ci.nodes.check_scope_integrity(&self.ci.vars); Some(0) } @@ -1700,6 +1798,7 @@ impl Codegen { else_var.value = self.ci.nodes.new_node(ty, Kind::Phi, inps); self.ci.nodes.lock(else_var.value); } + self.ci.nodes.check_scope_integrity(&self.ci.vars); } self.ci.nodes.modify_input(node, 1, self.ci.ctrl); @@ -1709,8 +1808,13 @@ impl Codegen { return None; } + self.ci.nodes.lock(self.ci.ctrl); + std::mem::swap(&mut self.ci.vars, &mut break_scope); + self.ci.nodes.check_scope_integrity(&self.ci.vars); + self.ci.nodes.check_scope_integrity(&break_scope); + self.ci.nodes.check_scope_integrity(&scope); for ((dest_var, scope_var), loop_var) in self.ci.vars.iter_mut().zip(scope).zip(break_scope) { @@ -1740,6 +1844,8 @@ impl Codegen { dest_var.value = phy; } + self.ci.nodes.unlock(self.ci.ctrl); + Some(0) } Expr::Break { pos } => { @@ -1774,12 +1880,15 @@ impl Codegen { else_var.value = self.ci.nodes.new_node(ty, Kind::Phi, inps); self.ci.nodes.lock(else_var.value); } + self.ci.nodes.check_scope_integrity(&self.ci.vars); + self.ci.nodes.check_scope_integrity(&loob.break_scope); } self.ci.ctrl = self.ci.end; None } Expr::Continue { pos } => { + todo!(); let Some(loob) = self.ci.loops.last_mut() else { self.report(pos, "break outside a loop"); return None; @@ -1813,6 +1922,7 @@ impl Codegen { else_var.value = self.ci.nodes.new_node(ty, Kind::Phi, inps); self.ci.nodes.lock(else_var.value); } + self.ci.nodes.check_scope_integrity(&self.ci.vars); } self.ci.ctrl = self.ci.end; @@ -1883,10 +1993,14 @@ impl Codegen { 0 }; + _ = self.ci.nodes[self.ci.ctrl]; + _ = self.ci.nodes[self.ci.end]; + _ = self.ci.nodes[value]; + let inps = [self.ci.ctrl, self.ci.end, value]; + let out = &mut String::new(); self.report_log_to(pos, "returning here", out); - log::dbg!("{out}"); self.ci.ctrl = self.ci.nodes.new_node(ty::VOID, Kind::Return, inps); let expected = *self.ci.ret.get_or_insert(self.tof(value)); @@ -2006,6 +2120,8 @@ impl Codegen { } if self.errors.borrow().is_empty() { + self.ci.nodes.graphviz(); + #[cfg(debug_assertions)] { self.ci.nodes.check_final_integrity(); @@ -2121,11 +2237,21 @@ impl Codegen { let left_unreachable = self.color_control(self.ci.nodes[ctrl].outputs[0]); let right_unreachable = self.color_control(self.ci.nodes[ctrl].outputs[1]); - let dest = left_unreachable.or(right_unreachable)?; + let dest = match (left_unreachable, right_unreachable) { + (None, None) => return None, + (None, Some(n)) | (Some(n), None) => n, + (Some(l), Some(r)) if l == r => l, + (Some(left), Some(right)) => { + todo!() + } + }; + if self.ci.nodes[dest].kind == Kind::Loop { return Some(dest); } + debug_assert!(left_unreachable.is_none() || right_unreachable.is_none()); + debug_assert_eq!(self.ci.nodes[dest].kind, Kind::Region); for i in 0..self.ci.nodes[dest].outputs.len() { @@ -2189,7 +2315,7 @@ impl Codegen { } fn color_phy(&mut self, maybe_phy: Nid) { - let Node { kind: Kind::Phi, inputs: [_, left, right], .. } = self.ci.nodes[maybe_phy] + let Node { kind: Kind::Phi, inputs: [region, left, right], .. } = self.ci.nodes[maybe_phy] else { return; }; @@ -2198,12 +2324,14 @@ impl Codegen { let rcolor = self.color_expr_consume(right); if self.ci.nodes[maybe_phy].color != 0 { - //if let Some(c) = lcolor { - // self.ci.recolor(left, c, self.ci.nodes[maybe_phy].color); - //} - //if let Some(c) = rcolor { - // self.ci.recolor(right, c, self.ci.nodes[maybe_phy].color); - //} + // loop phy + if let Some(c) = rcolor + && !self.ci.nodes.climb(right, |i, n| { + matches!(n.kind, Kind::Phi) && n.inputs[0] == region && i != maybe_phy + }) + { + self.ci.recolor(right, c, self.ci.nodes[maybe_phy].color); + } } else { let color = match (lcolor, rcolor) { (None, None) => self.ci.next_color(), @@ -2268,19 +2396,19 @@ impl Codegen { let ret = self.ci.nodes[ctrl].inputs[2]; if ret != 0 { // NOTE: this is safer less efficient way, maybe it will be needed - //self.emit_expr_consume(ret); - //if node_color!(self, ret).call_count != self.ci.call_count { - // let src = node_loc!(self, ret); - // let loc = match self.tys.size_of(self.ci.ret.expect("TODO")) { - // 0 => Loc::default(), - // 1..=8 => Loc { reg: 1 }, - // s => todo!("{s}"), - // }; - // if src != loc { - // let inst = instrs::cp(loc.reg, src.reg); - // self.ci.emit(inst); - // } - //} + // self.emit_expr_consume(ret); + // if node_color!(self, ret).call_count != self.ci.call_count { + // let src = node_loc!(self, ret); + // let loc = match self.tys.size_of(self.ci.ret.expect("TODO")) { + // 0 => Loc::default(), + // 1..=8 => Loc { reg: 1 }, + // s => todo!("{s}"), + // }; + // if src != loc { + // let inst = instrs::cp(loc.reg, src.reg); + // self.ci.emit(inst); + // } + // } node_loc!(self, ret) = match self.tys.size_of(self.ci.ret.expect("TODO")) { 0 => Loc::default(), diff --git a/hblang/tests/son_tests_loops.txt b/hblang/tests/son_tests_loops.txt index 811823c..ca91410 100644 --- a/hblang/tests/son_tests_loops.txt +++ b/hblang/tests/son_tests_loops.txt @@ -7,8 +7,8 @@ main: ADDI64 r254, r254, 8d JALA r0, r31, 0a fib: - ADDI64 r254, r254, -64d - ST r31, r254, 0a, 64h + ADDI64 r254, r254, -56d + ST r31, r254, 0a, 56h CP r32, r2 CP r33, r32 LI64 r34, 0d @@ -17,15 +17,14 @@ fib: CP r36, r35 2: JNE r33, r34, :0 JMP :1 - 0: SUB64 r37, r33, r35 - ADD64 r38, r1, r36 - CP r33, r37 + 0: SUB64 r33, r33, r35 + ADD64 r37, r1, r36 CP r1, r36 - CP r36, r38 + CP r36, r37 JMP :2 - 1: LD r31, r254, 0a, 64h - ADDI64 r254, r254, 64d + 1: LD r31, r254, 0a, 56h + ADDI64 r254, r254, 56d JALA r0, r31, 0a -code size: 207 +code size: 204 ret: 55 status: Ok(())