From ce7bb001da4125257c8c093555403ea345f533bc Mon Sep 17 00:00:00 2001 From: Jakub Doka Date: Sun, 27 Oct 2024 11:32:34 +0100 Subject: [PATCH] handling infinite loops properly --- Cargo.toml | 8 +- lang/Cargo.toml | 4 + lang/README.md | 17 ++ lang/src/fuzz.rs | 139 ++++++++++ lang/src/fuzz_main.rs | 3 + lang/src/lexer.rs | 1 + lang/src/lib.rs | 2 + lang/src/parser.rs | 14 +- lang/src/son.rs | 246 ++++++++---------- .../son_tests_exhaustive_loop_testing.txt | 69 ++--- ...son_tests_infinite_loop_after_peephole.txt | 9 + lang/tests/son_tests_string_flip.txt | 43 ++- 12 files changed, 358 insertions(+), 197 deletions(-) create mode 100644 lang/src/fuzz.rs create mode 100644 lang/src/fuzz_main.rs create mode 100644 lang/tests/son_tests_infinite_loop_after_peephole.txt diff --git a/Cargo.toml b/Cargo.toml index 5788270..50c8d8c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ hbjit = { path = "jit" } [profile.release] lto = true -#debug = true +debug = true #strip = true codegen-units = 1 panic = "abort" @@ -42,3 +42,9 @@ inherits = "dev" opt-level = "z" strip = "debuginfo" panic = "abort" + +[profile.fuzz] +inherits = "dev" +debug = true +opt-level = 3 +panic = "abort" diff --git a/lang/Cargo.toml b/lang/Cargo.toml index e80fb1b..b4be569 100644 --- a/lang/Cargo.toml +++ b/lang/Cargo.toml @@ -7,6 +7,10 @@ edition = "2021" name = "hbc" path = "src/main.rs" +[[bin]] +name = "fuzz" +path = "src/fuzz_main.rs" + [dependencies] hashbrown = { version = "0.15.0", default-features = false, features = ["raw-entry", "allocator-api2"] } hbbytecode = { workspace = true, features = ["disasm"] } diff --git a/lang/README.md b/lang/README.md index dfe7bf9..025e5b9 100644 --- a/lang/README.md +++ b/lang/README.md @@ -1011,6 +1011,11 @@ main := fn(): uint { return 6 } + infinite_loop() + return 0 +} + +infinite_loop := fn(): void { f := 0 loop { if f == 1 { @@ -1124,3 +1129,15 @@ main := fn(): uint { return 1 } ``` + +#### infinite_loop_after_peephole +```hb +main := fn(): uint { + n := 0 + f := 0 + loop if n != 0 break else { + f += 1 + } + return f +} +``` diff --git a/lang/src/fuzz.rs b/lang/src/fuzz.rs new file mode 100644 index 0000000..e753dae --- /dev/null +++ b/lang/src/fuzz.rs @@ -0,0 +1,139 @@ +use { + crate::{ + lexer::TokenKind, + parser, + son::{Codegen, CodegenCtx}, + }, + core::{fmt::Write, hash::BuildHasher, ops::Range}, + std::string::String, +}; + +#[derive(Default)] +struct Rand(pub u64); + +impl Rand { + pub fn next(&mut self) -> u64 { + self.0 = crate::FnvBuildHasher::default().hash_one(self.0); + self.0 + } + + pub fn range(&mut self, min: u64, max: u64) -> u64 { + self.next() % (max - min) + min + } + + fn bool(&mut self) -> bool { + self.next() % 2 == 0 + } +} + +#[derive(Default)] +struct FuncGen { + rand: Rand, + buf: String, + vars: u64, +} + +impl FuncGen { + fn gen(&mut self, seed: u64) -> &str { + self.rand = Rand(seed); + self.buf.clear(); + self.buf.push_str("main := fn(): void "); + self.block().unwrap(); + &self.buf + } + + fn block(&mut self) -> core::fmt::Result { + let prev_vars = self.vars; + self.buf.push('{'); + for _ in 0..self.rand.range(1, 10) { + self.stmt()?; + } + self.buf.push('}'); + self.vars = prev_vars; + + Ok(()) + } + + fn stmt(&mut self) -> core::fmt::Result { + match self.rand.range(0, 100) { + 0..4 => _ = self.block(), + 4..10 => { + write!(self.buf, "var{} := ", self.vars)?; + self.expr()?; + self.vars += 1; + } + + 10..20 if self.vars != 0 => { + write!(self.buf, "var{} = ", self.rand.range(0, self.vars))?; + self.expr()?; + } + 20..23 => { + self.buf.push_str("if "); + self.expr()?; + self.block()?; + if self.rand.bool() { + self.buf.push_str(" else "); + self.block()?; + } + } + _ => { + self.buf.push_str("return "); + self.expr()?; + } + } + + self.buf.push(';'); + Ok(()) + } + + fn expr(&mut self) -> core::fmt::Result { + match self.rand.range(0, 100) { + 0..80 => { + write!(self.buf, "{}", self.rand.next()) + } + 80..90 if self.vars != 0 => { + write!(self.buf, "var{}", self.rand.range(0, self.vars)) + } + 80..100 => { + self.expr()?; + let ops = [ + TokenKind::Add, + TokenKind::Sub, + TokenKind::Mul, + TokenKind::Div, + TokenKind::Shl, + TokenKind::Eq, + TokenKind::Ne, + TokenKind::Lt, + TokenKind::Gt, + TokenKind::Le, + TokenKind::Ge, + TokenKind::Band, + TokenKind::Bor, + TokenKind::Xor, + TokenKind::Mod, + TokenKind::Shr, + ]; + let op = ops[self.rand.range(0, ops.len() as u64) as usize]; + write!(self.buf, " {op} ")?; + self.expr() + } + _ => unreachable!(), + } + } +} + +pub fn fuzz(seed_range: Range) { + let mut gen = FuncGen::default(); + let mut ctx = CodegenCtx::default(); + for i in seed_range { + ctx.clear(); + let src = gen.gen(i); + let parsed = parser::Ast::new("fuzz", src, &mut ctx.parser, &mut parser::no_loader); + + assert!(ctx.parser.errors.get_mut().is_empty()); + + let mut cdg = Codegen::new(core::slice::from_ref(&parsed), &mut ctx); + cdg.generate(0); + } +} diff --git a/lang/src/fuzz_main.rs b/lang/src/fuzz_main.rs new file mode 100644 index 0000000..5122274 --- /dev/null +++ b/lang/src/fuzz_main.rs @@ -0,0 +1,3 @@ +fn main() { + hblang::fuzz::fuzz(0..1000000); +} diff --git a/lang/src/lexer.rs b/lang/src/lexer.rs index 1fbff44..cfec7ef 100644 --- a/lang/src/lexer.rs +++ b/lang/src/lexer.rs @@ -277,6 +277,7 @@ impl TokenKind { Self::Add => a.wrapping_add(b), Self::Sub => a.wrapping_sub(b), Self::Mul => a.wrapping_mul(b), + Self::Div if b == 0 => 0, Self::Div => a.wrapping_div(b), Self::Shl => a.wrapping_shl(b as _), Self::Eq => (a == b) as i64, diff --git a/lang/src/lib.rs b/lang/src/lib.rs index 49ffc80..bfdf4dd 100644 --- a/lang/src/lib.rs +++ b/lang/src/lib.rs @@ -75,6 +75,8 @@ pub mod lexer; #[cfg(feature = "opts")] mod vc; +pub mod fuzz; + mod debug { pub fn panicking() -> bool { diff --git a/lang/src/parser.rs b/lang/src/parser.rs index 3946dd1..84403dc 100644 --- a/lang/src/parser.rs +++ b/lang/src/parser.rs @@ -1147,8 +1147,18 @@ fn report_to(file: &str, path: &str, pos: Pos, msg: &dyn fmt::Display, out: &mut ..file[pos as usize..].find('\n').map_or(file.len(), |i| i + pos as usize)]; col += line.matches('\t').count() * 3; - _ = writeln!(out, "{}", line.replace("\t", " ")); - _ = writeln!(out, "{}^", " ".repeat(col - 1)); + for char in line.chars() { + if char == '\t' { + _ = out.write_str(" "); + } else { + _ = out.write_char(char); + } + } + _ = out.write_char('\n'); + for _ in 0..col - 1 { + _ = out.write_str(" "); + } + _ = out.write_str("^\n"); } #[derive(PartialEq, Eq, Hash)] diff --git a/lang/src/son.rs b/lang/src/son.rs index d6a08f1..cd7412f 100644 --- a/lang/src/son.rs +++ b/lang/src/son.rs @@ -35,6 +35,8 @@ const VOID: Nid = 0; const NEVER: Nid = 1; const ENTRY: Nid = 2; const MEM: Nid = 3; +const LOOPS: Nid = 4; +const ARG_START: usize = 3; type Nid = u16; @@ -154,23 +156,23 @@ impl Nodes { fn graphviz_in_browser(&self, tys: &Types, files: &[parser::Ast]) { #[cfg(all(debug_assertions, feature = "std"))] { - let out = &mut String::new(); - _ = self.graphviz_low(tys, files, out); - if !std::process::Command::new("brave") - .arg(format!("https://dreampuf.github.io/GraphvizOnline/#{out}")) - .status() - .unwrap() - .success() - { - log::error!("{out}"); - } + // let out = &mut String::new(); + // _ = self.graphviz_low(tys, files, out); + // if !std::process::Command::new("brave") + // .arg(format!("https://dreampuf.github.io/GraphvizOnline/#{out}")) + // .status() + // .unwrap() + // .success() + // { + // log::error!("{out}"); + // } } } fn gcm(&mut self) { + fix_loops(self); self.visited.clear(self.values.len()); push_up(self); - // TODO: handle infinte loops self.visited.clear(self.values.len()); push_down(self, VOID); } @@ -313,13 +315,12 @@ impl Nodes { None } - fn iter_peeps(&mut self, mut fuel: usize) { - self.lock(NEVER); + fn iter_peeps(&mut self, mut fuel: usize, stack: &mut Vec) { + stack.clear(); - let mut stack = self - .iter() + self.iter() .filter_map(|(id, node)| node.kind.is_peeped().then_some(id)) - .collect::>(); + .collect_into(stack); stack.iter().for_each(|&s| self.lock(s)); while fuel != 0 @@ -340,8 +341,6 @@ impl Nodes { stack.iter().skip(prev_len).for_each(|&n| self.lock(n)); } } - - self.unlock(NEVER); } fn peephole(&mut self, target: Nid) -> Option { @@ -524,6 +523,11 @@ impl Nodes { return Some(ctrl); } + K::Call { .. } | K::Return => { + if self[target].inputs[0] == NEVER { + return Some(NEVER); + } + } K::Phi => { let &[ctrl, lhs, rhs] = self[target].inputs.as_slice() else { unreachable!() }; @@ -667,7 +671,7 @@ impl Nodes { } match self[node].kind { Kind::Start => unreachable!(), - Kind::End => unreachable!(), + Kind::End => return Ok(()), Kind::If => write!(out, " if: "), Kind::Region | Kind::Loop => writeln!(out, " goto: {node}"), Kind::Return => write!(out, " ret: "), @@ -692,6 +696,7 @@ impl Nodes { Kind::Load => write!(out, "load: "), Kind::Stre => write!(out, "stre: "), Kind::Mem => write!(out, " mem: "), + Kind::Loops => write!(out, " loops: "), Kind::Extend => write!(out, " ext: "), }?; @@ -819,10 +824,16 @@ impl Nodes { log::error!("{} {} {:?}", node.lock_rc, 0, node.kind); failed = true; } - if !matches!(node.kind, Kind::End | Kind::Mem | Kind::Arg) && node.outputs.is_empty() { + if !matches!(node.kind, Kind::End | Kind::Mem | Kind::Arg | Kind::Loops) + && node.outputs.is_empty() + { log::error!("outputs are empry {id} {:?}", node.kind); failed = true; } + if node.inputs.first() == Some(&NEVER) && id != NEVER { + log::error!("is unreachable but still present {id} {:?}", node.kind); + failed = true; + } } if failed { @@ -1021,7 +1032,10 @@ pub enum Kind { Start, // [ctrl] Entry, + // [VOID] Mem, + // [VOID] + Loops, // [terms...] End, // [ctrl, cond] @@ -1070,7 +1084,7 @@ pub enum Kind { impl Kind { fn is_pinned(&self) -> bool { - self.is_cfg() || matches!(self, Self::Phi | Self::Arg | Self::Mem) + self.is_cfg() || matches!(self, Self::Phi | Self::Arg | Self::Mem | Self::Loops) } fn is_cfg(&self) -> bool { @@ -1140,7 +1154,7 @@ impl Node { fn is_not_gvnd(&self) -> bool { (self.kind == Kind::Phi && self.inputs[2] == 0) - || matches!(self.kind, Kind::Arg | Kind::Stck) + || matches!(self.kind, Kind::Arg | Kind::Stck | Kind::End) } fn is_mem(&self) -> bool { @@ -1368,16 +1382,20 @@ impl ItemCtx { let mem = self.nodes.new_node(ty::Id::VOID, Kind::Mem, [VOID]); debug_assert_eq!(mem, MEM); self.nodes.lock(mem); + let loops = self.nodes.new_node(ty::Id::VOID, Kind::Loops, [VOID]); + debug_assert_eq!(loops, LOOPS); + self.nodes.lock(loops); self.scope.store = Variable::new(0, ty::Id::VOID, false, MEM, &mut self.nodes); } - fn finalize(&mut self) { + fn finalize(&mut self, stack: &mut Vec) { self.scope.clear(&mut self.nodes); - self.nodes.unlock(NEVER); mem::take(&mut self.ctrl).soft_remove(&mut self.nodes); - self.nodes.unlock(MEM); self.nodes.eliminate_stack_temporaries(); - self.nodes.iter_peeps(1000); + self.nodes.iter_peeps(1000, stack); + self.nodes.unlock(MEM); + self.nodes.unlock(NEVER); + self.nodes.unlock(LOOPS); } fn emit(&mut self, instr: (usize, [u8; instrs::MAX_SIZE])) { @@ -1438,7 +1456,7 @@ impl ItemCtx { let (retl, mut parama) = tys.parama(sig.ret); let mut typs = sig.args.args(); - let mut args = fuc.nodes[VOID].outputs[2..].iter(); + let mut args = fuc.nodes[VOID].outputs[ARG_START..].iter(); while let Some(aty) = typs.next(tys) { let Arg::Value(ty) = aty else { continue }; let Some(loc) = parama.next(ty, tys) else { continue }; @@ -1710,6 +1728,7 @@ impl ItemCtx { | Kind::Entry | Kind::Mem | Kind::End + | Kind::Loops | Kind::Then | Kind::Else | Kind::Phi @@ -1852,6 +1871,7 @@ struct Pool { cis: Vec, used_cis: usize, ralloc: Regalloc, + nid_stack: Vec, } impl Pool { @@ -2840,7 +2860,7 @@ impl<'a> Codegen<'a> { let prev_file = mem::replace(&mut self.ci.file, file); self.ci.inline_depth += 1; - if self.expr(body).is_some() && sig.ret == ty::Id::VOID { + if self.expr(body).is_some() && sig.ret != ty::Id::VOID { self.report( body.pos(), "expected all paths in the fucntion to return \ @@ -3052,7 +3072,11 @@ impl<'a> Codegen<'a> { } Expr::Loop { body, .. } => { self.ci.ctrl.set( - self.ci.nodes.new_node(ty::Id::VOID, Kind::Loop, [self.ci.ctrl.get(); 2]), + self.ci.nodes.new_node(ty::Id::VOID, Kind::Loop, [ + self.ci.ctrl.get(), + self.ci.ctrl.get(), + LOOPS, + ]), &mut self.ci.nodes, ); self.ci.loops.push(Loop { @@ -3121,6 +3145,7 @@ impl<'a> Codegen<'a> { } scope.clear(&mut self.ci.nodes); self.ci.ctrl.set(NEVER, &mut self.ci.nodes); + return None; }; @@ -3182,9 +3207,9 @@ impl<'a> Codegen<'a> { self.ci.nodes.new_node(ty::Id::VOID, Kind::If, [self.ci.ctrl.get(), cnd.id]); 'b: { - let branch = match self.tof(if_node).expand().inner() { - ty::LEFT_UNREACHABLE => else_, - ty::RIGHT_UNREACHABLE => Some(then), + let branch = match self.ci.nodes[if_node].ty { + ty::Id::LEFT_UNREACHABLE => else_, + ty::Id::RIGHT_UNREACHABLE => Some(then), _ => break 'b, }; @@ -3471,11 +3496,6 @@ impl<'a> Codegen<'a> { None } - #[inline(always)] - fn tof(&self, id: Nid) -> ty::Id { - self.ci.nodes[id].ty - } - fn complete_call_graph(&mut self) -> bool { let prev_err_len = self.errors.borrow().len(); while self.ci.task_base < self.tys.tasks.len() @@ -3535,17 +3555,24 @@ impl<'a> Codegen<'a> { } } - if self.expr(body).is_some() && sig.ret == ty::Id::VOID { - self.report( - body.pos(), - "expected all paths in the fucntion to return \ - or the return type to be 'void'", - ); + if self.expr(body).is_some() { + if sig.ret == ty::Id::VOID { + self.expr(&Expr::Return { pos: body.pos(), val: None }); + } else { + self.report( + body.pos(), + fa!( + "expected all paths in the fucntion to return \ + or the return type to be 'void' (return type is '{}')", + self.ty_display(sig.ret), + ), + ); + } } self.ci.scope.vars.drain(..).for_each(|v| v.remove_ignore_arg(&mut self.ci.nodes)); - self.ci.finalize(); + self.ci.finalize(&mut self.pool.nid_stack); if self.errors.borrow().len() == prev_err_len { self.ci.emit_body(self.tys, self.files, sig, &mut self.pool.ralloc); @@ -3675,7 +3702,7 @@ impl TypeParser for Codegen<'_> { self.expr(&Expr::Return { pos: expr.pos(), val: Some(expr) }); scope = mem::take(&mut self.ci.scope.vars); - self.ci.finalize(); + self.ci.finalize(&mut self.pool.nid_stack); let res = if self.errors.borrow().len() == prev_err_len { self.emit_and_eval(file, ret, &mut []) @@ -3719,7 +3746,7 @@ impl TypeParser for Codegen<'_> { self.expr(&(Expr::Return { pos: expr.pos(), val: Some(expr) })); - self.ci.finalize(); + self.ci.finalize(&mut self.pool.nid_stack); let ret = self.ci.ret.expect("for return type to be infered"); if self.errors.borrow().len() == prev_err_len { @@ -3881,7 +3908,6 @@ impl<'a> Function<'a> { debug_assert_matches!(self.nodes[node.outputs[0]].kind, Kind::Entry); self.emit_node(node.outputs[0], VOID) } - Kind::End => {} Kind::If => { self.nodes[nid].ralloc_backref = self.nodes[prev].ralloc_backref; @@ -3974,7 +4000,7 @@ impl<'a> Function<'a> { let (ret, mut parama) = self.tys.parama(self.sig.ret); let mut typs = self.sig.args.args(); #[allow(clippy::unnecessary_to_owned)] - let mut args = self.nodes[VOID].outputs[2..].to_owned().into_iter(); + let mut args = self.nodes[VOID].outputs[ARG_START..].to_owned().into_iter(); while let Some(ty) = typs.next_value(self.tys) { let arg = args.next().unwrap(); match parama.next(ty, self.tys) { @@ -4136,7 +4162,8 @@ impl<'a> Function<'a> { let ops = vec![self.drg(nid)]; self.add_instr(nid, ops); } - Kind::Phi | Kind::Arg | Kind::Mem => {} + Kind::End | + Kind::Phi | Kind::Arg | Kind::Mem | Kind::Loops => {} Kind::Load { .. } if node.ty.loc(self.tys) == Loc::Stack => { self.nodes.lock(nid) } @@ -4414,6 +4441,27 @@ fn idepth(nodes: &mut Nodes, target: Nid) -> IDomDepth { nodes[target].depth } +fn fix_loops(nodes: &mut Nodes) { + 'o: for l in nodes[LOOPS].outputs.clone() { + let mut cursor = nodes[l].inputs[1]; + while cursor != l { + if nodes[cursor].kind == Kind::If + && nodes[cursor] + .outputs + .clone() + .into_iter() + .any(|b| loop_depth(b, nodes) < loop_depth(cursor, nodes)) + { + continue 'o; + } + cursor = idom(nodes, cursor); + } + + nodes[l].outputs.push(NEVER); + nodes[NEVER].inputs.push(l); + } +} + fn push_up(nodes: &mut Nodes) { fn collect_rpo(node: Nid, nodes: &mut Nodes, rpo: &mut Vec) { if !nodes.is_cfg(node) || !nodes.visited.set(node) { @@ -4491,13 +4539,15 @@ fn push_up(nodes: &mut Nodes) { nodes .iter() .map(|(n, _)| n) - .filter(|&n| !nodes.visited.get(n) && !matches!(nodes[n].kind, Kind::Arg | Kind::Mem)) + .filter(|&n| !nodes.visited.get(n) + && !matches!(nodes[n].kind, Kind::Arg | Kind::Mem | Kind::Loops)) .collect::>(), vec![], "{:?}", nodes .iter() - .filter(|&(n, nod)| !nodes.visited.get(n) && !matches!(nod.kind, Kind::Arg | Kind::Mem)) + .filter(|&(n, nod)| !nodes.visited.get(n) + && !matches!(nod.kind, Kind::Arg | Kind::Mem | Kind::Loops)) .collect::>() ); } @@ -4609,100 +4659,11 @@ fn common_dom(mut a: Nid, mut b: Nid, nodes: &mut Nodes) -> Nid { #[cfg(test)] mod tests { use { - super::{Codegen, CodegenCtx}, - crate::{ - lexer::TokenKind, - parser::{self}, - }, + super::CodegenCtx, alloc::{string::String, vec::Vec}, - core::{fmt::Write, hash::BuildHasher, ops::Range}, + core::fmt::Write, }; - #[derive(Default)] - struct Rand(pub u64); - - impl Rand { - pub fn next(&mut self) -> u64 { - self.0 = crate::FnvBuildHasher::default().hash_one(self.0); - self.0 - } - - pub fn range(&mut self, min: u64, max: u64) -> u64 { - self.next() % (max - min) + min - } - } - - #[derive(Default)] - struct FuncGen { - rand: Rand, - buf: String, - } - - impl FuncGen { - fn gen(&mut self, seed: u64) -> &str { - self.rand = Rand(seed); - self.buf.clear(); - self.buf.push_str("main := fn(): void { return "); - self.expr().unwrap(); - self.buf.push('}'); - &self.buf - } - - fn expr(&mut self) -> core::fmt::Result { - match self.rand.range(0, 100) { - 0..80 => { - write!(self.buf, "{}", self.rand.next()) - } - 80..100 => { - self.expr()?; - let ops = [ - TokenKind::Add, - TokenKind::Sub, - TokenKind::Mul, - TokenKind::Div, - TokenKind::Shl, - TokenKind::Eq, - TokenKind::Ne, - TokenKind::Lt, - TokenKind::Gt, - TokenKind::Le, - TokenKind::Ge, - TokenKind::Band, - TokenKind::Bor, - TokenKind::Xor, - TokenKind::Mod, - TokenKind::Shr, - ]; - let op = ops[self.rand.range(0, ops.len() as u64) as usize]; - write!(self.buf, " {op} ")?; - self.expr() - } - _ => unreachable!(), - } - } - } - - fn fuzz(seed_range: Range) { - let mut gen = FuncGen::default(); - let mut ctx = CodegenCtx::default(); - for i in seed_range { - ctx.clear(); - let src = gen.gen(i); - let parsed = parser::Ast::new("fuzz", src, &mut ctx.parser, &mut parser::no_loader); - - let mut cdg = Codegen::new(core::slice::from_ref(&parsed), &mut ctx); - cdg.generate(0); - } - } - - #[test] - #[ignore] - fn fuzz_test() { - _ = log::set_logger(&crate::fs::Logger); - log::set_max_level(log::LevelFilter::Info); - fuzz(0..10000); - } - fn generate(ident: &'static str, input: &'static str, output: &mut String) { _ = log::set_logger(&crate::fs::Logger); log::set_max_level(log::LevelFilter::Info); @@ -4795,5 +4756,6 @@ mod tests { conditional_stores; loop_stores; dead_code_in_loop; + infinite_loop_after_peephole; } } diff --git a/lang/tests/son_tests_exhaustive_loop_testing.txt b/lang/tests/son_tests_exhaustive_loop_testing.txt index 93f5274..e113d00 100644 --- a/lang/tests/son_tests_exhaustive_loop_testing.txt +++ b/lang/tests/son_tests_exhaustive_loop_testing.txt @@ -15,56 +15,65 @@ continue_and_state_change: 4: ADDI64 r2, r2, 1d 3: JMP :6 5: JALA r0, r31, 0a +infinite_loop: + ADDI64 r254, r254, -24d + ST r31, r254, 0a, 24h + LI64 r32, 1d + LI64 r33, 0d + CP r1, r33 + 1: JNE r1, r32, :0 + JMP :0 + 0: CP r2, r33 + JAL r31, r0, :continue_and_state_change + JMP :1 + LD r31, r254, 0a, 24h + ADDI64 r254, r254, 24d + JALA r0, r31, 0a main: - ADDI64 r254, r254, -64d - ST r31, r254, 0a, 64h + ADDI64 r254, r254, -56d + ST r31, r254, 0a, 56h LI64 r32, 0d CP r2, r32 JAL r31, r0, :multiple_breaks - LI64 r6, 1d - LI64 r7, 3d - JEQ r1, r7, :0 - CP r1, r6 + CP r2, r1 + LI64 r1, 3d + JEQ r2, r1, :0 + LI64 r1, 1d JMP :1 - 0: CP r33, r6 - CP r34, r7 - LI64 r35, 4d - CP r2, r35 + 0: CP r33, r1 + LI64 r34, 4d + CP r2, r34 JAL r31, r0, :multiple_breaks - CP r36, r35 - LI64 r37, 10d - JEQ r1, r37, :2 + CP r35, r34 + LI64 r36, 10d + JEQ r1, r36, :2 LI64 r1, 2d JMP :1 2: CP r2, r32 JAL r31, r0, :state_change_in_break JEQ r1, r32, :3 - CP r1, r34 + CP r1, r33 JMP :1 - 3: CP r2, r36 + 3: CP r2, r35 JAL r31, r0, :state_change_in_break - JEQ r1, r37, :4 - CP r1, r36 + JEQ r1, r36, :4 + CP r1, r35 JMP :1 - 4: CP r2, r37 + 4: CP r2, r36 JAL r31, r0, :continue_and_state_change - JEQ r1, r37, :5 + JEQ r1, r36, :5 LI64 r1, 5d JMP :1 - 5: CP r2, r34 + 5: CP r2, r33 JAL r31, r0, :continue_and_state_change JEQ r1, r32, :6 LI64 r1, 6d JMP :1 - 6: CP r1, r32 - CP r38, r33 - 8: JNE r1, r38, :7 - JMP :7 - 7: CP r2, r32 - JAL r31, r0, :continue_and_state_change - JMP :8 - 1: LD r31, r254, 0a, 64h - ADDI64 r254, r254, 64d + 6: CP r37, r32 + JAL r31, r0, :infinite_loop + CP r1, r37 + 1: LD r31, r254, 0a, 56h + ADDI64 r254, r254, 56d JALA r0, r31, 0a multiple_breaks: LI64 r6, 3d @@ -91,6 +100,6 @@ state_change_in_break: JMP :4 3: JALA r0, r31, 0a timed out -code size: 582 +code size: 668 ret: 10 status: Ok(()) diff --git a/lang/tests/son_tests_infinite_loop_after_peephole.txt b/lang/tests/son_tests_infinite_loop_after_peephole.txt new file mode 100644 index 0000000..e7d94df --- /dev/null +++ b/lang/tests/son_tests_infinite_loop_after_peephole.txt @@ -0,0 +1,9 @@ +main: + LI64 r2, 0d + 0: ADDI64 r2, r2, 1d + JMP :0 + JALA r0, r31, 0a +timed out +code size: 45 +ret: 0 +status: Ok(()) diff --git a/lang/tests/son_tests_string_flip.txt b/lang/tests/son_tests_string_flip.txt index e836521..18bd04d 100644 --- a/lang/tests/son_tests_string_flip.txt +++ b/lang/tests/son_tests_string_flip.txt @@ -7,30 +7,29 @@ main: CP r9, r4 6: JNE r9, r6, :0 LI64 r6, 2d - CP r7, r4 - 4: JNE r7, r8, :1 + ADDI64 r7, r254, 32d + CP r9, r4 + 4: JNE r9, r8, :1 LD r1, r254, 0a, 8h JMP :2 - 1: CP r10, r4 - 5: ADD64 r9, r7, r8 - JNE r10, r6, :3 - CP r7, r9 + 1: MUL64 r12, r9, r6 + ADD64 r9, r9, r8 + SUB64 r10, r6, r9 + MUL64 r10, r10, r6 + CP r3, r4 + 5: JNE r3, r6, :3 JMP :4 - 3: ADD64 r3, r10, r8 - MUL64 r12, r7, r6 - SUB64 r11, r6, r9 - ADD64 r9, r12, r10 - MUL64 r11, r11, r6 - MULI64 r9, r9, 8d - ADD64 r11, r11, r10 - ADD64 r9, r5, r9 - MULI64 r11, r11, 8d - ADDI64 r10, r254, 32d - ADD64 r11, r5, r11 - BMC r9, r10, 8h - BMC r11, r9, 8h - BMC r10, r11, 8h - CP r10, r3 + 3: ADD64 r11, r3, r8 + ADD64 r1, r12, r3 + MULI64 r1, r1, 8d + ADD64 r2, r10, r3 + ADD64 r1, r5, r1 + MULI64 r2, r2, 8d + ADD64 r2, r5, r2 + BMC r1, r7, 8h + BMC r2, r1, 8h + BMC r7, r2, 8h + CP r3, r11 JMP :5 0: ADD64 r2, r9, r8 MULI64 r12, r9, 8d @@ -40,6 +39,6 @@ main: JMP :6 2: ADDI64 r254, r254, 40d JALA r0, r31, 0a -code size: 274 +code size: 271 ret: 2 status: Ok(())