From 8076716df5620eca2035bfcdd2c8657cd6a7c7ec Mon Sep 17 00:00:00 2001 From: Cr0a3 Date: Thu, 31 Oct 2024 17:18:27 +0100 Subject: [PATCH] [FIX] fixes #45 #43 --- src/IR/parser/mod.rs | 19 +++++ src/IR/parser/semnatic.rs | 60 ++++++++++++++- .../Passes/DeadNodeElimination.rs | 76 +++++++++++-------- tests/Optimizations/dne/dne1.yl | 26 +++++++ tests/Optimizations/dne/dne2.yl | 16 ++++ tests/bugs/phi_non_recive.yl | 22 ++++++ tools/ytest/main.rs | 12 ++- 7 files changed, 199 insertions(+), 32 deletions(-) create mode 100644 tests/Optimizations/dne/dne1.yl create mode 100644 tests/Optimizations/dne/dne2.yl create mode 100644 tests/bugs/phi_non_recive.yl diff --git a/src/IR/parser/mod.rs b/src/IR/parser/mod.rs index 8be65dbb..abbf87d4 100644 --- a/src/IR/parser/mod.rs +++ b/src/IR/parser/mod.rs @@ -131,6 +131,14 @@ pub enum IrError { /// expected amount expected: usize, }, + + /// a block branches to a phi node, but the input of the block is not handled + PhiBranchNotHandled { + /// location + loc: Loc, + /// the branch name + branch: String, + } } impl Display for IrError { @@ -315,6 +323,17 @@ impl Display for IrError { fab.setCodeLine(loc.line_string.to_owned()); fab.addWhere(format!("to many arguments were supplyed - expected {expected}"), loc.coloumn, loc.length); + fab.to_string() + }, + + IrError::PhiBranchNotHandled { loc, branch } => { + let mut fab = Support::Error::new("unhandled phi branch", "", "", ""); + + fab.deactivateLocationDisplay(); + + fab.setCodeLine(loc.line_string.to_owned()); + fab.addWhere(format!("block {branch} can branch to the phi node but there is no specified input"), loc.coloumn, loc.length); + fab.to_string() } }) diff --git a/src/IR/parser/semnatic.rs b/src/IR/parser/semnatic.rs index dc05701a..42a1da20 100644 --- a/src/IR/parser/semnatic.rs +++ b/src/IR/parser/semnatic.rs @@ -113,6 +113,31 @@ impl<'a> IrSemnatic<'a> { let func = name; + // analyise block dependence + + let mut branches_to = HashMap::new(); // HashMap> + + for (name, block) in body { + for node in &block.body { + + let mut branches = Vec::new(); + + if let Some(br) = node.inst.as_any().downcast_ref::>() { + branches.push(br.inner1.to_owned()); + } else if let Some(br) = node.inst.as_any().downcast_ref::>() { + branches.push(br.inner2.to_owned()); + branches.push(br.inner3.to_owned()); + } else if let Some(switch) = node.inst.as_any().downcast_ref::() { + branches.push(switch.default.to_owned()); + for (_, case) in &switch.cases { + branches.push(case.to_owned()); + } + } + + branches_to.insert(name.to_owned(), branches); + } + } + for (name, block) in body { if blocks.contains(name) { Err(IrError::DefinedTwice { @@ -219,10 +244,43 @@ impl<'a> IrSemnatic<'a> { self.analaysiz_load(&mut vars, node, loc)?; } else if let Some(node) = any.downcast_ref::() { if vars.contains_key(&node.out.name) { - Err(IrError::DefinedTwice { loc: loc, name: node.out.name.to_owned() })? + Err(IrError::DefinedTwice { loc: loc.to_owned(), name: node.out.name.to_owned() })? } else { vars.insert(node.out.name.to_owned(), node.typ); } + + let mut handled = Vec::new(); + + for (branch, branches) in &branches_to { + if branches.contains(&BlockId(name.to_owned())) { + let mut recived = false; + + for recive in &node.recive_from_blocks { + if recive.0.name == branch.to_owned() { + handled.push(recive.0.name.clone()); + recived = true; + break; + } + } + + if !recived { + Err(IrError::PhiBranchNotHandled { + loc: loc.to_owned(), + branch: branch.to_owned(), + })? + } + } + } + + for (recive, _) in &node.recive_from_blocks { + if !handled.contains(&recive.name) { + Err(IrError::Unkown { + what: "block".into(), + name: recive.name.to_owned(), + loc: loc.to_owned() + })? + } + } } else if let Some(node) = any.downcast_ref::() { self.analyze_switch(func, &mut vars, node, loc)?; } else if let Some(node) = any.downcast_ref::>() { diff --git a/src/Optimizations/Passes/DeadNodeElimination.rs b/src/Optimizations/Passes/DeadNodeElimination.rs index 8fed54af..8659132f 100644 --- a/src/Optimizations/Passes/DeadNodeElimination.rs +++ b/src/Optimizations/Passes/DeadNodeElimination.rs @@ -1,4 +1,4 @@ -use crate::{prelude::Call, Optimizations::Pass, IR::{FuncId, Var}}; +use crate::{prelude::{Call, Phi}, Optimizations::Pass, IR::{FuncId, Var}}; /// ## Pass DeadNodeElimination
/// deletes unused nodes @@ -11,49 +11,65 @@ pub fn DeadNodeElimination() -> Box { impl Pass for DeadNodeElimination_ { fn run_func(&self, func: &mut crate::prelude::Function) { - let mut used: Vec = Vec::new(); + for _ in 0..2 { // iterate two times, cuz then we can remove dependants with a dept of 1 + let mut used: Vec = Vec::new(); - let mut to_remove = Vec::new(); + let mut to_remove = Vec::new(); - for block in func.blocks.iter().rev() { - let iter = block.nodes.iter(); - let iter = iter.rev(); + // first iterate over all phis - let mut index = iter.len() as i32; - - for node in iter { - let inputs = node.inputs(); - let out = node.output(); - - for input in inputs { - if !used.contains(&input.name) { - used.push(input.name); + for block in func.blocks.iter() { + for node in &block.nodes { + if let Some(phi) = node.as_any().downcast_ref::() { + for (_, reciver) in &phi.recive_from_blocks { + used.push(reciver.name.to_owned()); + } } } + } + + // now we can iterate over all normal nodes + + for block in func.blocks.iter().rev() { + let iter = block.nodes.iter(); + let iter = iter.rev(); - if let Some(out) = out { - if !used.contains(&out.name) { - if let Some(_) = node.as_any().downcast_ref::, Var>>() {} else { - // node isn't a call - to_remove.push((block.name.clone(), index - 1)); + let mut index = iter.len() as i32; + + for node in iter { + let inputs = node.inputs(); + let out = node.output(); + + for input in inputs { + if !used.contains(&input.name) { + used.push(input.name); } } + + if let Some(out) = out { + if !used.contains(&out.name) { + if let Some(_) = node.as_any().downcast_ref::, Var>>() {} else { + // node isn't a call + to_remove.push((block.name.clone(), index - 1)); + } + } + } + + index -= 1; } - - index -= 1; } - } - for block in &mut func.blocks { - let mut off = 0; + for block in &mut func.blocks { + let mut off = 0; - for (target_block, node) in &to_remove { - if target_block == &block.name { - block.nodes.remove((*node - off) as usize); + for (target_block, node) in &to_remove { + if target_block == &block.name { + block.nodes.remove((*node - off) as usize); - off += 1; + off += 1; + } } } - } + } } } \ No newline at end of file diff --git a/tests/Optimizations/dne/dne1.yl b/tests/Optimizations/dne/dne1.yl new file mode 100644 index 00000000..d2f495a8 --- /dev/null +++ b/tests/Optimizations/dne/dne1.yl @@ -0,0 +1,26 @@ +# RUN: +cargo run -p ylc -- -in=%s -passes=dne -fmt +# IN: + +define void @main() { + entry: + br loop + loop: + %0 = phi i64 [%1, loop] + %1 = sub i64 %0, 1 + br cond %0 end, loop + end: + ret void 0 +} + +# STDOUT: +define void @main() { + entry: + br loop + loop: + %0 = phi i64 [%1, loop] + %1 = sub i64 %0, 1 + br cond %0 end, loop + end: + ret void 0 +} \ No newline at end of file diff --git a/tests/Optimizations/dne/dne2.yl b/tests/Optimizations/dne/dne2.yl new file mode 100644 index 00000000..2b0f40b1 --- /dev/null +++ b/tests/Optimizations/dne/dne2.yl @@ -0,0 +1,16 @@ +# RUN: +cargo run -p ylc -- -in=%s -passes=dne -fmt +# IN: + +define i32 @main() { + entry: + %0 = i32 5 + %2 = add i32 %0, 3 + ret i32 4 +} + +# STDOUT: +define i32 @main() { + entry: + ret i32 4 +} \ No newline at end of file diff --git a/tests/bugs/phi_non_recive.yl b/tests/bugs/phi_non_recive.yl new file mode 100644 index 00000000..30107576 --- /dev/null +++ b/tests/bugs/phi_non_recive.yl @@ -0,0 +1,22 @@ +# RUN: +cargo run -p ylc -- -in=%s -fmt +# IN: + +define void @main() { + entry: + %tmp = i32 0 + br loop + + loop: + %0 = phi i64 [ %tmp, entry %1, loop ] + %1 = sub i64 %0, 1 + br cond %0 end, loop + + abc: + br loop + + end: + ret void 0 + +} +# EXPECT_FAIL \ No newline at end of file diff --git a/tools/ytest/main.rs b/tools/ytest/main.rs index b454e4aa..0e97f458 100644 --- a/tools/ytest/main.rs +++ b/tools/ytest/main.rs @@ -144,12 +144,17 @@ fn main() { match cmd.status() { Ok(status) => { + if parsed.ignore_fail { + code = status.code().expect("expected exit code") as u32; + continue; + } + if !status.success() { if let Some(exit_code) = status.code() { code = exit_code as u32; if let Some(expected_code) = parsed.expected_code { - if expected_code == 0 || (cli.opt("exit") && code == (-1i32 as u32)) && !parsed.ignore_fail { + if (expected_code == 0 || (cli.opt("exit")) && code == (-1i32 as u32)) && !parsed.ignore_fail { println!("{}: the programm didn't exit sucessfull with code {}", "Error".red().bold(), exit_code); if !cli.opt("no-exit") { exit(-1); @@ -209,4 +214,9 @@ fn main() { println!("expected exit code {} matched with found one {}", expected_code, code); } } + + if parsed.ignore_fail && code == 0 { // expected fail but it did not fail + println!("{}: expect fail", "Error".red().bold()); + exit(-1); + } } \ No newline at end of file