diff --git a/Makefile b/Makefile index 48470530..53fa1183 100644 --- a/Makefile +++ b/Makefile @@ -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 \ diff --git a/src/b.rs b/src/b.rs index bb1154be..6de68582 100644 --- a/src/b.rs +++ b/src/b.rs @@ -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; @@ -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() { @@ -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}, @@ -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(); diff --git a/src/codegen/fasm_x86_64.rs b/src/codegen/fasm_x86_64.rs index 36f42a3c..831fe19b 100644 --- a/src/codegen/fasm_x86_64.rs +++ b/src/codegen/fasm_x86_64.rs @@ -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); diff --git a/src/codegen/gas_aarch64_linux.rs b/src/codegen/gas_aarch64_linux.rs index cc707756..8d6dd64c 100644 --- a/src/codegen/gas_aarch64_linux.rs +++ b/src/codegen/gas_aarch64_linux.rs @@ -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); diff --git a/src/codegen/gas_x86_64.rs b/src/codegen/gas_x86_64.rs index c6010745..cc54ca86 100644 --- a/src/codegen/gas_x86_64.rs +++ b/src/codegen/gas_x86_64.rs @@ -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); diff --git a/src/codegen/ir.rs b/src/codegen/ir.rs index bdac41bc..81abac40 100644 --- a/src/codegen/ir.rs +++ b/src/codegen/ir.rs @@ -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 { diff --git a/src/codegen/mos6502.rs b/src/codegen/mos6502.rs index ccb285c6..4273172a 100644 --- a/src/codegen/mos6502.rs +++ b/src/codegen/mos6502.rs @@ -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); diff --git a/src/codegen/uxn.rs b/src/codegen/uxn.rs index 96b56393..bb7427cf 100644 --- a/src/codegen/uxn.rs +++ b/src/codegen/uxn.rs @@ -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 diff --git a/src/opt.rs b/src/opt.rs new file mode 100644 index 00000000..b0ecab3f --- /dev/null +++ b/src/opt.rs @@ -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 + + } +}