From 60851779829a12a10dc11c98b8642d88e656157d Mon Sep 17 00:00:00 2001 From: Jakub Doka Date: Tue, 17 Dec 2024 20:53:13 +0100 Subject: [PATCH] fixed the unreachable functions deleting branches Signed-off-by: Jakub Doka --- bytecode/src/lib.rs | 3 +-- lang/README.md | 5 ++++- lang/src/backend/hbvm/regalloc.rs | 33 ++++++++++++++++++------------- lang/src/fs.rs | 3 ++- lang/src/nodes.rs | 5 ++++- lang/src/son.rs | 14 +++++++++++-- 6 files changed, 42 insertions(+), 21 deletions(-) diff --git a/bytecode/src/lib.rs b/bytecode/src/lib.rs index f32bbc75..26132dd7 100644 --- a/bytecode/src/lib.rs +++ b/bytecode/src/lib.rs @@ -254,8 +254,7 @@ pub fn disasm<'a>( || global_offset > off + len || prev .get(global_offset as usize) - .map_or(true, |&b| instr_from_byte(b).is_err()) - || prev[global_offset as usize] == 0; + .map_or(true, |&b| instr_from_byte(b).is_err()); has_oob |= local_has_oob; let label = labels.get(&global_offset).unwrap(); if local_has_oob { diff --git a/lang/README.md b/lang/README.md index e14a3f4b..4c0f1a03 100644 --- a/lang/README.md +++ b/lang/README.md @@ -662,10 +662,13 @@ main := fn(): uint { #### die ```hb + +fun := fn(): never die + main := fn(): never { // simply emmits 'un' instruction that immediately terminates the execution // the expresion has similar properties to 'return' but does not accept a value - die + fun() } ``` diff --git a/lang/src/backend/hbvm/regalloc.rs b/lang/src/backend/hbvm/regalloc.rs index 1d60af78..55053262 100644 --- a/lang/src/backend/hbvm/regalloc.rs +++ b/lang/src/backend/hbvm/regalloc.rs @@ -23,7 +23,7 @@ impl HbvmBackend { tys: &Types, files: &EntSlice, ) -> (usize, bool) { - let tail = Function::build(nodes, tys, &mut self.ralloc, sig); + let tail = FunctionBuilder::build(nodes, tys, &mut self.ralloc, sig); let strip_load = |value| match nodes[value].kind { Kind::Load { .. } if nodes[value].ty.loc(tys) == Loc::Stack => nodes[value].inputs[1], @@ -392,8 +392,9 @@ impl HbvmBackend { todo!("unhandled operator: {op}"); } } - Kind::Call { args, func } => { + Kind::Call { args, func, .. } => { let (ret, mut parama) = tys.parama(node.ty); + debug_assert!(node.ty != ty::Id::NEVER || ret.is_none()); if let Some(PLoc::Ref(r, ..)) = ret { self.emit(instrs::cp(r, atr(*node.inputs.last().unwrap()))) } @@ -530,7 +531,7 @@ impl HbvmBackend { } } -struct Function<'a> { +struct FunctionBuilder<'a> { sig: Sig, tail: bool, nodes: &'a Nodes, @@ -538,7 +539,7 @@ struct Function<'a> { func: &'a mut Res, } -impl core::fmt::Debug for Function<'_> { +impl core::fmt::Debug for FunctionBuilder<'_> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { for block in &self.func.blocks { writeln!(f, "{:?}", self.nodes[block.entry].kind)?; @@ -551,7 +552,7 @@ impl core::fmt::Debug for Function<'_> { } } -impl<'a> Function<'a> { +impl<'a> FunctionBuilder<'a> { fn build(nodes: &'a Nodes, tys: &'a Types, func: &'a mut Res, sig: Sig) -> bool { func.blocks.clear(); func.instrs.clear(); @@ -670,17 +671,21 @@ impl<'a> Function<'a> { self.emit_node(o); } } - Kind::Call { func, .. } => { + Kind::Call { func, unreachable, .. } => { self.tail &= func == ty::Func::ECA; - self.add_instr(nid); - - for &o in node.outputs.iter().rev() { - if self.nodes[o].inputs[0] == nid - || (matches!(self.nodes[o].kind, Kind::Loop | Kind::Region) - && self.nodes[o].inputs[1] == nid) - { - self.emit_node(o); + if unreachable { + self.close_block(nid); + self.emit_node(node.outputs[0]); + } else { + self.add_instr(nid); + for &o in node.outputs.iter().rev() { + if self.nodes[o].inputs[0] == nid + || (matches!(self.nodes[o].kind, Kind::Loop | Kind::Region) + && self.nodes[o].inputs[1] == nid) + { + self.emit_node(o); + } } } } diff --git a/lang/src/fs.rs b/lang/src/fs.rs index e638a28c..61a8f46b 100644 --- a/lang/src/fs.rs +++ b/lang/src/fs.rs @@ -177,8 +177,9 @@ pub fn run_compiler( if options.dump_asm { let mut disasm = String::new(); - codegen.disasm(&mut disasm, out).map_err(|e| io::Error::other(e.to_string()))?; + let err = codegen.disasm(&mut disasm, out).map_err(|e| io::Error::other(e.to_string())); *out = disasm.into_bytes(); + err? } } diff --git a/lang/src/nodes.rs b/lang/src/nodes.rs index 1d1f17ff..56fc43d7 100644 --- a/lang/src/nodes.rs +++ b/lang/src/nodes.rs @@ -97,6 +97,7 @@ impl Nodes { debug_assert_ne!(next, 0); if matches!(self[cursor].kind, Kind::Then | Kind::Else) { debug_assert_eq!(self[next].kind, Kind::If); + debug_assert_eq!(self[next].ty, ty::Id::VOID); let other = self[next].outputs[(self[next].outputs[0] == cursor) as usize]; self[other].loop_depth.set(depth - 1); } @@ -1714,7 +1715,7 @@ impl Nodes { Kind::BinOp { op } | Kind::UnOp { op } => { write!(out, "{:>4}: ", op.name()) } - Kind::Call { func, args: _ } => { + Kind::Call { func, args: _, unreachable: _ } => { write!(out, "call: {func} {} ", self[node].depth.get()) } Kind::Global { global } => write!(out, "glob: {global:<5}"), @@ -2109,6 +2110,7 @@ pub enum Kind { }, // [ctrl, ...args] Call { + unreachable: bool, func: ty::Func, args: ty::Tuple, }, @@ -2163,6 +2165,7 @@ impl Kind { fn ends_basic_block(&self) -> bool { matches!(self, Self::Return { .. } | Self::If | Self::End | Self::Die) + || matches!(self, Kind::Call { unreachable: true, .. }) } pub fn starts_basic_block(&self) -> bool { diff --git a/lang/src/son.rs b/lang/src/son.rs index fb13e543..af5aefef 100644 --- a/lang/src/son.rs +++ b/lang/src/son.rs @@ -1516,7 +1516,11 @@ impl<'a> Codegen<'a> { inps[0] = self.ci.ctrl.get(); self.ci.ctrl.set( - self.ci.nodes.new_node_nop(ty, Kind::Call { func: ty::Func::ECA, args }, inps), + self.ci.nodes.new_node_nop( + ty, + Kind::Call { func: ty::Func::ECA, args, unreachable: false }, + inps, + ), &mut self.ci.nodes, ); @@ -2656,13 +2660,19 @@ impl<'a> Codegen<'a> { inps[0] = self.ci.ctrl.get(); self.ci.ctrl.set( - self.ci.nodes.new_node_nop(sig.ret, Kind::Call { func: fu, args: sig.args }, inps), + self.ci.nodes.new_node_nop( + sig.ret, + Kind::Call { func: fu, args: sig.args, unreachable: sig.ret == ty::Id::NEVER }, + inps, + ), &mut self.ci.nodes, ); self.add_clobber_stores(clobbered_aliases); if sig.ret == ty::Id::NEVER { + self.ci.nodes.bind(self.ci.ctrl.get(), NEVER); + self.ci.ctrl.set(NEVER, &mut self.ci.nodes); return None; }