Skip to content

adding conservative constant folding #172

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ CRUST_FLAGS=-g --edition 2021 -C opt-level=0 -C panic="abort"

RSS=\
$(SRC)/arena.rs \
$(SRC)/opt.rs \
$(SRC)/b.rs \
$(SRC)/crust.rs \
$(SRC)/flag.rs \
Expand Down
9 changes: 9 additions & 0 deletions src/b.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ pub mod runner;
pub mod lexer;
pub mod targets;

pub mod opt;

use core::ffi::*;
use core::mem::zeroed;
use core::ptr;
Expand All @@ -27,6 +29,8 @@ use crust::libc::*;
use arena::Arena;
use targets::*;
use lexer::{Lexer, Loc, Token};
use crate::opt::optimize;


pub unsafe fn expect_tokens(l: *mut Lexer, tokens: *const [Token]) -> Option<()> {
for i in 0..tokens.len() {
Expand Down Expand Up @@ -310,6 +314,9 @@ pub struct AsmStmt {
#[derive(Clone, Copy)]
pub enum Op {
Bogus,

NoOp,

UnaryNot {result: usize, arg: Arg},
Negate {result: usize, arg: Arg},
Asm {stmts: Array<AsmStmt>},
Expand Down Expand Up @@ -1281,6 +1288,8 @@ pub unsafe fn main(mut argc: i32, mut argv: *mut*mut c_char) -> Option<()> {
return None
}

optimize(&mut c);

let mut output: String_Builder = zeroed();
let mut cmd: Cmd = zeroed();

Expand Down
2 changes: 2 additions & 0 deletions src/codegen/fasm_x86_64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ pub unsafe fn generate_function(name: *const c_char, params_count: usize, auto_v
let op = (*body)[i];
match op.opcode {
Op::Bogus => unreachable!("bogus-amogus"),
Op::NoOp => {},

Op::Return {arg} => {
if let Some(arg) = arg {
load_arg_to_reg(arg, c!("rax"), output);
Expand Down
2 changes: 2 additions & 0 deletions src/codegen/gas_aarch64_linux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ pub unsafe fn generate_function(name: *const c_char, _name_loc: Loc, params_coun
let op = (*body)[i];
match op.opcode {
Op::Bogus => unreachable!("bogus-amogus"),
Op::NoOp => {},

Op::Return {arg} => {
if let Some(arg) = arg {
load_arg_to_reg(arg, c!("x0"), output, op.loc);
Expand Down
2 changes: 2 additions & 0 deletions src/codegen/gas_x86_64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ pub unsafe fn generate_function(name: *const c_char, params_count: usize, auto_v
let op = (*body)[i];
match op.opcode {
Op::Bogus => unreachable!("bogus-amogus"),
Op::NoOp => {},

Op::Return { arg } => {
if let Some(arg) = arg {
load_arg_to_reg(arg, c!("rax"), output);
Expand Down
2 changes: 2 additions & 0 deletions src/codegen/ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ pub unsafe fn generate_function(name: *const c_char, params_count: usize, auto_v
let op = (*body)[i];
match op.opcode {
Op::Bogus => unreachable!("bogus-amogus"),
Op::NoOp => {sb_appendf(output, c!(" nothing "));},

Op::Return {arg} => {
sb_appendf(output, c!(" return "));
if let Some(arg) = arg {
Expand Down
2 changes: 2 additions & 0 deletions src/codegen/mos6502.rs
Original file line number Diff line number Diff line change
Expand Up @@ -757,6 +757,8 @@ pub unsafe fn generate_function(name: *const c_char, params_count: usize, auto_v
let op = (*body)[i];
match op.opcode {
Op::Bogus => unreachable!("bogus-amogus"),
Op::NoOp => {},

Op::Return {arg} => {
if let Some(arg) = arg {
load_arg(arg, op.loc, out, asm);
Expand Down
2 changes: 2 additions & 0 deletions src/codegen/uxn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,8 @@ pub unsafe fn generate_function(name: *const c_char, name_loc: Loc, params_count
let op = (*body)[i];
match op.opcode {
Op::Bogus => unreachable!("bogus-amogus"),
Op::NoOp => {},

Op::UnaryNot {result, arg} => {
load_arg(arg, output, assembler);
// if arg == 0 then 1 else 0
Expand Down
150 changes: 150 additions & 0 deletions src/opt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
use crate::*;

pub unsafe fn optimize(c: *mut Compiler) {
for i in 0..(*c).funcs.count {
optimize_func(c,(*c).funcs.items.add(i));
}
}

#[derive(Clone, Copy)]
pub struct BlockStyle{
no_entries:bool,
no_readers:bool,
}

pub unsafe fn optimize_func(c: *mut Compiler,f:*mut Func){
//function entry is special since no one is yet able to read/write
//when we see the first label that means someone may actually jump from a new context
//this means we can constant fold all autovar assigments here and its fine

let mut style = BlockStyle{no_readers:false,no_entries:true};
let mut block = 0;

//rest is normal
while block < (*f).body.count {
block=optimize_block(c,f,block,&mut style);
}

}

pub unsafe fn eliminate_block_end(f: *mut Func, block: usize,style:*mut BlockStyle) -> usize {
let mut id = block;

while id < (*f).body.count {

let spot = (*f).body.items.add(id);
match (*spot).opcode {
Op::Label{..} | Op::Asm{..} => {
//the asm may have a label and then fall through

(*style).no_readers = false;
(*style).no_entries = false;
return id
},
_=>{(*spot).opcode = Op::NoOp;}
};

id+=1;
}

id
}

pub unsafe fn optimize_block(c: *mut Compiler,f: *mut Func, block: usize,style:*mut BlockStyle) -> usize {
let mut id = block;

while id < (*f).body.count {
let spot = (*f).body.items.add(id);

(*spot).opcode = eval_constant_op((*spot).opcode,(*c).target,*style);

match (*spot).opcode {
Op::Label{ .. } | Op::Asm{..} => {
(*style).no_entries = false;
(*style).no_readers = false;
return id+1
},
Op::Funcall{..} => {
(*style).no_readers = false;
return id+1
},
Op::Return{..} => {
//since this is returning out no one can possibly read the autovars
//because autovars are function local
//in fact this is an even more agressive type of constant folding we could do here
if (*style).no_readers == false {
(*style).no_readers = true;
return optimize_block(c,f,block,style);
}
return eliminate_block_end(f,id+1,style);

},
Op::JmpLabel{..} => {
return eliminate_block_end(f,id+1,style);

}
_ => {}
};

id+=1;
}

id
}


pub unsafe fn eval_constant_op(op:Op,target:Target,style:BlockStyle) -> Op{
if style.no_readers {
eval_constant_op_strong(op,target)
}else {
eval_constant_op_weak(op,target)
}
}

pub unsafe fn eval_constant_op_strong(op:Op,target:Target) -> Op {
eval_constant_op_weak(op,target)
}

pub unsafe fn eval_constant_op_weak(op:Op,target:Target) -> Op {
let mask = {
let bits = target.word_size() * 8;
if bits >= 64 { !0 } else { (1u64 << bits) - 1 }
};

match op {
Op::UnaryNot { result, arg:Arg::Literal(lit)} => Op::AutoAssign{index:result,arg:Arg::Literal((lit==0) as u64)},
Op::Negate { result, arg:Arg::Literal(lit)} => Op::AutoAssign{index:result,arg:Arg::Literal((!lit).wrapping_add(1) & mask)},
Op::Binop { binop, index, lhs: Arg::Literal(a), rhs: Arg::Literal(b) } => {
let val = match binop {
Binop::Plus => a.wrapping_add(b),
Binop::Minus => a.wrapping_sub(b),
Binop::Mult => a.wrapping_mul(b),
Binop::Div => if b == 0 { return op } else { a.wrapping_div(b) },
Binop::Mod => if b == 0 { return op } else { a.wrapping_rem(b) },

Binop::Less => (a < b) as u64,
Binop::Greater => (a > b) as u64,
Binop::Equal => (a == b) as u64,
Binop::NotEqual => (a != b) as u64,
Binop::GreaterEqual => (a >= b) as u64,
Binop::LessEqual => (a <= b) as u64,

Binop::BitOr => a | b,
Binop::BitAnd => a & b,
Binop::BitShl => a.wrapping_shl((b & 63) as u32),
Binop::BitShr => a.wrapping_shr((b & 63) as u32),
} & mask;

Op::AutoAssign { index, arg: Arg::Literal(val) }
},
Op::JmpIfNotLabel{label,arg:Arg::Literal(lit)} => {
if lit == 0 {
Op::JmpLabel{label}
}else{
Op::NoOp
}
}
_ => op

}
}