diff --git a/lang/README.md b/lang/README.md index 9f28bbc..ccf6ed7 100644 --- a/lang/README.md +++ b/lang/README.md @@ -277,6 +277,9 @@ main := fn(): uint { same_type_as_byte := @as(@TypeOf(byte), 30) wide_uint := @as(u32, 40) truncated_uint := @as(u8, @intcast(wide_uint)) + widened_float := @as(f64, @floatcast(1.)) + int_from_float := @as(int, @fti(1.)) + float_from_int := @as(f64, @itf(@as(int, 1))) size_of_Type_in_bytes := @sizeof(foo.Type) align_of_Type_in_bytes := @alignof(foo.Type) hardcoded_pointer := @as(^u8, @bitcast(10)) diff --git a/lang/src/lexer.rs b/lang/src/lexer.rs index 1af1760..674ab2d 100644 --- a/lang/src/lexer.rs +++ b/lang/src/lexer.rs @@ -257,9 +257,16 @@ impl TokenKind { && self.precedence() != Self::Eof.precedence() } - pub fn apply_unop(&self, value: i64) -> i64 { + pub fn apply_unop(&self, value: i64, float: bool) -> i64 { match self { + Self::Sub if float => (-f64::from_bits(value as _)).to_bits() as _, Self::Sub => value.wrapping_neg(), + Self::Float if float => value, + Self::Float => (value as f64).to_bits() as _, + Self::Number => { + debug_assert!(float); + f64::from_bits(value as _).to_bits() as _ + } s => todo!("{s}"), } } diff --git a/lang/src/son.rs b/lang/src/son.rs index f55eecc..fc4b1be 100644 --- a/lang/src/son.rs +++ b/lang/src/son.rs @@ -792,10 +792,14 @@ impl Nodes { let &[ctrl, oper] = self[target].inputs.as_slice() else { unreachable!() }; let ty = self[target].ty; + let is_float = self[oper].ty.is_float(); + if let K::CInt { value } = self[oper].kind { - return Some( - self.new_node(ty, K::CInt { value: op.apply_unop(value) }, [ctrl]), - ); + return Some(self.new_node( + ty, + K::CInt { value: op.apply_unop(value, is_float) }, + [ctrl], + )); } } K::If => { @@ -2649,6 +2653,79 @@ impl<'a> Codegen<'a> { Some(val.ty(ty)) } } + Expr::Directive { pos, name: "floatcast", args: [expr] } => { + let val = self.expr(expr)?; + + if !val.ty.is_float() { + self.report( + expr.pos(), + fa!( + "only floats can be truncated ('{}' is not a float)", + self.ty_display(val.ty) + ), + ); + return Value::NEVER; + } + + inference!(ty, ctx, self, pos, "float", "@as(, @floatcast())"); + + if !ty.is_float() { + self.report( + expr.pos(), + fa!( + "floatcast is inferred to output '{}', which is not a float", + self.ty_display(ty) + ), + ); + } + + if self.tys.size_of(val.ty) < self.tys.size_of(ty) { + Some( + self.ci + .nodes + .new_node_lit(ty, Kind::UnOp { op: TokenKind::Float }, [VOID, val.id]), + ) + } else { + Some(val.ty(ty)) + } + } + Expr::Directive { name: "fti", args: [expr], .. } => { + let val = self.expr(expr)?; + + let ret_ty = match val.ty { + ty::Id::F64 => ty::Id::INT, + ty::Id::F32 => ty::Id::I32, + _ => { + self.report( + expr.pos(), + fa!("expected float ('{}' is not a float)", self.ty_display(val.ty)), + ); + return Value::NEVER; + } + }; + + Some( + self.ci + .nodes + .new_node_lit(ret_ty, Kind::UnOp { op: TokenKind::Number }, [VOID, val.id]), + ) + } + Expr::Directive { name: "itf", args: [expr], .. } => { + let mut val = self.expr(expr)?; + + let (ret_ty, expected) = match val.ty.simple_size().unwrap() { + 8 => (ty::Id::F64, ty::Id::INT), + _ => (ty::Id::F32, ty::Id::I32), + }; + + self.assert_ty(expr.pos(), &mut val, expected, "converted integer"); + + Some( + self.ci + .nodes + .new_node_lit(ret_ty, Kind::UnOp { op: TokenKind::Float }, [VOID, val.id]), + ) + } Expr::Directive { name: "as", args: [ty, expr], .. } => { let ty = self.ty(ty); let ctx = Ctx::default().with_ty(ty); diff --git a/lang/src/son/hbvm.rs b/lang/src/son/hbvm.rs index 2a82aeb..a0b7e6c 100644 --- a/lang/src/son/hbvm.rs +++ b/lang/src/son/hbvm.rs @@ -323,7 +323,9 @@ impl ItemCtx { _ => instrs::li64(atr(allocs[0]), value as _), }), Kind::UnOp { op } => { - let op = op.unop().expect("TODO: unary operator not supported"); + let op = op + .unop(node.ty, fuc.nodes[node.inputs[1]].ty) + .expect("TODO: unary operator not supported"); let &[dst, oper] = allocs else { unreachable!() }; self.emit(op(atr(dst), atr(oper))); } @@ -1332,9 +1334,19 @@ impl TokenKind { Some(ops[size.ilog2() as usize]) } - pub fn unop(&self) -> Option EncodedInstr> { + pub fn unop(&self, dst: ty::Id, src: ty::Id) -> Option EncodedInstr> { + let src_idx = src.simple_size().unwrap().ilog2() as usize - 2; Some(match self { Self::Sub => instrs::neg, + Self::Float if dst.is_float() && src.is_integer() => { + [instrs::itf32, instrs::itf64][src_idx] + } + Self::Number if src.is_float() && dst.is_integer() => { + [|a, b| instrs::fti32(a, b, 1), |a, b| instrs::fti64(a, b, 1)][src_idx] + } + Self::Float if dst.is_float() && src.is_float() => { + [instrs::fc32t64, |a, b| instrs::fc64t32(a, b, 1)][src_idx] + } _ => return None, }) }