adding floating point arithmetic
This commit is contained in:
parent
348d9014e3
commit
80558ea7e6
|
@ -51,6 +51,13 @@ main := fn(): uint {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### floating_point_arithmetic
|
||||||
|
```hb
|
||||||
|
main := fn(): f32 {
|
||||||
|
return 10. - 20. / 2. + 4. * (2. + 2.) - 4. * 4. + -1.
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
#### functions
|
#### functions
|
||||||
```hb
|
```hb
|
||||||
main := fn(): uint {
|
main := fn(): uint {
|
||||||
|
|
|
@ -366,6 +366,9 @@ impl<'a> Formatter<'a> {
|
||||||
let mut buf = [0u8; 64];
|
let mut buf = [0u8; 64];
|
||||||
f.write_str(display_radix(radix, value as u64, &mut buf))
|
f.write_str(display_radix(radix, value as u64, &mut buf))
|
||||||
}
|
}
|
||||||
|
Expr::Float { pos, .. } => {
|
||||||
|
f.write_str(&self.source[Lexer::restore(self.source, pos).eat().range()])
|
||||||
|
}
|
||||||
Expr::Bool { value, .. } => f.write_str(if value { "true" } else { "false" }),
|
Expr::Bool { value, .. } => f.write_str(if value { "true" } else { "false" }),
|
||||||
Expr::Idk { .. } => f.write_str("idk"),
|
Expr::Idk { .. } => f.write_str("idk"),
|
||||||
Expr::Null { .. } => f.write_str("null"),
|
Expr::Null { .. } => f.write_str("null"),
|
||||||
|
@ -475,7 +478,8 @@ pub mod test {
|
||||||
let len = crate::fmt::minify(&mut minned);
|
let len = crate::fmt::minify(&mut minned);
|
||||||
minned.truncate(len);
|
minned.truncate(len);
|
||||||
|
|
||||||
let ast = parser::Ast::new(ident, minned, &mut Ctx::default(), &mut parser::no_loader);
|
let mut ctx = Ctx::default();
|
||||||
|
let ast = parser::Ast::new(ident, minned, &mut ctx, &mut parser::no_loader);
|
||||||
//log::error!(
|
//log::error!(
|
||||||
// "{} / {} = {} | {} / {} = {}",
|
// "{} / {} = {} | {} / {} = {}",
|
||||||
// ast.mem.size(),
|
// ast.mem.size(),
|
||||||
|
|
|
@ -116,6 +116,7 @@ pub enum TokenKind {
|
||||||
|
|
||||||
Ident,
|
Ident,
|
||||||
Number,
|
Number,
|
||||||
|
Float,
|
||||||
Eof,
|
Eof,
|
||||||
|
|
||||||
Ct,
|
Ct,
|
||||||
|
@ -190,7 +191,43 @@ impl TokenKind {
|
||||||
matches!(self, S::Eq | S::Ne | S::Bor | S::Xor | S::Band | S::Add | S::Mul)
|
matches!(self, S::Eq | S::Ne | S::Bor | S::Xor | S::Band | S::Add | S::Mul)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply_binop(self, a: i64, b: i64) -> i64 {
|
pub fn is_supported_float_op(self) -> bool {
|
||||||
|
matches!(
|
||||||
|
self,
|
||||||
|
Self::Add
|
||||||
|
| Self::Sub
|
||||||
|
| Self::Mul
|
||||||
|
| Self::Div
|
||||||
|
| Self::Eq
|
||||||
|
| Self::Ne
|
||||||
|
| Self::Le
|
||||||
|
| Self::Ge
|
||||||
|
| Self::Lt
|
||||||
|
| Self::Gt
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn apply_binop(self, a: i64, b: i64, float: bool) -> i64 {
|
||||||
|
if float {
|
||||||
|
debug_assert!(self.is_supported_float_op());
|
||||||
|
let [a, b] = [f64::from_bits(a as _), f64::from_bits(b as _)];
|
||||||
|
let res = match self {
|
||||||
|
Self::Add => a + b,
|
||||||
|
Self::Sub => a - b,
|
||||||
|
Self::Mul => a * b,
|
||||||
|
Self::Div => a / b,
|
||||||
|
Self::Eq => return (a == b) as i64,
|
||||||
|
Self::Ne => return (a != b) as i64,
|
||||||
|
Self::Lt => return (a < b) as i64,
|
||||||
|
Self::Gt => return (a > b) as i64,
|
||||||
|
Self::Le => return (a >= b) as i64,
|
||||||
|
Self::Ge => return (a <= b) as i64,
|
||||||
|
_ => todo!("floating point op: {self}"),
|
||||||
|
};
|
||||||
|
|
||||||
|
return res.to_bits() as _;
|
||||||
|
}
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Self::Add => a.wrapping_add(b),
|
Self::Add => a.wrapping_add(b),
|
||||||
Self::Sub => a.wrapping_sub(b),
|
Self::Sub => a.wrapping_sub(b),
|
||||||
|
@ -214,15 +251,6 @@ impl TokenKind {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cmp_against(self) -> Option<u64> {
|
|
||||||
Some(match self {
|
|
||||||
TokenKind::Le | TokenKind::Gt => 1,
|
|
||||||
TokenKind::Ne | TokenKind::Eq => 0,
|
|
||||||
TokenKind::Ge | TokenKind::Lt => (-1i64) as _,
|
|
||||||
_ => return None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_homogenous(&self) -> bool {
|
pub fn is_homogenous(&self) -> bool {
|
||||||
self.precedence() != Self::Eq.precedence()
|
self.precedence() != Self::Eq.precedence()
|
||||||
&& self.precedence() != Self::Gt.precedence()
|
&& self.precedence() != Self::Gt.precedence()
|
||||||
|
@ -254,6 +282,7 @@ gen_token_kind! {
|
||||||
CtIdent,
|
CtIdent,
|
||||||
Ident,
|
Ident,
|
||||||
Number,
|
Number,
|
||||||
|
Float,
|
||||||
Eof,
|
Eof,
|
||||||
Directive,
|
Directive,
|
||||||
#[keywords]
|
#[keywords]
|
||||||
|
@ -418,8 +447,16 @@ impl<'a> Lexer<'a> {
|
||||||
while let Some(b'0'..=b'9') = self.peek() {
|
while let Some(b'0'..=b'9') = self.peek() {
|
||||||
self.advance();
|
self.advance();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.advance_if(b'.') {
|
||||||
|
while let Some(b'0'..=b'9') = self.peek() {
|
||||||
|
self.advance();
|
||||||
|
}
|
||||||
|
T::Float
|
||||||
|
} else {
|
||||||
T::Number
|
T::Number
|
||||||
}
|
}
|
||||||
|
}
|
||||||
b'a'..=b'z' | b'A'..=b'Z' | b'_' | 127.. => {
|
b'a'..=b'z' | b'A'..=b'Z' | b'_' | 127.. => {
|
||||||
advance_ident(self);
|
advance_ident(self);
|
||||||
let ident = &self.source[start as usize..self.pos as usize];
|
let ident = &self.source[start as usize..self.pos as usize];
|
||||||
|
|
|
@ -409,6 +409,10 @@ mod ty {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_float(self) -> bool {
|
||||||
|
matches!(self.repr(), F32 | F64) || self.is_never()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_signed(self) -> bool {
|
pub fn is_signed(self) -> bool {
|
||||||
matches!(self.repr(), I8..=INT) || self.is_never()
|
matches!(self.repr(), I8..=INT) || self.is_never()
|
||||||
}
|
}
|
||||||
|
@ -465,8 +469,8 @@ mod ty {
|
||||||
Kind::Ptr(_) => 8,
|
Kind::Ptr(_) => 8,
|
||||||
Kind::Builtin(VOID) => 0,
|
Kind::Builtin(VOID) => 0,
|
||||||
Kind::Builtin(NEVER) => 0,
|
Kind::Builtin(NEVER) => 0,
|
||||||
Kind::Builtin(INT | UINT) => 8,
|
Kind::Builtin(INT | UINT | F64) => 8,
|
||||||
Kind::Builtin(I32 | U32 | TYPE) => 4,
|
Kind::Builtin(I32 | U32 | TYPE | F32) => 4,
|
||||||
Kind::Builtin(I16 | U16) => 2,
|
Kind::Builtin(I16 | U16) => 2,
|
||||||
Kind::Builtin(I8 | U8 | BOOL) => 1,
|
Kind::Builtin(I8 | U8 | BOOL) => 1,
|
||||||
_ => return None,
|
_ => return None,
|
||||||
|
@ -547,7 +551,6 @@ mod ty {
|
||||||
};)*
|
};)*
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(dead_code)]
|
|
||||||
impl Id {
|
impl Id {
|
||||||
$(pub const $name: Self = Kind::Builtin($name).compress();)*
|
$(pub const $name: Self = Kind::Builtin($name).compress();)*
|
||||||
}
|
}
|
||||||
|
@ -584,7 +587,8 @@ mod ty {
|
||||||
I16;
|
I16;
|
||||||
I32;
|
I32;
|
||||||
INT;
|
INT;
|
||||||
|
F32;
|
||||||
|
F64;
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! type_kind {
|
macro_rules! type_kind {
|
||||||
|
|
|
@ -472,6 +472,14 @@ impl<'a, 'b> Parser<'a, 'b> {
|
||||||
radix,
|
radix,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
T::Float => E::Float {
|
||||||
|
pos,
|
||||||
|
value: match <f64 as core::str::FromStr>::from_str(self.lexer.slice(token.range()))
|
||||||
|
{
|
||||||
|
Ok(f) => f.to_bits(),
|
||||||
|
Err(e) => self.report(token.start, format_args!("invalid float: {e}"))?,
|
||||||
|
},
|
||||||
|
},
|
||||||
T::LParen => {
|
T::LParen => {
|
||||||
let expr = self.expr()?;
|
let expr = self.expr()?;
|
||||||
self.expect_advance(T::RParen)?;
|
self.expect_advance(T::RParen)?;
|
||||||
|
@ -811,6 +819,11 @@ generate_expr! {
|
||||||
value: i64,
|
value: i64,
|
||||||
radix: Radix,
|
radix: Radix,
|
||||||
},
|
},
|
||||||
|
/// `'[0-9]+.[0-9]*'`
|
||||||
|
Float {
|
||||||
|
pos: Pos,
|
||||||
|
value: u64,
|
||||||
|
},
|
||||||
/// node: precedence defined in `OP` applies
|
/// node: precedence defined in `OP` applies
|
||||||
/// `Expr OP Expr`
|
/// `Expr OP Expr`
|
||||||
BinOp {
|
BinOp {
|
||||||
|
|
|
@ -692,12 +692,16 @@ impl Nodes {
|
||||||
};
|
};
|
||||||
let ty = self[target].ty;
|
let ty = self[target].ty;
|
||||||
|
|
||||||
|
let is_float = self[lhs].ty.is_float();
|
||||||
|
|
||||||
if let (&K::CInt { value: a }, &K::CInt { value: b }) =
|
if let (&K::CInt { value: a }, &K::CInt { value: b }) =
|
||||||
(&self[lhs].kind, &self[rhs].kind)
|
(&self[lhs].kind, &self[rhs].kind)
|
||||||
{
|
{
|
||||||
return Some(
|
return Some(self.new_node(
|
||||||
self.new_node(ty, K::CInt { value: op.apply_binop(a, b) }, [ctrl]),
|
ty,
|
||||||
);
|
K::CInt { value: op.apply_binop(a, b, is_float) },
|
||||||
|
[ctrl],
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if lhs == rhs {
|
if lhs == rhs {
|
||||||
|
@ -734,10 +738,11 @@ impl Nodes {
|
||||||
&& let K::CInt { value: bv } = self[rhs].kind
|
&& let K::CInt { value: bv } = self[rhs].kind
|
||||||
{
|
{
|
||||||
// (a op #b) op #c => a op (#b op #c)
|
// (a op #b) op #c => a op (#b op #c)
|
||||||
let new_rhs =
|
let new_rhs = self.new_node_nop(
|
||||||
self.new_node_nop(ty, K::CInt { value: op.apply_binop(av, bv) }, [
|
ty,
|
||||||
ctrl,
|
K::CInt { value: op.apply_binop(av, bv, is_float) },
|
||||||
]);
|
[ctrl],
|
||||||
|
);
|
||||||
return Some(self.new_node(ty, K::BinOp { op }, [ctrl, a, new_rhs]));
|
return Some(self.new_node(ty, K::BinOp { op }, [ctrl, a, new_rhs]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2235,6 +2240,11 @@ impl<'a> Codegen<'a> {
|
||||||
Kind::CInt { value },
|
Kind::CInt { value },
|
||||||
[VOID],
|
[VOID],
|
||||||
)),
|
)),
|
||||||
|
Expr::Float { value, .. } => Some(self.ci.nodes.new_node_lit(
|
||||||
|
ctx.ty.filter(|ty| ty.is_float()).unwrap_or(ty::Id::F32),
|
||||||
|
Kind::CInt { value: value as _ },
|
||||||
|
[VOID],
|
||||||
|
)),
|
||||||
Expr::Ident { id, .. }
|
Expr::Ident { id, .. }
|
||||||
if let Some(index) = self.ci.scope.vars.iter().rposition(|v| v.id == id) =>
|
if let Some(index) = self.ci.scope.vars.iter().rposition(|v| v.id == id) =>
|
||||||
{
|
{
|
||||||
|
@ -2425,10 +2435,21 @@ impl<'a> Codegen<'a> {
|
||||||
Expr::UnOp { pos, op: op @ TokenKind::Sub, val } => {
|
Expr::UnOp { pos, op: op @ TokenKind::Sub, val } => {
|
||||||
let val =
|
let val =
|
||||||
self.expr_ctx(val, Ctx::default().with_ty(ctx.ty.unwrap_or(ty::Id::INT)))?;
|
self.expr_ctx(val, Ctx::default().with_ty(ctx.ty.unwrap_or(ty::Id::INT)))?;
|
||||||
if !val.ty.is_integer() {
|
if val.ty.is_integer() {
|
||||||
self.report(pos, fa!("cant negate '{}'", self.ty_display(val.ty)));
|
|
||||||
}
|
|
||||||
Some(self.ci.nodes.new_node_lit(val.ty, Kind::UnOp { op }, [VOID, val.id]))
|
Some(self.ci.nodes.new_node_lit(val.ty, Kind::UnOp { op }, [VOID, val.id]))
|
||||||
|
} else if val.ty.is_float() {
|
||||||
|
let value = self.ci.nodes.new_node_nop(
|
||||||
|
val.ty,
|
||||||
|
Kind::CInt { value: (-1f64).to_bits() as _ },
|
||||||
|
[VOID],
|
||||||
|
);
|
||||||
|
Some(self.ci.nodes.new_node_lit(val.ty, Kind::BinOp { op: TokenKind::Mul }, [
|
||||||
|
VOID, val.id, value,
|
||||||
|
]))
|
||||||
|
} else {
|
||||||
|
self.report(pos, fa!("cant negate '{}'", self.ty_display(val.ty)));
|
||||||
|
Value::NEVER
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Expr::BinOp { left, op: TokenKind::Decl, right, .. } => {
|
Expr::BinOp { left, op: TokenKind::Decl, right, .. } => {
|
||||||
let mut right = self.expr(right)?;
|
let mut right = self.expr(right)?;
|
||||||
|
@ -2473,7 +2494,11 @@ impl<'a> Codegen<'a> {
|
||||||
self.strip_var(&mut lhs);
|
self.strip_var(&mut lhs);
|
||||||
|
|
||||||
match lhs.ty.expand() {
|
match lhs.ty.expand() {
|
||||||
_ if lhs.ty.is_pointer() || lhs.ty.is_integer() || lhs.ty == ty::Id::BOOL => {
|
_ if lhs.ty.is_pointer()
|
||||||
|
|| lhs.ty.is_integer()
|
||||||
|
|| lhs.ty == ty::Id::BOOL
|
||||||
|
|| (lhs.ty.is_float() && op.is_supported_float_op()) =>
|
||||||
|
{
|
||||||
self.strip_ptr(&mut lhs);
|
self.strip_ptr(&mut lhs);
|
||||||
self.ci.nodes.lock(lhs.id);
|
self.ci.nodes.lock(lhs.id);
|
||||||
let rhs = self.expr_ctx(right, Ctx::default().with_ty(lhs.ty));
|
let rhs = self.expr_ctx(right, Ctx::default().with_ty(lhs.ty));
|
||||||
|
@ -3924,6 +3949,7 @@ mod tests {
|
||||||
// Tour Examples
|
// Tour Examples
|
||||||
main_fn;
|
main_fn;
|
||||||
arithmetic;
|
arithmetic;
|
||||||
|
floating_point_arithmetic;
|
||||||
functions;
|
functions;
|
||||||
comments;
|
comments;
|
||||||
if_statements;
|
if_statements;
|
||||||
|
|
|
@ -247,7 +247,7 @@ impl ItemCtx {
|
||||||
let &[_, cnd] = node.inputs.as_slice() else { unreachable!() };
|
let &[_, cnd] = node.inputs.as_slice() else { unreachable!() };
|
||||||
if let Kind::BinOp { op } = fuc.nodes[cnd].kind
|
if let Kind::BinOp { op } = fuc.nodes[cnd].kind
|
||||||
&& let Some((op, swapped)) =
|
&& let Some((op, swapped)) =
|
||||||
op.cond_op(fuc.nodes[fuc.nodes[cnd].inputs[1]].ty.is_signed())
|
op.cond_op(fuc.nodes[fuc.nodes[cnd].inputs[1]].ty)
|
||||||
{
|
{
|
||||||
let &[lhs, rhs] = allocs else { unreachable!() };
|
let &[lhs, rhs] = allocs else { unreachable!() };
|
||||||
let &[_, lh, rh] = fuc.nodes[cnd].inputs.as_slice() else {
|
let &[_, lh, rh] = fuc.nodes[cnd].inputs.as_slice() else {
|
||||||
|
@ -306,6 +306,16 @@ impl ItemCtx {
|
||||||
self.emit(instrs::jmp(0));
|
self.emit(instrs::jmp(0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Kind::CInt { value } if node.ty.is_float() => {
|
||||||
|
self.emit(match node.ty {
|
||||||
|
ty::Id::F32 => instrs::li32(
|
||||||
|
atr(allocs[0]),
|
||||||
|
(f64::from_bits(value as _) as f32).to_bits(),
|
||||||
|
),
|
||||||
|
ty::Id::F64 => instrs::li64(atr(allocs[0]), value as _),
|
||||||
|
_ => unreachable!(),
|
||||||
|
});
|
||||||
|
}
|
||||||
Kind::CInt { value } => self.emit(match tys.size_of(node.ty) {
|
Kind::CInt { value } => self.emit(match tys.size_of(node.ty) {
|
||||||
1 => instrs::li8(atr(allocs[0]), value as _),
|
1 => instrs::li8(atr(allocs[0]), value as _),
|
||||||
2 => instrs::li16(atr(allocs[0]), value as _),
|
2 => instrs::li16(atr(allocs[0]), value as _),
|
||||||
|
@ -319,33 +329,48 @@ impl ItemCtx {
|
||||||
}
|
}
|
||||||
Kind::BinOp { .. } if node.lock_rc != 0 => {}
|
Kind::BinOp { .. } if node.lock_rc != 0 => {}
|
||||||
Kind::BinOp { op } => {
|
Kind::BinOp { op } => {
|
||||||
let &[.., rhs] = node.inputs.as_slice() else { unreachable!() };
|
let &[.., lh, rh] = node.inputs.as_slice() else { unreachable!() };
|
||||||
|
|
||||||
if let Kind::CInt { value } = fuc.nodes[rhs].kind
|
if let Kind::CInt { value } = fuc.nodes[rh].kind
|
||||||
&& fuc.nodes[rhs].lock_rc != 0
|
&& fuc.nodes[rh].lock_rc != 0
|
||||||
&& let Some(op) =
|
&& let Some(op) = op.imm_binop(node.ty)
|
||||||
op.imm_binop(node.ty.is_signed(), fuc.tys.size_of(node.ty))
|
|
||||||
{
|
{
|
||||||
let &[dst, lhs] = allocs else { unreachable!() };
|
let &[dst, lhs] = allocs else { unreachable!() };
|
||||||
self.emit(op(atr(dst), atr(lhs), value as _));
|
self.emit(op(atr(dst), atr(lhs), value as _));
|
||||||
} else if let Some(op) =
|
} else if let Some(op) =
|
||||||
op.binop(node.ty.is_signed(), fuc.tys.size_of(node.ty))
|
op.binop(node.ty).or(op.float_cmp(fuc.nodes[lh].ty))
|
||||||
{
|
{
|
||||||
let &[dst, lhs, rhs] = allocs else { unreachable!() };
|
let &[dst, lhs, rhs] = allocs else { unreachable!() };
|
||||||
self.emit(op(atr(dst), atr(lhs), atr(rhs)));
|
self.emit(op(atr(dst), atr(lhs), atr(rhs)));
|
||||||
} else if let Some(against) = op.cmp_against() {
|
} else if let Some(against) = op.cmp_against() {
|
||||||
let &[_, lh, rh] = node.inputs.as_slice() else { unreachable!() };
|
let op_ty = fuc.nodes[lh].ty;
|
||||||
|
|
||||||
self.emit(extend(fuc.nodes[lh].ty, fuc.nodes[lh].ty.extend(), 0, 0));
|
self.emit(extend(fuc.nodes[lh].ty, fuc.nodes[lh].ty.extend(), 0, 0));
|
||||||
self.emit(extend(fuc.nodes[rh].ty, fuc.nodes[rh].ty.extend(), 1, 1));
|
self.emit(extend(fuc.nodes[rh].ty, fuc.nodes[rh].ty.extend(), 1, 1));
|
||||||
|
|
||||||
let signed = fuc.nodes[lh].ty.is_signed();
|
|
||||||
let op_fn = if signed { instrs::cmps } else { instrs::cmpu };
|
|
||||||
let &[dst, lhs, rhs] = allocs else { unreachable!() };
|
let &[dst, lhs, rhs] = allocs else { unreachable!() };
|
||||||
|
|
||||||
|
if op_ty.is_float() && matches!(op, TokenKind::Le | TokenKind::Ge) {
|
||||||
|
let opop = match op {
|
||||||
|
TokenKind::Le => TokenKind::Gt,
|
||||||
|
TokenKind::Ge => TokenKind::Lt,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
let op_fn = opop.float_cmp(op_ty).unwrap();
|
||||||
|
self.emit(op_fn(atr(dst), atr(lhs), atr(rhs)));
|
||||||
|
self.emit(instrs::not(atr(dst), atr(dst)));
|
||||||
|
} else if op_ty.is_integer() {
|
||||||
|
let op_fn =
|
||||||
|
if op_ty.is_signed() { instrs::cmps } else { instrs::cmpu };
|
||||||
self.emit(op_fn(atr(dst), atr(lhs), atr(rhs)));
|
self.emit(op_fn(atr(dst), atr(lhs), atr(rhs)));
|
||||||
self.emit(instrs::cmpui(atr(dst), atr(dst), against));
|
self.emit(instrs::cmpui(atr(dst), atr(dst), against));
|
||||||
if matches!(op, TokenKind::Eq | TokenKind::Lt | TokenKind::Gt) {
|
if matches!(op, TokenKind::Eq | TokenKind::Lt | TokenKind::Gt) {
|
||||||
self.emit(instrs::not(atr(dst), atr(dst)));
|
self.emit(instrs::not(atr(dst), atr(dst)));
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
todo!("unhandled operator: {op}");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
todo!("unhandled operator: {op}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Kind::Call { args, func } => {
|
Kind::Call { args, func } => {
|
||||||
|
@ -725,7 +750,7 @@ impl<'a> Function<'a> {
|
||||||
let &[mut then, mut else_] = node.outputs.as_slice() else { unreachable!() };
|
let &[mut then, mut else_] = node.outputs.as_slice() else { unreachable!() };
|
||||||
|
|
||||||
if let Kind::BinOp { op } = self.nodes[cond].kind
|
if let Kind::BinOp { op } = self.nodes[cond].kind
|
||||||
&& let Some((_, swapped)) = op.cond_op(node.ty.is_signed())
|
&& let Some((_, swapped)) = op.cond_op(node.ty)
|
||||||
{
|
{
|
||||||
if swapped {
|
if swapped {
|
||||||
mem::swap(&mut then, &mut else_);
|
mem::swap(&mut then, &mut else_);
|
||||||
|
@ -789,9 +814,9 @@ impl<'a> Function<'a> {
|
||||||
if node.outputs.iter().all(|&o| {
|
if node.outputs.iter().all(|&o| {
|
||||||
let ond = &self.nodes[o];
|
let ond = &self.nodes[o];
|
||||||
matches!(ond.kind, Kind::BinOp { op }
|
matches!(ond.kind, Kind::BinOp { op }
|
||||||
if op.imm_binop(ond.ty.is_signed(), 8).is_some()
|
if op.imm_binop(ond.ty).is_some()
|
||||||
&& self.nodes.is_const(ond.inputs[2])
|
&& self.nodes.is_const(ond.inputs[2])
|
||||||
&& op.cond_op(ond.ty.is_signed()).is_none())
|
&& op.cond_op(ond.ty).is_none())
|
||||||
}) =>
|
}) =>
|
||||||
{
|
{
|
||||||
self.nodes.lock(nid)
|
self.nodes.lock(nid)
|
||||||
|
@ -853,7 +878,7 @@ impl<'a> Function<'a> {
|
||||||
self.nodes.lock(nid)
|
self.nodes.lock(nid)
|
||||||
}
|
}
|
||||||
Kind::BinOp { op }
|
Kind::BinOp { op }
|
||||||
if op.cond_op(node.ty.is_signed()).is_some()
|
if op.cond_op(node.ty).is_some()
|
||||||
&& node.outputs.iter().all(|&n| self.nodes[n].kind == Kind::If) =>
|
&& node.outputs.iter().all(|&n| self.nodes[n].kind == Kind::If) =>
|
||||||
{
|
{
|
||||||
self.nodes.lock(nid)
|
self.nodes.lock(nid)
|
||||||
|
@ -1183,8 +1208,36 @@ impl regalloc2::Function for Function<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TokenKind {
|
impl TokenKind {
|
||||||
|
pub fn cmp_against(self) -> Option<u64> {
|
||||||
|
Some(match self {
|
||||||
|
TokenKind::Le | TokenKind::Gt => 1,
|
||||||
|
TokenKind::Ne | TokenKind::Eq => 0,
|
||||||
|
TokenKind::Ge | TokenKind::Lt => (-1i64) as _,
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_cmp(self, ty: ty::Id) -> Option<fn(u8, u8, u8) -> EncodedInstr> {
|
||||||
|
if !ty.is_float() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let size = ty.simple_size().unwrap();
|
||||||
|
|
||||||
|
let ops = match self {
|
||||||
|
TokenKind::Gt => [instrs::fcmpgt32, instrs::fcmpgt64],
|
||||||
|
TokenKind::Lt => [instrs::fcmplt32, instrs::fcmplt64],
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(ops[size.ilog2() as usize - 2])
|
||||||
|
}
|
||||||
|
|
||||||
#[expect(clippy::type_complexity)]
|
#[expect(clippy::type_complexity)]
|
||||||
fn cond_op(self, signed: bool) -> Option<(fn(u8, u8, i16) -> EncodedInstr, bool)> {
|
fn cond_op(self, ty: ty::Id) -> Option<(fn(u8, u8, i16) -> EncodedInstr, bool)> {
|
||||||
|
if ty.is_float() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let signed = ty.is_signed();
|
||||||
Some((
|
Some((
|
||||||
match self {
|
match self {
|
||||||
Self::Le if signed => instrs::jgts,
|
Self::Le if signed => instrs::jgts,
|
||||||
|
@ -1203,9 +1256,12 @@ impl TokenKind {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn binop(self, signed: bool, size: u32) -> Option<fn(u8, u8, u8) -> EncodedInstr> {
|
fn binop(self, ty: ty::Id) -> Option<fn(u8, u8, u8) -> EncodedInstr> {
|
||||||
|
let size = ty.simple_size().unwrap();
|
||||||
|
if ty.is_integer() || ty == ty::Id::BOOL || ty.is_pointer() {
|
||||||
macro_rules! div { ($($op:ident),*) => {[$(|a, b, c| $op(a, 0, b, c)),*]}; }
|
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)),*]}; }
|
macro_rules! rem { ($($op:ident),*) => {[$(|a, b, c| $op(0, a, b, c)),*]}; }
|
||||||
|
let signed = ty.is_signed();
|
||||||
|
|
||||||
let ops = match self {
|
let ops = match self {
|
||||||
Self::Add => [add8, add16, add32, add64],
|
Self::Add => [add8, add16, add32, add64],
|
||||||
|
@ -1225,9 +1281,20 @@ impl TokenKind {
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(ops[size.ilog2() as usize])
|
Some(ops[size.ilog2() as usize])
|
||||||
|
} else {
|
||||||
|
debug_assert!(ty.is_float(), "{self} {ty:?}");
|
||||||
|
let ops = match self {
|
||||||
|
Self::Add => [fadd32, fadd64],
|
||||||
|
Self::Sub => [fsub32, fsub64],
|
||||||
|
Self::Mul => [fmul32, fmul64],
|
||||||
|
Self::Div => [fdiv32, fdiv64],
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
Some(ops[size.ilog2() as usize - 2])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn imm_binop(self, signed: bool, size: u32) -> Option<fn(u8, u8, u64) -> EncodedInstr> {
|
fn imm_binop(self, ty: ty::Id) -> Option<fn(u8, u8, u64) -> EncodedInstr> {
|
||||||
macro_rules! def_op {
|
macro_rules! def_op {
|
||||||
($name:ident |$a:ident, $b:ident, $c:ident| $($tt:tt)*) => {
|
($name:ident |$a:ident, $b:ident, $c:ident| $($tt:tt)*) => {
|
||||||
macro_rules! $name {
|
macro_rules! $name {
|
||||||
|
@ -1240,9 +1307,14 @@ impl TokenKind {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ty.is_float() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
def_op!(basic_op | a, b, c | a, b, c as _);
|
def_op!(basic_op | a, b, c | a, b, c as _);
|
||||||
def_op!(sub_op | a, b, c | a, b, c.wrapping_neg() as _);
|
def_op!(sub_op | a, b, c | a, b, c.wrapping_neg() as _);
|
||||||
|
|
||||||
|
let signed = ty.is_signed();
|
||||||
let ops = match self {
|
let ops = match self {
|
||||||
Self::Add => basic_op!(addi8, addi16, addi32, addi64),
|
Self::Add => basic_op!(addi8, addi16, addi32, addi64),
|
||||||
Self::Sub => sub_op!(addi8, addi16, addi32, addi64),
|
Self::Sub => sub_op!(addi8, addi16, addi32, addi64),
|
||||||
|
@ -1256,6 +1328,7 @@ impl TokenKind {
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let size = ty.simple_size().unwrap();
|
||||||
Some(ops[size.ilog2() as usize])
|
Some(ops[size.ilog2() as usize])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
6
lang/tests/son_tests_floating_point_arithmetic.txt
Normal file
6
lang/tests/son_tests_floating_point_arithmetic.txt
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
main:
|
||||||
|
LI32 r1, 3212836864w
|
||||||
|
JALA r0, r31, 0a
|
||||||
|
code size: 25
|
||||||
|
ret: 3212836864
|
||||||
|
status: Ok(())
|
Loading…
Reference in a new issue