moving op instruction selection to token methods

This commit is contained in:
mlokr 2024-09-13 18:41:01 +02:00
parent 87c8f48c18
commit 32e34e4c76
3 changed files with 114 additions and 174 deletions

View file

@ -1383,35 +1383,11 @@ impl Codegen {
loc: Loc::ct(value as u64), loc: Loc::ct(value as u64),
}), }),
E::If { cond, then, mut else_, .. } => { E::If { cond, then, mut else_, .. } => {
#[allow(clippy::type_complexity)]
fn cond_op(
op: TokenKind,
signed: bool,
) -> Option<(fn(u8, u8, i16) -> (usize, [u8; instrs::MAX_SIZE]), bool)>
{
Some((
match op {
TokenKind::Le if signed => instrs::jgts,
TokenKind::Le => instrs::jgtu,
TokenKind::Lt if signed => instrs::jlts,
TokenKind::Lt => instrs::jltu,
TokenKind::Ge if signed => instrs::jlts,
TokenKind::Ge => instrs::jltu,
TokenKind::Gt if signed => instrs::jgts,
TokenKind::Gt => instrs::jgtu,
TokenKind::Eq => instrs::jne,
TokenKind::Ne => instrs::jeq,
_ => return None,
},
matches!(op, TokenKind::Lt | TokenKind::Gt),
))
}
let mut then = Some(then); let mut then = Some(then);
let jump_offset; let jump_offset;
if let &E::BinOp { left, op, right } = cond if let &E::BinOp { left, op, right } = cond
&& let ty = self.infer_type(left) && let ty = self.infer_type(left)
&& let Some((op, swapped)) = cond_op(op, ty.is_signed()) && let Some((op, swapped)) = op.cond_op(ty.is_signed())
{ {
let left = self.expr_ctx(left, Ctx::default())?; let left = self.expr_ctx(left, Ctx::default())?;
let right = self.expr_ctx(right, Ctx::default())?; let right = self.expr_ctx(right, Ctx::default())?;
@ -1419,7 +1395,7 @@ impl Codegen {
let rsize = self.tys.size_of(right.ty); let rsize = self.tys.size_of(right.ty);
let left_reg = self.loc_to_reg(&left.loc, lsize); let left_reg = self.loc_to_reg(&left.loc, lsize);
let right_reg = self.loc_to_reg(&right.loc, rsize); let right_reg = self.loc_to_reg(&right.loc, rsize);
jump_offset = self.local_offset(); jump_offset = self.ci.code.len();
self.ci.emit(op(left_reg.get(), right_reg.get(), 0)); self.ci.emit(op(left_reg.get(), right_reg.get(), 0));
self.ci.free_loc(left.loc); self.ci.free_loc(left.loc);
self.ci.free_loc(right.loc); self.ci.free_loc(right.loc);
@ -1431,7 +1407,7 @@ impl Codegen {
} else { } else {
let cond = self.expr_ctx(cond, Ctx::default().with_ty(ty::BOOL))?; let cond = self.expr_ctx(cond, Ctx::default().with_ty(ty::BOOL))?;
let reg = self.loc_to_reg(&cond.loc, 1); let reg = self.loc_to_reg(&cond.loc, 1);
jump_offset = self.local_offset(); jump_offset = self.ci.code.len();
self.ci.emit(jeq(reg.get(), 0, 0)); self.ci.emit(jeq(reg.get(), 0, 0));
self.ci.free_loc(cond.loc); self.ci.free_loc(cond.loc);
self.ci.regs.free(reg); self.ci.regs.free(reg);
@ -1441,38 +1417,38 @@ impl Codegen {
if let Some(then) = then { self.expr(then).is_none() } else { false }; if let Some(then) = then { self.expr(then).is_none() } else { false };
let mut else_unreachable = false; let mut else_unreachable = false;
let mut jump = self.local_offset() as i64 - jump_offset as i64; let mut jump = self.ci.code.len() as i64 - jump_offset as i64;
if let Some(else_) = else_ { if let Some(else_) = else_ {
let else_jump_offset = self.local_offset(); let else_jump_offset = self.ci.code.len();
if !then_unreachable { if !then_unreachable {
self.ci.emit(jmp(0)); self.ci.emit(jmp(0));
jump = self.local_offset() as i64 - jump_offset as i64; jump = self.ci.code.len() as i64 - jump_offset as i64;
} }
else_unreachable = self.expr(else_).is_none(); else_unreachable = self.expr(else_).is_none();
if !then_unreachable { if !then_unreachable {
let jump = self.local_offset() as i64 - else_jump_offset as i64; let jump = self.ci.code.len() as i64 - else_jump_offset as i64;
write_reloc(self.local_code(), else_jump_offset as usize + 1, jump, 4); write_reloc(&mut self.ci.code, else_jump_offset + 1, jump, 4);
} }
} }
write_reloc(self.local_code(), jump_offset as usize + 3, jump, 2); write_reloc(&mut self.ci.code, jump_offset + 3, jump, 2);
(!then_unreachable || !else_unreachable).then_some(Value::void()) (!then_unreachable || !else_unreachable).then_some(Value::void())
} }
E::Loop { body, .. } => 'a: { E::Loop { body, .. } => 'a: {
let loop_start = self.local_offset(); let loop_start = self.ci.code.len();
self.ci.loops.push(Loop { self.ci.loops.push(Loop {
var_count: self.ci.vars.len() as _, var_count: self.ci.vars.len() as _,
offset: loop_start, offset: loop_start as _,
reloc_base: self.ci.loop_relocs.len() as u32, reloc_base: self.ci.loop_relocs.len() as u32,
}); });
let body_unreachable = self.expr(body).is_none(); let body_unreachable = self.expr(body).is_none();
if !body_unreachable { if !body_unreachable {
let loop_end = self.local_offset(); let loop_end = self.ci.code.len();
self.ci.emit(jmp(loop_start as i32 - loop_end as i32)); self.ci.emit(jmp(loop_start as i32 - loop_end as i32));
} }
@ -1504,7 +1480,7 @@ impl Codegen {
} }
E::Continue { .. } => { E::Continue { .. } => {
let loop_ = self.ci.loops.last().unwrap(); let loop_ = self.ci.loops.last().unwrap();
let offset = self.local_offset(); let offset = self.ci.code.len();
self.ci.emit(jmp(loop_.offset as i32 - offset as i32)); self.ci.emit(jmp(loop_.offset as i32 - offset as i32));
None None
} }
@ -1570,7 +1546,7 @@ impl Codegen {
let signed = ty.is_signed(); let signed = ty.is_signed();
if let Loc::Ct { value: CtValue(mut imm), derefed } = right.loc if let Loc::Ct { value: CtValue(mut imm), derefed } = right.loc
&& let Some(oper) = Self::imm_math_op(op, signed, size) && let Some(oper) = op.imm_math_op(signed, size)
{ {
if derefed { if derefed {
let mut dst = [0u8; 8]; let mut dst = [0u8; 8];
@ -1623,7 +1599,7 @@ impl Codegen {
} }
} }
if let Some(op) = Self::math_op(op, signed, size) { if let Some(op) = op.math_op(signed, size) {
self.ci.emit(op(dst.get(), lhs.get(), rhs.get())); self.ci.emit(op(dst.get(), lhs.get(), rhs.get()));
self.ci.regs.free(lhs); self.ci.regs.free(lhs);
self.ci.regs.free(rhs); self.ci.regs.free(rhs);
@ -1952,7 +1928,7 @@ impl Codegen {
let lhs = self.loc_to_reg(left, size); let lhs = self.loc_to_reg(left, size);
if let Loc::Ct { value, derefed: false } = right if let Loc::Ct { value, derefed: false } = right
&& let Some(op) = Self::imm_math_op(op, signed, size) && let Some(op) = op.imm_math_op(signed, size)
{ {
self.ci.emit(op(lhs.get(), lhs.get(), value.0)); self.ci.emit(op(lhs.get(), lhs.get(), value.0));
return Some(if let Some(value) = ctx.into_value() { return Some(if let Some(value) = ctx.into_value() {
@ -1965,7 +1941,7 @@ impl Codegen {
let rhs = self.loc_to_reg(right, size); let rhs = self.loc_to_reg(right, size);
if let Some(op) = Self::math_op(op, signed, size) { if let Some(op) = op.math_op(signed, size) {
self.ci.emit(op(lhs.get(), lhs.get(), rhs.get())); self.ci.emit(op(lhs.get(), lhs.get(), rhs.get()));
self.ci.regs.free(rhs); self.ci.regs.free(rhs);
return if let Some(value) = ctx.into_value() { return if let Some(value) = ctx.into_value() {
@ -1979,76 +1955,6 @@ impl Codegen {
unimplemented!("{:#?}", op) unimplemented!("{:#?}", op)
} }
#[allow(clippy::type_complexity)]
fn math_op(
op: TokenKind,
signed: bool,
size: u32,
) -> Option<fn(u8, u8, u8) -> (usize, [u8; instrs::MAX_SIZE])> {
use TokenKind as T;
macro_rules! div { ($($op:ident),*) => {[$(|a, b, c| $op(a, ZERO, b, c)),*]}; }
macro_rules! rem { ($($op:ident),*) => {[$(|a, b, c| $op(ZERO, a, b, c)),*]}; }
let ops = match op {
T::Add => [add8, add16, add32, add64],
T::Sub => [sub8, sub16, sub32, sub64],
T::Mul => [mul8, mul16, mul32, mul64],
T::Div if signed => div!(dirs8, dirs16, dirs32, dirs64),
T::Div => div!(diru8, diru16, diru32, diru64),
T::Mod if signed => rem!(dirs8, dirs16, dirs32, dirs64),
T::Mod => rem!(diru8, diru16, diru32, diru64),
T::Band => return Some(and),
T::Bor => return Some(or),
T::Xor => return Some(xor),
T::Shl => [slu8, slu16, slu32, slu64],
T::Shr if signed => [srs8, srs16, srs32, srs64],
T::Shr => [sru8, sru16, sru32, sru64],
_ => return None,
};
Some(ops[size.ilog2() as usize])
}
#[allow(clippy::type_complexity)]
fn imm_math_op(
op: TokenKind,
signed: bool,
size: u32,
) -> Option<fn(u8, u8, u64) -> (usize, [u8; instrs::MAX_SIZE])> {
use TokenKind as T;
macro_rules! def_op {
($name:ident |$a:ident, $b:ident, $c:ident| $($tt:tt)*) => {
macro_rules! $name {
($$($$op:ident),*) => {
[$$(
|$a, $b, $c: u64| $$op($($tt)*),
)*]
}
}
};
}
def_op!(basic_op | a, b, c | a, b, c as _);
def_op!(sub_op | a, b, c | a, b, c.wrapping_neg() as _);
let ops = match op {
T::Add => basic_op!(addi8, addi16, addi32, addi64),
T::Sub => sub_op!(addi8, addi16, addi32, addi64),
T::Mul => basic_op!(muli8, muli16, muli32, muli64),
T::Band => return Some(andi),
T::Bor => return Some(ori),
T::Xor => return Some(xori),
T::Shr if signed => basic_op!(srui8, srui16, srui32, srui64),
T::Shr => basic_op!(srui8, srui16, srui32, srui64),
T::Shl => basic_op!(slui8, slui16, slui32, slui64),
_ => return None,
};
Some(ops[size.ilog2() as usize])
}
fn handle_global(&mut self, id: ty::Global) -> Option<Value> { fn handle_global(&mut self, id: ty::Global) -> Option<Value> {
let ptr = self.ci.regs.allocate(); let ptr = self.ci.regs.allocate();
@ -2708,14 +2614,6 @@ impl Codegen {
&self.files[self.ci.file as usize] &self.files[self.ci.file as usize]
} }
fn local_code(&mut self) -> &mut [u8] {
&mut self.ci.code
}
fn local_offset(&self) -> u32 {
self.ci.code.len() as _
}
fn pack_args(&mut self, pos: Pos, arg_base: usize) -> ty::Tuple { fn pack_args(&mut self, pos: Pos, arg_base: usize) -> ty::Tuple {
let needle = &self.tys.args[arg_base..]; let needle = &self.tys.args[arg_base..];
if needle.is_empty() { if needle.is_empty() {

View file

@ -1,3 +1,5 @@
use crate::instrs;
const fn ascii_mask(chars: &[u8]) -> u128 { const fn ascii_mask(chars: &[u8]) -> u128 {
let mut eq = 0; let mut eq = 0;
let mut i = 0; let mut i = 0;
@ -168,6 +170,98 @@ pub enum TokenKind {
} }
impl TokenKind { impl TokenKind {
#[allow(clippy::type_complexity)]
pub fn cond_op(
self,
signed: bool,
) -> Option<(fn(u8, u8, i16) -> (usize, [u8; instrs::MAX_SIZE]), bool)> {
Some((
match self {
Self::Le if signed => instrs::jgts,
Self::Le => instrs::jgtu,
Self::Lt if signed => instrs::jlts,
Self::Lt => instrs::jltu,
Self::Ge if signed => instrs::jlts,
Self::Ge => instrs::jltu,
Self::Gt if signed => instrs::jgts,
Self::Gt => instrs::jgtu,
Self::Eq => instrs::jne,
Self::Ne => instrs::jeq,
_ => return None,
},
matches!(self, Self::Lt | TokenKind::Gt),
))
}
#[allow(clippy::type_complexity)]
pub fn math_op(
self,
signed: bool,
size: u32,
) -> Option<fn(u8, u8, u8) -> (usize, [u8; instrs::MAX_SIZE])> {
use instrs::*;
macro_rules! div { ($($op:ident),*) => {[$(|a, b, c| $op(a, 0, b, c)),*]}; }
macro_rules! rem { ($($op:ident),*) => {[$(|a, b, c| $op(0, a, b, c)),*]}; }
let ops = match self {
Self::Add => [add8, add16, add32, add64],
Self::Sub => [sub8, sub16, sub32, sub64],
Self::Mul => [mul8, mul16, mul32, mul64],
Self::Div if signed => div!(dirs8, dirs16, dirs32, dirs64),
Self::Div => div!(diru8, diru16, diru32, diru64),
Self::Mod if signed => rem!(dirs8, dirs16, dirs32, dirs64),
Self::Mod => rem!(diru8, diru16, diru32, diru64),
Self::Band => return Some(and),
Self::Bor => return Some(or),
Self::Xor => return Some(xor),
Self::Shl => [slu8, slu16, slu32, slu64],
Self::Shr if signed => [srs8, srs16, srs32, srs64],
Self::Shr => [sru8, sru16, sru32, sru64],
_ => return None,
};
Some(ops[size.ilog2() as usize])
}
#[allow(clippy::type_complexity)]
pub fn imm_math_op(
self,
signed: bool,
size: u32,
) -> Option<fn(u8, u8, u64) -> (usize, [u8; instrs::MAX_SIZE])> {
use instrs::*;
macro_rules! def_op {
($name:ident |$a:ident, $b:ident, $c:ident| $($tt:tt)*) => {
macro_rules! $name {
($$($$op:ident),*) => {
[$$(
|$a, $b, $c: u64| $$op($($tt)*),
)*]
}
}
};
}
def_op!(basic_op | a, b, c | a, b, c as _);
def_op!(sub_op | a, b, c | a, b, c.wrapping_neg() as _);
let ops = match self {
Self::Add => basic_op!(addi8, addi16, addi32, addi64),
Self::Sub => sub_op!(addi8, addi16, addi32, addi64),
Self::Mul => basic_op!(muli8, muli16, muli32, muli64),
Self::Band => return Some(andi),
Self::Bor => return Some(ori),
Self::Xor => return Some(xori),
Self::Shr if signed => basic_op!(srui8, srui16, srui32, srui64),
Self::Shr => basic_op!(srui8, srui16, srui32, srui64),
Self::Shl => basic_op!(slui8, slui16, slui32, slui64),
_ => return None,
};
Some(ops[size.ilog2() as usize])
}
pub fn ass_op(self) -> Option<Self> { pub fn ass_op(self) -> Option<Self> {
let id = (self as u8).saturating_sub(128); let id = (self as u8).saturating_sub(128);
if ascii_mask(b"|+-*/%^&79") & (1u128 << id) == 0 { if ascii_mask(b"|+-*/%^&79") & (1u128 << id) == 0 {

View file

@ -332,7 +332,6 @@ impl BitSet {
type Nid = u16; type Nid = u16;
pub mod reg { pub mod reg {
pub const STACK_PTR: Reg = 254; pub const STACK_PTR: Reg = 254;
pub const ZERO: Reg = 0; pub const ZERO: Reg = 0;
pub const RET: Reg = 1; pub const RET: Reg = 1;
@ -2245,8 +2244,7 @@ impl Codegen {
unreachable!() unreachable!()
}; };
let Some((op, swpd)) = let Some((op, swpd)) = op.cond_op(self.ci.nodes[left].ty.is_signed())
Self::cond_op(op, self.ci.nodes[left].ty.is_signed())
else { else {
break 'optimize_cond; break 'optimize_cond;
}; };
@ -2488,8 +2486,8 @@ impl Codegen {
} else { } else {
self.emit_expr_consume(right); self.emit_expr_consume(right);
let op = Self::math_op(op, ty.is_signed(), self.tys.size_of(ty)) let op =
.expect("TODO: what now?"); op.math_op(ty.is_signed(), self.tys.size_of(ty)).expect("TODO: what now?");
let instr = op( let instr = op(
node_loc!(self, expr).reg, node_loc!(self, expr).reg,
@ -2564,56 +2562,6 @@ impl Codegen {
Some(ops[size.ilog2() as usize]) Some(ops[size.ilog2() as usize])
} }
#[allow(clippy::type_complexity)]
fn cond_op(
op: TokenKind,
signed: bool,
) -> Option<(fn(u8, u8, i16) -> (usize, [u8; instrs::MAX_SIZE]), bool)> {
Some((
match op {
TokenKind::Le if signed => instrs::jgts,
TokenKind::Le => instrs::jgtu,
TokenKind::Lt if signed => instrs::jlts,
TokenKind::Lt => instrs::jltu,
TokenKind::Eq => instrs::jne,
TokenKind::Ne => instrs::jeq,
_ => return None,
},
matches!(op, TokenKind::Lt | TokenKind::Gt),
))
}
#[allow(clippy::type_complexity)]
fn math_op(
op: TokenKind,
signed: bool,
size: u32,
) -> Option<fn(u8, u8, u8) -> (usize, [u8; instrs::MAX_SIZE])> {
use {instrs::*, TokenKind as T};
macro_rules! div { ($($op:ident),*) => {[$(|a, b, c| $op(a, reg::ZERO, b, c)),*]}; }
macro_rules! rem { ($($op:ident),*) => {[$(|a, b, c| $op(reg::ZERO, a, b, c)),*]}; }
let ops = match op {
T::Add => [add8, add16, add32, add64],
T::Sub => [sub8, sub16, sub32, sub64],
T::Mul => [mul8, mul16, mul32, mul64],
T::Div if signed => div!(dirs8, dirs16, dirs32, dirs64),
T::Div => div!(diru8, diru16, diru32, diru64),
T::Mod if signed => rem!(dirs8, dirs16, dirs32, dirs64),
T::Mod => rem!(diru8, diru16, diru32, diru64),
T::Band => return Some(and),
T::Bor => return Some(or),
T::Xor => return Some(xor),
T::Shl => [slu8, slu16, slu32, slu64],
T::Shr if signed => [srs8, srs16, srs32, srs64],
T::Shr => [sru8, sru16, sru32, sru64],
_ => return None,
};
Some(ops[size.ilog2() as usize])
}
// TODO: sometimes its better to do this in bulk // TODO: sometimes its better to do this in bulk
fn ty(&mut self, expr: &Expr) -> ty::Id { fn ty(&mut self, expr: &Expr) -> ty::Id {
match *expr { match *expr {