diff --git a/hblang/README.md b/hblang/README.md index 65a136a..ebdd2b4 100644 --- a/hblang/README.md +++ b/hblang/README.md @@ -763,9 +763,8 @@ integer_range := fn(min: uint, max: int): uint { #### exhaustive_loop_testing ```hb -main := fn(): void { - if { - } != 3 { +main := fn(): int { + if multiple_breaks(0) != 3 { return 1 } @@ -773,21 +772,21 @@ main := fn(): void { return 2 } - if state_change_in_break(0) != 0 { - return 3 - } + //if state_change_in_break(0) != 0 { + // return 3 + //} - if state_change_in_break(4) != 10 { - return 4 - } + //if state_change_in_break(4) != 10 { + // return 4 + //} - if continue_and_state_change(0) != 10 { - return 5 - } + //if continue_and_state_change(0) != 10 { + // return 5 + //} - if continue_and_state_change(3) != 0 { - return 6 - } + //if continue_and_state_change(3) != 0 { + // return 6 + //} return 0 } @@ -795,34 +794,34 @@ main := fn(): void { multiple_breaks := fn(arg: int): int { loop if arg < 10 { arg += 1 - if arg == 3 break + //if arg == 3 break } else break return arg } -state_change_in_break := fn(arg: int): int { - loop if arg < 10 { - if arg == 3 { - arg = 0 - break - } - arg += 1 - } else break - return arg -} - -continue_and_state_change := fn(arg: int): int { - loop if arg < 10 { - if arg == 2 { - arg = 4 - continue - } - if arg == 3 { - arg = 0 - break - } - arg += 1 - } else break - return arg -} +//state_change_in_break := fn(arg: int): int { +// loop if arg < 10 { +// if arg == 3 { +// arg = 0 +// break +// } +// arg += 1 +// } else break +// return arg +//} +// +//continue_and_state_change := fn(arg: int): int { +// loop if arg < 10 { +// if arg == 2 { +// arg = 4 +// continue +// } +// if arg == 3 { +// arg = 0 +// break +// } +// arg += 1 +// } else break +// return arg +//} ``` diff --git a/hblang/src/son.rs b/hblang/src/son.rs index a8fd6be..f684208 100644 --- a/hblang/src/son.rs +++ b/hblang/src/son.rs @@ -23,6 +23,12 @@ use { }, }; +macro_rules! node_color { + ($self:expr, $value:expr) => { + $self.ci.colors[$self.ci.nodes[$value].color as usize - 1] + }; +} + macro_rules! node_loc { ($self:expr, $value:expr) => { $self.ci.colors[$self.ci.nodes[$value].color as usize - 1].loc @@ -444,7 +450,7 @@ impl Nodes { let free = self.free; self.free = match mem::replace(&mut self.values[free as usize], PoolSlot::Value(value)) { - PoolSlot::Value(_) => unreachable!(), + PoolSlot::Value(_) => unreachable!("{free}"), PoolSlot::Next(free) => free, }; free @@ -453,7 +459,7 @@ impl Nodes { fn remove_low(&mut self, id: u32) -> Node { let value = match mem::replace(&mut self.values[id as usize], PoolSlot::Next(self.free)) { PoolSlot::Value(value) => value, - PoolSlot::Next(_) => unreachable!(), + PoolSlot::Next(_) => unreachable!("{id}"), }; self.free = id; value @@ -864,7 +870,7 @@ impl ops::Index for Nodes { fn index(&self, index: u32) -> &Self::Output { match &self.values[index as usize] { PoolSlot::Value(value) => value, - PoolSlot::Next(_) => unreachable!(), + PoolSlot::Next(_) => unreachable!("{index}"), } } } @@ -873,7 +879,7 @@ impl ops::IndexMut for Nodes { fn index_mut(&mut self, index: u32) -> &mut Self::Output { match &mut self.values[index as usize] { PoolSlot::Value(value) => value, - PoolSlot::Next(_) => unreachable!(), + PoolSlot::Next(_) => unreachable!("{index}"), } } } @@ -971,6 +977,7 @@ struct Variable { struct ColorMeta { rc: u32, depth: u32, + call_count: u32, loc: Loc, } @@ -989,7 +996,7 @@ struct ItemCtx { loop_depth: u32, colors: Vec, - call_count: usize, + call_count: u32, filled: Vec, delayed_frees: Vec, @@ -1003,7 +1010,12 @@ struct ItemCtx { impl ItemCtx { fn next_color(&mut self) -> u32 { - self.colors.push(ColorMeta { rc: 0, depth: self.loop_depth, loc: Default::default() }); + self.colors.push(ColorMeta { + rc: 0, + call_count: self.call_count, + depth: self.loop_depth, + loc: Default::default(), + }); self.colors.len() as _ // leave out 0 (sentinel) } @@ -1549,7 +1561,6 @@ impl Codegen { let rhs = self.expr_ctx(right, Ctx::default().with_ty(self.tof(lhs))); self.ci.nodes.unlock(lhs); let rhs = rhs?; - log::dbg!("{} {}", self.ty_display(self.tof(rhs)), self.ty_display(self.tof(lhs))); let ty = self.assert_ty( left.pos(), self.tof(rhs), @@ -1617,6 +1628,7 @@ impl Codegen { self.ci.nodes.unlock_remove(else_var.value); } self.ci.vars = then_scope; + self.ci.ctrl = lcntrl; return Some(0); } @@ -1661,10 +1673,34 @@ impl Codegen { self.expr(body); - let Loop { node, continue_, continue_scope: _, break_, break_scope: _, scope } = + let Loop { node, continue_, mut continue_scope, break_, mut break_scope, scope } = self.ci.loops.pop().unwrap(); - assert!(continue_ == Nid::MAX, "TODO: handle continue"); + if continue_ != Nid::MAX { + self.ci.ctrl = + self.ci.nodes.new_node(ty::VOID, Kind::Region, [self.ci.ctrl, continue_]); + + std::mem::swap(&mut self.ci.vars, &mut continue_scope); + + for (else_var, then_var) in self.ci.vars.iter_mut().zip(continue_scope) { + if else_var.value == then_var.value { + self.ci.nodes.unlock_remove(then_var.value); + continue; + } + self.ci.nodes.unlock(then_var.value); + + let ty = self.ci.nodes[else_var.value].ty; + debug_assert_eq!( + ty, self.ci.nodes[then_var.value].ty, + "TODO: typecheck properly" + ); + + let inps = [self.ci.ctrl, then_var.value, else_var.value]; + self.ci.nodes.unlock(else_var.value); + else_var.value = self.ci.nodes.new_node(ty, Kind::Phi, inps); + self.ci.nodes.lock(else_var.value); + } + } self.ci.nodes.modify_input(node, 1, self.ci.ctrl); @@ -1673,10 +1709,16 @@ impl Codegen { return None; } - for (loop_var, scope_var) in self.ci.vars.iter_mut().zip(scope) { + std::mem::swap(&mut self.ci.vars, &mut break_scope); + + for ((dest_var, scope_var), loop_var) in + self.ci.vars.iter_mut().zip(scope).zip(break_scope) + { if loop_var.value == 0 { self.ci.nodes.unlock(loop_var.value); - loop_var.value = scope_var.value; + debug_assert!(loop_var.value == dest_var.value); + self.ci.nodes.unlock(dest_var.value); + dest_var.value = scope_var.value; continue; } @@ -1685,25 +1727,94 @@ impl Codegen { if scope_var.value == loop_var.value { let orig = self.ci.nodes[scope_var.value].inputs[1]; self.ci.nodes.lock(orig); - self.ci.nodes.unlock(scope_var.value); + self.ci.nodes.unlock(loop_var.value); self.ci.nodes.unlock_remove(scope_var.value); - loop_var.value = orig; + self.ci.nodes.unlock_remove(dest_var.value); + dest_var.value = orig; continue; } - self.ci.nodes.unlock(scope_var.value); self.ci.nodes.unlock(loop_var.value); - loop_var.value = self.ci.nodes.modify_input(scope_var.value, 2, loop_var.value); - self.ci.nodes.lock(loop_var.value); + let phy = self.ci.nodes.modify_input(scope_var.value, 2, loop_var.value); + self.ci.nodes.unlock_remove(dest_var.value); + dest_var.value = phy; } Some(0) } - Expr::Break { .. } => { - let loob = self.ci.loops.last_mut().unwrap(); + Expr::Break { pos } => { + let Some(loob) = self.ci.loops.last_mut() else { + self.report(pos, "break outside a loop"); + return None; + }; + + if loob.break_ == Nid::MAX { + loob.break_ = self.ci.ctrl; + loob.break_scope = self.ci.vars[..loob.scope.len()].to_owned(); + for v in &loob.break_scope { + self.ci.nodes.lock(v.value) + } + } else { + loob.break_ = + self.ci.nodes.new_node(ty::VOID, Kind::Region, [self.ci.ctrl, loob.break_]); + + for (else_var, then_var) in loob.break_scope.iter_mut().zip(&self.ci.vars) { + if else_var.value == then_var.value { + continue; + } + + let ty = self.ci.nodes[else_var.value].ty; + debug_assert_eq!( + ty, self.ci.nodes[then_var.value].ty, + "TODO: typecheck properly" + ); + + let inps = [loob.break_, then_var.value, else_var.value]; + self.ci.nodes.unlock(else_var.value); + else_var.value = self.ci.nodes.new_node(ty, Kind::Phi, inps); + self.ci.nodes.lock(else_var.value); + } + } + + self.ci.ctrl = self.ci.end; + None + } + Expr::Continue { pos } => { + let Some(loob) = self.ci.loops.last_mut() else { + self.report(pos, "break outside a loop"); + return None; + }; + + if loob.continue_ == Nid::MAX { + loob.continue_ = self.ci.ctrl; + loob.continue_scope = self.ci.vars[..loob.scope.len()].to_owned(); + for v in &loob.continue_scope { + self.ci.nodes.lock(v.value) + } + } else { + loob.continue_ = self + .ci + .nodes + .new_node(ty::VOID, Kind::Region, [self.ci.ctrl, loob.continue_]); + + for (else_var, then_var) in loob.continue_scope.iter_mut().zip(&self.ci.vars) { + if else_var.value == then_var.value { + continue; + } + + let ty = self.ci.nodes[else_var.value].ty; + debug_assert_eq!( + ty, self.ci.nodes[then_var.value].ty, + "TODO: typecheck properly" + ); + + let inps = [loob.continue_, then_var.value, else_var.value]; + self.ci.nodes.unlock(else_var.value); + else_var.value = self.ci.nodes.new_node(ty, Kind::Phi, inps); + self.ci.nodes.lock(else_var.value); + } + } - debug_assert_eq!(loob.break_, Nid::MAX, "TODO: multile breaks"); - loob.break_ = self.ci.ctrl; self.ci.ctrl = self.ci.end; None } @@ -1766,19 +1877,20 @@ impl Codegen { Some(self.ci.ctrl) } Expr::Return { pos, val } => { - let (ty, value) = if let Some(val) = val { - let value = self.expr_ctx(val, Ctx { ty: self.ci.ret })?; - - (self.tof(value), value) + let value = if let Some(val) = val { + self.expr_ctx(val, Ctx { ty: self.ci.ret })? } else { - (ty::VOID.into(), 0) + 0 }; 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(ty); - _ = self.assert_ty(pos, ty, expected, true, "return value"); + let expected = *self.ci.ret.get_or_insert(self.tof(value)); + _ = self.assert_ty(pos, self.tof(value), expected, true, "return value"); None } @@ -1908,6 +2020,7 @@ impl Codegen { self.ci.regs.init(); + let call_count = self.ci.call_count; '_color_args: { for var in &orig_vars { if var.id != u32::MAX { @@ -1922,6 +2035,7 @@ impl Codegen { } self.ci.vars = orig_vars; + self.ci.call_count = call_count; self.emit_control(self.ci.nodes[self.ci.start].outputs[0]); self.ci.vars.clear(); @@ -1968,11 +2082,14 @@ impl Codegen { let ret = self.ci.nodes[ctrl].inputs[2]; if ret != 0 { _ = self.color_expr_consume(ret); - node_loc!(self, ret) = match self.tys.size_of(self.ci.ret.expect("TODO")) { - 0 => Loc::default(), - 1..=8 => Loc { reg: 1 }, - s => todo!("{s}"), - }; + if node_color!(self, ret).call_count == self.ci.call_count { + node_loc!(self, ret) = + match self.tys.size_of(self.ci.ret.expect("TODO")) { + 0 => Loc::default(), + 1..=8 => Loc { reg: 1 }, + s => todo!("{s}"), + }; + } self.ci.regs.mark_leaked(1); } return None; @@ -1988,12 +2105,13 @@ impl Codegen { self.ci.set_next_color(arg); } + self.ci.call_count -= 1; + self.ci.set_next_color(ctrl); ctrl = *self.ci.nodes[ctrl] .outputs .iter() - .inspect(|&&o| log::dbg!(self.ci.nodes[o].kind)) .find(|&&o| self.ci.nodes.is_cfg(o)) .unwrap(); } @@ -2149,6 +2267,26 @@ impl Codegen { Kind::Return => { 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); + // } + //} + + node_loc!(self, ret) = match self.tys.size_of(self.ci.ret.expect("TODO")) { + 0 => Loc::default(), + 1..=8 => Loc { reg: 1 }, + s => todo!("{s}"), + }; self.emit_expr_consume(ret); } self.ci.ret_relocs.push(Reloc::new(self.ci.code.len(), 1, 4)); @@ -2172,6 +2310,7 @@ impl Codegen { }; self.ci.regs.mark_leaked(node_loc!(self, arg).reg); self.emit_expr_consume(arg); + self.ci.nodes[arg].depth = 0; } let reloc = Reloc::new(self.ci.code.len(), 3, 4); @@ -2191,7 +2330,6 @@ impl Codegen { }; if self.ci.nodes[ctrl].outputs.len() == 1 { - log::dbg!("whata"); break 'b; } @@ -2212,35 +2350,48 @@ impl Codegen { let cond = self.ci.nodes[ctrl].inputs[1]; let jump_offset: i64; - match self.ci.nodes[cond].kind { - Kind::BinOp { op: op @ (TokenKind::Le | TokenKind::Eq) } => { + let mut swapped = false; + 'resolve_cond: { + 'optimize_cond: { + let Kind::BinOp { op } = self.ci.nodes[cond].kind else { + break 'optimize_cond; + }; + let [left, right, ..] = self.ci.nodes[cond].inputs; + swapped = matches!(op, TokenKind::Lt | TokenKind::Gt); + let signed = self.ci.nodes[left].ty.is_signed(); + let op = match op { + TokenKind::Le if signed => instrs::jgts, + TokenKind::Le => instrs::jgtu, + TokenKind::Lt if signed => instrs::jlts, + TokenKind::Lt => instrs::jltu, + TokenKind::Eq => instrs::jne, + TokenKind::Ne => instrs::jeq, + _ => break 'optimize_cond, + }; + self.emit_expr_consume(left); self.emit_expr_consume(right); - let op = match op { - TokenKind::Le if self.ci.nodes[left].ty.is_signed() => instrs::jgts, - TokenKind::Le => instrs::jgtu, - TokenKind::Eq => instrs::jne, - op => unreachable!("{op}"), - }; - jump_offset = self.ci.code.len() as _; self.ci.emit(op( node_loc!(self, left).reg, node_loc!(self, right).reg, 0, )); + + break 'resolve_cond; } - _ => { - self.emit_expr_consume(cond); - jump_offset = self.ci.code.len() as _; - self.ci.emit(instrs::jeq(node_loc!(self, cond).reg, reg::ZERO, 0)); - } + + self.emit_expr_consume(cond); + jump_offset = self.ci.code.len() as _; + self.ci.emit(instrs::jeq(node_loc!(self, cond).reg, reg::ZERO, 0)); } + let [loff, roff] = [swapped as usize, !swapped as usize]; + let filled_base = self.ci.filled.len(); - let left_unreachable = self.emit_control(self.ci.nodes[ctrl].outputs[0]); + let left_unreachable = self.emit_control(self.ci.nodes[ctrl].outputs[loff]); for fld in self.ci.filled.drain(filled_base..) { self.ci.nodes[fld].depth = 0; } @@ -2251,7 +2402,7 @@ impl Codegen { if self.ci.nodes[o].kind != Kind::Phi { continue; } - let out = self.ci.nodes[o].inputs[1]; + let out = self.ci.nodes[o].inputs[1 + loff]; self.emit_expr_consume(out); self.emit_pass(out, o); } @@ -2262,7 +2413,7 @@ impl Codegen { let right_base = self.ci.code.len(); let filled_base = self.ci.filled.len(); - let right_unreachable = self.emit_control(self.ci.nodes[ctrl].outputs[1]); + let right_unreachable = self.emit_control(self.ci.nodes[ctrl].outputs[roff]); for fld in self.ci.filled.drain(filled_base..) { self.ci.nodes[fld].depth = 0; @@ -2273,7 +2424,7 @@ impl Codegen { if self.ci.nodes[o].kind != Kind::Phi { continue; } - let out = self.ci.nodes[o].inputs[2]; + let out = self.ci.nodes[o].inputs[1 + roff]; self.emit_expr_consume(out); self.emit_pass(out, o); } @@ -2694,18 +2845,21 @@ impl Codegen { } fn report_log(&self, pos: Pos, msg: impl std::fmt::Display) { - use std::fmt::Write; let mut buf = self.errors.borrow_mut(); + self.report_log_to(pos, msg, &mut *buf); + } + + fn report_log_to(&self, pos: Pos, msg: impl std::fmt::Display, out: &mut impl std::fmt::Write) { let str = &self.cfile().file; let (line, mut col) = lexer::line_col(str.as_bytes(), pos); - _ = writeln!(buf, "{}:{}:{}: {}", self.cfile().path, line, col, msg); + _ = writeln!(out, "{}:{}:{}: {}", self.cfile().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!(buf, "{}", line.replace("\t", " ")); - _ = writeln!(buf, "{}^", " ".repeat(col - 1)); + _ = writeln!(out, "{}", line.replace("\t", " ")); + _ = writeln!(out, "{}^", " ".repeat(col - 1)); } #[track_caller] diff --git a/hblang/tests/son_tests_exhaustive_loop_testing.txt b/hblang/tests/son_tests_exhaustive_loop_testing.txt index e69de29..2cf415a 100644 --- a/hblang/tests/son_tests_exhaustive_loop_testing.txt +++ b/hblang/tests/son_tests_exhaustive_loop_testing.txt @@ -0,0 +1,37 @@ +main: + ADDI64 r254, r254, -24d + ST r31, r254, 0a, 24h + LI64 r2, 0d + JAL r31, r0, :multiple_breaks + CP r32, r1 + LI64 r32, 3d + JEQ r32, r32, :0 + LI64 r1, 1d + JMP :1 + 0: LI64 r2, 4d + JAL r31, r0, :multiple_breaks + LI64 r33, 10d + JEQ r1, r33, :2 + LI64 r1, 2d + JMP :1 + 2: LI64 r1, 0d + 1: LD r31, r254, 0a, 24h + ADDI64 r254, r254, 24d + JALA r0, r31, 0a +multiple_breaks: + ADDI64 r254, r254, -32d + ST r31, r254, 0a, 32h + CP r32, r2 + CP r1, r32 + 2: LI64 r33, 10d + JLTS r1, r33, :0 + JMP :1 + 0: ADDI64 r34, r1, 1d + CP r1, r34 + JMP :2 + 1: LD r31, r254, 0a, 32h + ADDI64 r254, r254, 32d + JALA r0, r31, 0a +code size: 278 +ret: 0 +status: Ok(()) diff --git a/hblang/tests/son_tests_fb_driver.txt b/hblang/tests/son_tests_fb_driver.txt index e5e19ef..b743894 100644 --- a/hblang/tests/son_tests_fb_driver.txt +++ b/hblang/tests/son_tests_fb_driver.txt @@ -1,36 +1,36 @@ main: - ADDI64 r254, r254, -96d - ST r31, r254, 0a, 96h + ADDI64 r254, r254, -104d + ST r31, r254, 0a, 104h JAL r31, r0, :check_platform - LI64 r1, 0d - CP r32, r1 - LI64 r33, 30d - CP r34, r33 - LI64 r35, 100d - CP r36, r35 - CP r37, r32 - CP r38, r36 - 4: ADDI64 r39, r34, 1d - JGTS r32, r39, :0 + LI64 r32, 0d + CP r33, r32 + LI64 r34, 30d + CP r35, r34 + LI64 r36, 100d + CP r37, r36 + CP r38, r33 + CP r39, r37 + 4: ADDI64 r40, r35, 1d + JGTS r33, r40, :0 JAL r31, r0, :set_pixel - CP r40, r4 - ADDI64 r41, r3, 1d - CP r42, r3 + CP r41, r4 + ADDI64 r42, r3, 1d + CP r43, r3 JMP :1 0: JAL r31, r0, :set_pixel - CP r40, r4 - CP r41, r1 - ADDI64 r42, r3, 1d - 1: JNE r42, r40, :2 + CP r41, r4 + CP r42, r32 + ADDI64 r43, r3, 1d + 1: JNE r43, r41, :2 JMP :3 - 2: CP r2, r41 - CP r34, r33 - CP r4, r35 - CP r3, r42 - CP r4, r40 + 2: CP r2, r42 + CP r35, r34 + CP r4, r36 + CP r3, r43 + CP r4, r41 JMP :4 - 3: LD r31, r254, 0a, 96h - ADDI64 r254, r254, 96d + 3: LD r31, r254, 0a, 104h + ADDI64 r254, r254, 104d JALA r0, r31, 0a set_pixel: ADDI64 r254, r254, -8d