forked from AbleOS/holey-bytes
handling infinite loops properly
This commit is contained in:
parent
9c90adbfe8
commit
ce7bb001da
|
@ -23,7 +23,7 @@ hbjit = { path = "jit" }
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = true
|
lto = true
|
||||||
#debug = true
|
debug = true
|
||||||
#strip = true
|
#strip = true
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
panic = "abort"
|
panic = "abort"
|
||||||
|
@ -42,3 +42,9 @@ inherits = "dev"
|
||||||
opt-level = "z"
|
opt-level = "z"
|
||||||
strip = "debuginfo"
|
strip = "debuginfo"
|
||||||
panic = "abort"
|
panic = "abort"
|
||||||
|
|
||||||
|
[profile.fuzz]
|
||||||
|
inherits = "dev"
|
||||||
|
debug = true
|
||||||
|
opt-level = 3
|
||||||
|
panic = "abort"
|
||||||
|
|
|
@ -7,6 +7,10 @@ edition = "2021"
|
||||||
name = "hbc"
|
name = "hbc"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "fuzz"
|
||||||
|
path = "src/fuzz_main.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
hashbrown = { version = "0.15.0", default-features = false, features = ["raw-entry", "allocator-api2"] }
|
hashbrown = { version = "0.15.0", default-features = false, features = ["raw-entry", "allocator-api2"] }
|
||||||
hbbytecode = { workspace = true, features = ["disasm"] }
|
hbbytecode = { workspace = true, features = ["disasm"] }
|
||||||
|
|
|
@ -1011,6 +1011,11 @@ main := fn(): uint {
|
||||||
return 6
|
return 6
|
||||||
}
|
}
|
||||||
|
|
||||||
|
infinite_loop()
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
infinite_loop := fn(): void {
|
||||||
f := 0
|
f := 0
|
||||||
loop {
|
loop {
|
||||||
if f == 1 {
|
if f == 1 {
|
||||||
|
@ -1124,3 +1129,15 @@ main := fn(): uint {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### infinite_loop_after_peephole
|
||||||
|
```hb
|
||||||
|
main := fn(): uint {
|
||||||
|
n := 0
|
||||||
|
f := 0
|
||||||
|
loop if n != 0 break else {
|
||||||
|
f += 1
|
||||||
|
}
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
139
lang/src/fuzz.rs
Normal file
139
lang/src/fuzz.rs
Normal file
|
@ -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<u64>) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
3
lang/src/fuzz_main.rs
Normal file
3
lang/src/fuzz_main.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
fn main() {
|
||||||
|
hblang::fuzz::fuzz(0..1000000);
|
||||||
|
}
|
|
@ -277,6 +277,7 @@ impl TokenKind {
|
||||||
Self::Add => a.wrapping_add(b),
|
Self::Add => a.wrapping_add(b),
|
||||||
Self::Sub => a.wrapping_sub(b),
|
Self::Sub => a.wrapping_sub(b),
|
||||||
Self::Mul => a.wrapping_mul(b),
|
Self::Mul => a.wrapping_mul(b),
|
||||||
|
Self::Div if b == 0 => 0,
|
||||||
Self::Div => a.wrapping_div(b),
|
Self::Div => a.wrapping_div(b),
|
||||||
Self::Shl => a.wrapping_shl(b as _),
|
Self::Shl => a.wrapping_shl(b as _),
|
||||||
Self::Eq => (a == b) as i64,
|
Self::Eq => (a == b) as i64,
|
||||||
|
|
|
@ -75,6 +75,8 @@ pub mod lexer;
|
||||||
#[cfg(feature = "opts")]
|
#[cfg(feature = "opts")]
|
||||||
mod vc;
|
mod vc;
|
||||||
|
|
||||||
|
pub mod fuzz;
|
||||||
|
|
||||||
mod debug {
|
mod debug {
|
||||||
|
|
||||||
pub fn panicking() -> bool {
|
pub fn panicking() -> bool {
|
||||||
|
|
|
@ -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)];
|
..file[pos as usize..].find('\n').map_or(file.len(), |i| i + pos as usize)];
|
||||||
col += line.matches('\t').count() * 3;
|
col += line.matches('\t').count() * 3;
|
||||||
|
|
||||||
_ = writeln!(out, "{}", line.replace("\t", " "));
|
for char in line.chars() {
|
||||||
_ = writeln!(out, "{}^", " ".repeat(col - 1));
|
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)]
|
#[derive(PartialEq, Eq, Hash)]
|
||||||
|
|
246
lang/src/son.rs
246
lang/src/son.rs
|
@ -35,6 +35,8 @@ const VOID: Nid = 0;
|
||||||
const NEVER: Nid = 1;
|
const NEVER: Nid = 1;
|
||||||
const ENTRY: Nid = 2;
|
const ENTRY: Nid = 2;
|
||||||
const MEM: Nid = 3;
|
const MEM: Nid = 3;
|
||||||
|
const LOOPS: Nid = 4;
|
||||||
|
const ARG_START: usize = 3;
|
||||||
|
|
||||||
type Nid = u16;
|
type Nid = u16;
|
||||||
|
|
||||||
|
@ -154,23 +156,23 @@ impl Nodes {
|
||||||
fn graphviz_in_browser(&self, tys: &Types, files: &[parser::Ast]) {
|
fn graphviz_in_browser(&self, tys: &Types, files: &[parser::Ast]) {
|
||||||
#[cfg(all(debug_assertions, feature = "std"))]
|
#[cfg(all(debug_assertions, feature = "std"))]
|
||||||
{
|
{
|
||||||
let out = &mut String::new();
|
// let out = &mut String::new();
|
||||||
_ = self.graphviz_low(tys, files, out);
|
// _ = self.graphviz_low(tys, files, out);
|
||||||
if !std::process::Command::new("brave")
|
// if !std::process::Command::new("brave")
|
||||||
.arg(format!("https://dreampuf.github.io/GraphvizOnline/#{out}"))
|
// .arg(format!("https://dreampuf.github.io/GraphvizOnline/#{out}"))
|
||||||
.status()
|
// .status()
|
||||||
.unwrap()
|
// .unwrap()
|
||||||
.success()
|
// .success()
|
||||||
{
|
// {
|
||||||
log::error!("{out}");
|
// log::error!("{out}");
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gcm(&mut self) {
|
fn gcm(&mut self) {
|
||||||
|
fix_loops(self);
|
||||||
self.visited.clear(self.values.len());
|
self.visited.clear(self.values.len());
|
||||||
push_up(self);
|
push_up(self);
|
||||||
// TODO: handle infinte loops
|
|
||||||
self.visited.clear(self.values.len());
|
self.visited.clear(self.values.len());
|
||||||
push_down(self, VOID);
|
push_down(self, VOID);
|
||||||
}
|
}
|
||||||
|
@ -313,13 +315,12 @@ impl Nodes {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn iter_peeps(&mut self, mut fuel: usize) {
|
fn iter_peeps(&mut self, mut fuel: usize, stack: &mut Vec<Nid>) {
|
||||||
self.lock(NEVER);
|
stack.clear();
|
||||||
|
|
||||||
let mut stack = self
|
self.iter()
|
||||||
.iter()
|
|
||||||
.filter_map(|(id, node)| node.kind.is_peeped().then_some(id))
|
.filter_map(|(id, node)| node.kind.is_peeped().then_some(id))
|
||||||
.collect::<Vec<_>>();
|
.collect_into(stack);
|
||||||
stack.iter().for_each(|&s| self.lock(s));
|
stack.iter().for_each(|&s| self.lock(s));
|
||||||
|
|
||||||
while fuel != 0
|
while fuel != 0
|
||||||
|
@ -340,8 +341,6 @@ impl Nodes {
|
||||||
stack.iter().skip(prev_len).for_each(|&n| self.lock(n));
|
stack.iter().skip(prev_len).for_each(|&n| self.lock(n));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.unlock(NEVER);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn peephole(&mut self, target: Nid) -> Option<Nid> {
|
fn peephole(&mut self, target: Nid) -> Option<Nid> {
|
||||||
|
@ -524,6 +523,11 @@ impl Nodes {
|
||||||
|
|
||||||
return Some(ctrl);
|
return Some(ctrl);
|
||||||
}
|
}
|
||||||
|
K::Call { .. } | K::Return => {
|
||||||
|
if self[target].inputs[0] == NEVER {
|
||||||
|
return Some(NEVER);
|
||||||
|
}
|
||||||
|
}
|
||||||
K::Phi => {
|
K::Phi => {
|
||||||
let &[ctrl, lhs, rhs] = self[target].inputs.as_slice() else { unreachable!() };
|
let &[ctrl, lhs, rhs] = self[target].inputs.as_slice() else { unreachable!() };
|
||||||
|
|
||||||
|
@ -667,7 +671,7 @@ impl Nodes {
|
||||||
}
|
}
|
||||||
match self[node].kind {
|
match self[node].kind {
|
||||||
Kind::Start => unreachable!(),
|
Kind::Start => unreachable!(),
|
||||||
Kind::End => unreachable!(),
|
Kind::End => return Ok(()),
|
||||||
Kind::If => write!(out, " if: "),
|
Kind::If => write!(out, " if: "),
|
||||||
Kind::Region | Kind::Loop => writeln!(out, " goto: {node}"),
|
Kind::Region | Kind::Loop => writeln!(out, " goto: {node}"),
|
||||||
Kind::Return => write!(out, " ret: "),
|
Kind::Return => write!(out, " ret: "),
|
||||||
|
@ -692,6 +696,7 @@ impl Nodes {
|
||||||
Kind::Load => write!(out, "load: "),
|
Kind::Load => write!(out, "load: "),
|
||||||
Kind::Stre => write!(out, "stre: "),
|
Kind::Stre => write!(out, "stre: "),
|
||||||
Kind::Mem => write!(out, " mem: "),
|
Kind::Mem => write!(out, " mem: "),
|
||||||
|
Kind::Loops => write!(out, " loops: "),
|
||||||
Kind::Extend => write!(out, " ext: "),
|
Kind::Extend => write!(out, " ext: "),
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
|
@ -819,10 +824,16 @@ impl Nodes {
|
||||||
log::error!("{} {} {:?}", node.lock_rc, 0, node.kind);
|
log::error!("{} {} {:?}", node.lock_rc, 0, node.kind);
|
||||||
failed = true;
|
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);
|
log::error!("outputs are empry {id} {:?}", node.kind);
|
||||||
failed = true;
|
failed = true;
|
||||||
}
|
}
|
||||||
|
if node.inputs.first() == Some(&NEVER) && id != NEVER {
|
||||||
|
log::error!("is unreachable but still present {id} {:?}", node.kind);
|
||||||
|
failed = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if failed {
|
if failed {
|
||||||
|
@ -1021,7 +1032,10 @@ pub enum Kind {
|
||||||
Start,
|
Start,
|
||||||
// [ctrl]
|
// [ctrl]
|
||||||
Entry,
|
Entry,
|
||||||
|
// [VOID]
|
||||||
Mem,
|
Mem,
|
||||||
|
// [VOID]
|
||||||
|
Loops,
|
||||||
// [terms...]
|
// [terms...]
|
||||||
End,
|
End,
|
||||||
// [ctrl, cond]
|
// [ctrl, cond]
|
||||||
|
@ -1070,7 +1084,7 @@ pub enum Kind {
|
||||||
|
|
||||||
impl Kind {
|
impl Kind {
|
||||||
fn is_pinned(&self) -> bool {
|
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 {
|
fn is_cfg(&self) -> bool {
|
||||||
|
@ -1140,7 +1154,7 @@ impl Node {
|
||||||
|
|
||||||
fn is_not_gvnd(&self) -> bool {
|
fn is_not_gvnd(&self) -> bool {
|
||||||
(self.kind == Kind::Phi && self.inputs[2] == 0)
|
(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 {
|
fn is_mem(&self) -> bool {
|
||||||
|
@ -1368,16 +1382,20 @@ impl ItemCtx {
|
||||||
let mem = self.nodes.new_node(ty::Id::VOID, Kind::Mem, [VOID]);
|
let mem = self.nodes.new_node(ty::Id::VOID, Kind::Mem, [VOID]);
|
||||||
debug_assert_eq!(mem, MEM);
|
debug_assert_eq!(mem, MEM);
|
||||||
self.nodes.lock(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);
|
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<Nid>) {
|
||||||
self.scope.clear(&mut self.nodes);
|
self.scope.clear(&mut self.nodes);
|
||||||
self.nodes.unlock(NEVER);
|
|
||||||
mem::take(&mut self.ctrl).soft_remove(&mut self.nodes);
|
mem::take(&mut self.ctrl).soft_remove(&mut self.nodes);
|
||||||
self.nodes.unlock(MEM);
|
|
||||||
self.nodes.eliminate_stack_temporaries();
|
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])) {
|
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 (retl, mut parama) = tys.parama(sig.ret);
|
||||||
let mut typs = sig.args.args();
|
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) {
|
while let Some(aty) = typs.next(tys) {
|
||||||
let Arg::Value(ty) = aty else { continue };
|
let Arg::Value(ty) = aty else { continue };
|
||||||
let Some(loc) = parama.next(ty, tys) else { continue };
|
let Some(loc) = parama.next(ty, tys) else { continue };
|
||||||
|
@ -1710,6 +1728,7 @@ impl ItemCtx {
|
||||||
| Kind::Entry
|
| Kind::Entry
|
||||||
| Kind::Mem
|
| Kind::Mem
|
||||||
| Kind::End
|
| Kind::End
|
||||||
|
| Kind::Loops
|
||||||
| Kind::Then
|
| Kind::Then
|
||||||
| Kind::Else
|
| Kind::Else
|
||||||
| Kind::Phi
|
| Kind::Phi
|
||||||
|
@ -1852,6 +1871,7 @@ struct Pool {
|
||||||
cis: Vec<ItemCtx>,
|
cis: Vec<ItemCtx>,
|
||||||
used_cis: usize,
|
used_cis: usize,
|
||||||
ralloc: Regalloc,
|
ralloc: Regalloc,
|
||||||
|
nid_stack: Vec<Nid>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pool {
|
impl Pool {
|
||||||
|
@ -2840,7 +2860,7 @@ impl<'a> Codegen<'a> {
|
||||||
let prev_file = mem::replace(&mut self.ci.file, file);
|
let prev_file = mem::replace(&mut self.ci.file, file);
|
||||||
self.ci.inline_depth += 1;
|
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(
|
self.report(
|
||||||
body.pos(),
|
body.pos(),
|
||||||
"expected all paths in the fucntion to return \
|
"expected all paths in the fucntion to return \
|
||||||
|
@ -3052,7 +3072,11 @@ impl<'a> Codegen<'a> {
|
||||||
}
|
}
|
||||||
Expr::Loop { body, .. } => {
|
Expr::Loop { body, .. } => {
|
||||||
self.ci.ctrl.set(
|
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,
|
&mut self.ci.nodes,
|
||||||
);
|
);
|
||||||
self.ci.loops.push(Loop {
|
self.ci.loops.push(Loop {
|
||||||
|
@ -3121,6 +3145,7 @@ impl<'a> Codegen<'a> {
|
||||||
}
|
}
|
||||||
scope.clear(&mut self.ci.nodes);
|
scope.clear(&mut self.ci.nodes);
|
||||||
self.ci.ctrl.set(NEVER, &mut self.ci.nodes);
|
self.ci.ctrl.set(NEVER, &mut self.ci.nodes);
|
||||||
|
|
||||||
return None;
|
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]);
|
self.ci.nodes.new_node(ty::Id::VOID, Kind::If, [self.ci.ctrl.get(), cnd.id]);
|
||||||
|
|
||||||
'b: {
|
'b: {
|
||||||
let branch = match self.tof(if_node).expand().inner() {
|
let branch = match self.ci.nodes[if_node].ty {
|
||||||
ty::LEFT_UNREACHABLE => else_,
|
ty::Id::LEFT_UNREACHABLE => else_,
|
||||||
ty::RIGHT_UNREACHABLE => Some(then),
|
ty::Id::RIGHT_UNREACHABLE => Some(then),
|
||||||
_ => break 'b,
|
_ => break 'b,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3471,11 +3496,6 @@ impl<'a> Codegen<'a> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn tof(&self, id: Nid) -> ty::Id {
|
|
||||||
self.ci.nodes[id].ty
|
|
||||||
}
|
|
||||||
|
|
||||||
fn complete_call_graph(&mut self) -> bool {
|
fn complete_call_graph(&mut self) -> bool {
|
||||||
let prev_err_len = self.errors.borrow().len();
|
let prev_err_len = self.errors.borrow().len();
|
||||||
while self.ci.task_base < self.tys.tasks.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 {
|
if self.expr(body).is_some() {
|
||||||
self.report(
|
if sig.ret == ty::Id::VOID {
|
||||||
body.pos(),
|
self.expr(&Expr::Return { pos: body.pos(), val: None });
|
||||||
"expected all paths in the fucntion to return \
|
} else {
|
||||||
or the return type to be 'void'",
|
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.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 {
|
if self.errors.borrow().len() == prev_err_len {
|
||||||
self.ci.emit_body(self.tys, self.files, sig, &mut self.pool.ralloc);
|
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) });
|
self.expr(&Expr::Return { pos: expr.pos(), val: Some(expr) });
|
||||||
|
|
||||||
scope = mem::take(&mut self.ci.scope.vars);
|
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 {
|
let res = if self.errors.borrow().len() == prev_err_len {
|
||||||
self.emit_and_eval(file, ret, &mut [])
|
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.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");
|
let ret = self.ci.ret.expect("for return type to be infered");
|
||||||
if self.errors.borrow().len() == prev_err_len {
|
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);
|
debug_assert_matches!(self.nodes[node.outputs[0]].kind, Kind::Entry);
|
||||||
self.emit_node(node.outputs[0], VOID)
|
self.emit_node(node.outputs[0], VOID)
|
||||||
}
|
}
|
||||||
Kind::End => {}
|
|
||||||
Kind::If => {
|
Kind::If => {
|
||||||
self.nodes[nid].ralloc_backref = self.nodes[prev].ralloc_backref;
|
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 (ret, mut parama) = self.tys.parama(self.sig.ret);
|
||||||
let mut typs = self.sig.args.args();
|
let mut typs = self.sig.args.args();
|
||||||
#[allow(clippy::unnecessary_to_owned)]
|
#[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) {
|
while let Some(ty) = typs.next_value(self.tys) {
|
||||||
let arg = args.next().unwrap();
|
let arg = args.next().unwrap();
|
||||||
match parama.next(ty, self.tys) {
|
match parama.next(ty, self.tys) {
|
||||||
|
@ -4136,7 +4162,8 @@ impl<'a> Function<'a> {
|
||||||
let ops = vec![self.drg(nid)];
|
let ops = vec![self.drg(nid)];
|
||||||
self.add_instr(nid, ops);
|
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 => {
|
Kind::Load { .. } if node.ty.loc(self.tys) == Loc::Stack => {
|
||||||
self.nodes.lock(nid)
|
self.nodes.lock(nid)
|
||||||
}
|
}
|
||||||
|
@ -4414,6 +4441,27 @@ fn idepth(nodes: &mut Nodes, target: Nid) -> IDomDepth {
|
||||||
nodes[target].depth
|
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 push_up(nodes: &mut Nodes) {
|
||||||
fn collect_rpo(node: Nid, nodes: &mut Nodes, rpo: &mut Vec<Nid>) {
|
fn collect_rpo(node: Nid, nodes: &mut Nodes, rpo: &mut Vec<Nid>) {
|
||||||
if !nodes.is_cfg(node) || !nodes.visited.set(node) {
|
if !nodes.is_cfg(node) || !nodes.visited.set(node) {
|
||||||
|
@ -4491,13 +4539,15 @@ fn push_up(nodes: &mut Nodes) {
|
||||||
nodes
|
nodes
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(n, _)| n)
|
.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<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
vec![],
|
vec![],
|
||||||
"{:?}",
|
"{:?}",
|
||||||
nodes
|
nodes
|
||||||
.iter()
|
.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::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -4609,100 +4659,11 @@ fn common_dom(mut a: Nid, mut b: Nid, nodes: &mut Nodes) -> Nid {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use {
|
use {
|
||||||
super::{Codegen, CodegenCtx},
|
super::CodegenCtx,
|
||||||
crate::{
|
|
||||||
lexer::TokenKind,
|
|
||||||
parser::{self},
|
|
||||||
},
|
|
||||||
alloc::{string::String, vec::Vec},
|
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<u64>) {
|
|
||||||
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) {
|
fn generate(ident: &'static str, input: &'static str, output: &mut String) {
|
||||||
_ = log::set_logger(&crate::fs::Logger);
|
_ = log::set_logger(&crate::fs::Logger);
|
||||||
log::set_max_level(log::LevelFilter::Info);
|
log::set_max_level(log::LevelFilter::Info);
|
||||||
|
@ -4795,5 +4756,6 @@ mod tests {
|
||||||
conditional_stores;
|
conditional_stores;
|
||||||
loop_stores;
|
loop_stores;
|
||||||
dead_code_in_loop;
|
dead_code_in_loop;
|
||||||
|
infinite_loop_after_peephole;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,56 +15,65 @@ continue_and_state_change:
|
||||||
4: ADDI64 r2, r2, 1d
|
4: ADDI64 r2, r2, 1d
|
||||||
3: JMP :6
|
3: JMP :6
|
||||||
5: JALA r0, r31, 0a
|
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:
|
main:
|
||||||
ADDI64 r254, r254, -64d
|
ADDI64 r254, r254, -56d
|
||||||
ST r31, r254, 0a, 64h
|
ST r31, r254, 0a, 56h
|
||||||
LI64 r32, 0d
|
LI64 r32, 0d
|
||||||
CP r2, r32
|
CP r2, r32
|
||||||
JAL r31, r0, :multiple_breaks
|
JAL r31, r0, :multiple_breaks
|
||||||
LI64 r6, 1d
|
CP r2, r1
|
||||||
LI64 r7, 3d
|
LI64 r1, 3d
|
||||||
JEQ r1, r7, :0
|
JEQ r2, r1, :0
|
||||||
CP r1, r6
|
LI64 r1, 1d
|
||||||
JMP :1
|
JMP :1
|
||||||
0: CP r33, r6
|
0: CP r33, r1
|
||||||
CP r34, r7
|
LI64 r34, 4d
|
||||||
LI64 r35, 4d
|
CP r2, r34
|
||||||
CP r2, r35
|
|
||||||
JAL r31, r0, :multiple_breaks
|
JAL r31, r0, :multiple_breaks
|
||||||
CP r36, r35
|
CP r35, r34
|
||||||
LI64 r37, 10d
|
LI64 r36, 10d
|
||||||
JEQ r1, r37, :2
|
JEQ r1, r36, :2
|
||||||
LI64 r1, 2d
|
LI64 r1, 2d
|
||||||
JMP :1
|
JMP :1
|
||||||
2: CP r2, r32
|
2: CP r2, r32
|
||||||
JAL r31, r0, :state_change_in_break
|
JAL r31, r0, :state_change_in_break
|
||||||
JEQ r1, r32, :3
|
JEQ r1, r32, :3
|
||||||
CP r1, r34
|
CP r1, r33
|
||||||
JMP :1
|
JMP :1
|
||||||
3: CP r2, r36
|
3: CP r2, r35
|
||||||
JAL r31, r0, :state_change_in_break
|
JAL r31, r0, :state_change_in_break
|
||||||
JEQ r1, r37, :4
|
JEQ r1, r36, :4
|
||||||
CP r1, r36
|
CP r1, r35
|
||||||
JMP :1
|
JMP :1
|
||||||
4: CP r2, r37
|
4: CP r2, r36
|
||||||
JAL r31, r0, :continue_and_state_change
|
JAL r31, r0, :continue_and_state_change
|
||||||
JEQ r1, r37, :5
|
JEQ r1, r36, :5
|
||||||
LI64 r1, 5d
|
LI64 r1, 5d
|
||||||
JMP :1
|
JMP :1
|
||||||
5: CP r2, r34
|
5: CP r2, r33
|
||||||
JAL r31, r0, :continue_and_state_change
|
JAL r31, r0, :continue_and_state_change
|
||||||
JEQ r1, r32, :6
|
JEQ r1, r32, :6
|
||||||
LI64 r1, 6d
|
LI64 r1, 6d
|
||||||
JMP :1
|
JMP :1
|
||||||
6: CP r1, r32
|
6: CP r37, r32
|
||||||
CP r38, r33
|
JAL r31, r0, :infinite_loop
|
||||||
8: JNE r1, r38, :7
|
CP r1, r37
|
||||||
JMP :7
|
1: LD r31, r254, 0a, 56h
|
||||||
7: CP r2, r32
|
ADDI64 r254, r254, 56d
|
||||||
JAL r31, r0, :continue_and_state_change
|
|
||||||
JMP :8
|
|
||||||
1: LD r31, r254, 0a, 64h
|
|
||||||
ADDI64 r254, r254, 64d
|
|
||||||
JALA r0, r31, 0a
|
JALA r0, r31, 0a
|
||||||
multiple_breaks:
|
multiple_breaks:
|
||||||
LI64 r6, 3d
|
LI64 r6, 3d
|
||||||
|
@ -91,6 +100,6 @@ state_change_in_break:
|
||||||
JMP :4
|
JMP :4
|
||||||
3: JALA r0, r31, 0a
|
3: JALA r0, r31, 0a
|
||||||
timed out
|
timed out
|
||||||
code size: 582
|
code size: 668
|
||||||
ret: 10
|
ret: 10
|
||||||
status: Ok(())
|
status: Ok(())
|
||||||
|
|
9
lang/tests/son_tests_infinite_loop_after_peephole.txt
Normal file
9
lang/tests/son_tests_infinite_loop_after_peephole.txt
Normal file
|
@ -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(())
|
|
@ -7,30 +7,29 @@ main:
|
||||||
CP r9, r4
|
CP r9, r4
|
||||||
6: JNE r9, r6, :0
|
6: JNE r9, r6, :0
|
||||||
LI64 r6, 2d
|
LI64 r6, 2d
|
||||||
CP r7, r4
|
ADDI64 r7, r254, 32d
|
||||||
4: JNE r7, r8, :1
|
CP r9, r4
|
||||||
|
4: JNE r9, r8, :1
|
||||||
LD r1, r254, 0a, 8h
|
LD r1, r254, 0a, 8h
|
||||||
JMP :2
|
JMP :2
|
||||||
1: CP r10, r4
|
1: MUL64 r12, r9, r6
|
||||||
5: ADD64 r9, r7, r8
|
ADD64 r9, r9, r8
|
||||||
JNE r10, r6, :3
|
SUB64 r10, r6, r9
|
||||||
CP r7, r9
|
MUL64 r10, r10, r6
|
||||||
|
CP r3, r4
|
||||||
|
5: JNE r3, r6, :3
|
||||||
JMP :4
|
JMP :4
|
||||||
3: ADD64 r3, r10, r8
|
3: ADD64 r11, r3, r8
|
||||||
MUL64 r12, r7, r6
|
ADD64 r1, r12, r3
|
||||||
SUB64 r11, r6, r9
|
MULI64 r1, r1, 8d
|
||||||
ADD64 r9, r12, r10
|
ADD64 r2, r10, r3
|
||||||
MUL64 r11, r11, r6
|
ADD64 r1, r5, r1
|
||||||
MULI64 r9, r9, 8d
|
MULI64 r2, r2, 8d
|
||||||
ADD64 r11, r11, r10
|
ADD64 r2, r5, r2
|
||||||
ADD64 r9, r5, r9
|
BMC r1, r7, 8h
|
||||||
MULI64 r11, r11, 8d
|
BMC r2, r1, 8h
|
||||||
ADDI64 r10, r254, 32d
|
BMC r7, r2, 8h
|
||||||
ADD64 r11, r5, r11
|
CP r3, r11
|
||||||
BMC r9, r10, 8h
|
|
||||||
BMC r11, r9, 8h
|
|
||||||
BMC r10, r11, 8h
|
|
||||||
CP r10, r3
|
|
||||||
JMP :5
|
JMP :5
|
||||||
0: ADD64 r2, r9, r8
|
0: ADD64 r2, r9, r8
|
||||||
MULI64 r12, r9, 8d
|
MULI64 r12, r9, 8d
|
||||||
|
@ -40,6 +39,6 @@ main:
|
||||||
JMP :6
|
JMP :6
|
||||||
2: ADDI64 r254, r254, 40d
|
2: ADDI64 r254, r254, 40d
|
||||||
JALA r0, r31, 0a
|
JALA r0, r31, 0a
|
||||||
code size: 274
|
code size: 271
|
||||||
ret: 2
|
ret: 2
|
||||||
status: Ok(())
|
status: Ok(())
|
||||||
|
|
Loading…
Reference in a new issue