From 4b831569f67a8e8bd5131f92966cd186784b495f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 5 Jul 2016 09:08:24 +0200 Subject: [PATCH] implement floats by running the ops on the host architecture --- src/interpreter/mod.rs | 24 +++++++++++++++++++++--- src/memory.rs | 3 +++ src/primval.rs | 35 +++++++++++++++++++++++++++++++++++ tests/run-pass/floats.rs | 8 ++++++++ 4 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 tests/run-pass/floats.rs diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 4e564005f23..8ca37a65177 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -169,7 +169,8 @@ pub fn stack(&self) -> &[Frame<'a, 'tcx>] { // TODO(solson): Try making const_to_primval instead. fn const_to_ptr(&mut self, const_val: &const_val::ConstVal) -> EvalResult<'tcx, Pointer> { use rustc::middle::const_val::ConstVal::*; - use rustc_const_math::{ConstInt, ConstIsize, ConstUsize}; + use rustc_const_math::{ConstInt, ConstIsize, ConstUsize, ConstFloat}; + use std::mem::transmute; macro_rules! i2p { ($i:ident, $n:expr) => {{ let ptr = self.memory.allocate($n); @@ -178,7 +179,15 @@ macro_rules! i2p { }} } match *const_val { - Float(_f) => unimplemented!(), + Float(ConstFloat::F32(f)) => { + let i = unsafe { transmute::<_, u32>(f) }; + i2p!(i, 4) + }, + Float(ConstFloat::F64(f)) => { + let i = unsafe { transmute::<_, u64>(f) }; + i2p!(i, 8) + }, + Float(ConstFloat::FInfer{..}) => unreachable!(), Integral(ConstInt::Infer(_)) => unreachable!(), Integral(ConstInt::InferSigned(_)) => unreachable!(), Integral(ConstInt::I8(i)) => i2p!(i, 1), @@ -824,7 +833,8 @@ fn move_(&mut self, src: Pointer, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<'tc } pub fn read_primval(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { - use syntax::ast::{IntTy, UintTy}; + use syntax::ast::{IntTy, UintTy, FloatTy}; + use std::mem::transmute; let val = match (self.memory.pointer_size(), &ty.sty) { (_, &ty::TyBool) => PrimVal::Bool(self.memory.read_bool(ptr)?), (_, &ty::TyChar) => { @@ -848,6 +858,14 @@ pub fn read_primval(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, P (_, &ty::TyUint(UintTy::U32)) => PrimVal::U32(self.memory.read_uint(ptr, 4)? as u32), (8, &ty::TyUint(UintTy::Us)) | (_, &ty::TyUint(UintTy::U64)) => PrimVal::U64(self.memory.read_uint(ptr, 8)? as u64), + (_, &ty::TyFloat(FloatTy::F32)) => { + let i = self.memory.read_uint(ptr, 4)? as u32; + PrimVal::F32(unsafe { transmute(i) }) + }, + (_, &ty::TyFloat(FloatTy::F64)) => { + let i = self.memory.read_uint(ptr, 8)?; + PrimVal::F64(unsafe { transmute(i) }) + }, (_, &ty::TyFnDef(def_id, substs, fn_ty)) => { PrimVal::FnPtr(self.memory.create_fn_ptr(def_id, substs, fn_ty)) diff --git a/src/memory.rs b/src/memory.rs index f3072b886b8..92f69d575f1 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -394,6 +394,7 @@ pub fn write_ptr(&mut self, dest: Pointer, ptr: Pointer) -> EvalResult<'tcx, ()> } pub fn write_primval(&mut self, ptr: Pointer, val: PrimVal) -> EvalResult<'tcx, ()> { + use std::mem::transmute; let pointer_size = self.pointer_size(); match val { PrimVal::Bool(b) => self.write_bool(ptr, b), @@ -407,6 +408,8 @@ pub fn write_primval(&mut self, ptr: Pointer, val: PrimVal) -> EvalResult<'tcx, PrimVal::U64(n) => self.write_uint(ptr, n as u64, 8), PrimVal::Char(c) => self.write_uint(ptr, c as u64, 4), PrimVal::IntegerPtr(n) => self.write_uint(ptr, n as u64, pointer_size), + PrimVal::F32(f) => self.write_uint(ptr, unsafe { transmute::<_, u32>(f) } as u64, 4), + PrimVal::F64(f) => self.write_uint(ptr, unsafe { transmute::<_, u64>(f) }, 8), PrimVal::FnPtr(_p) | PrimVal::AbstractPtr(_p) => unimplemented!(), } diff --git a/src/primval.rs b/src/primval.rs index f3aedfc1974..f6f9a025770 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -13,6 +13,8 @@ pub enum PrimVal { FnPtr(Pointer), IntegerPtr(u64), Char(char), + + F32(f32), F64(f64), } /// returns the result of the operation and whether the operation overflowed @@ -57,6 +59,34 @@ macro_rules! int_binops { }) } + macro_rules! float_binops { + ($v:ident, $l:ident, $r:ident) => ({ + match bin_op { + Add => $v($l + $r), + Sub => $v($l - $r), + Mul => $v($l * $r), + Div => $v($l / $r), + Rem => $v($l % $r), + + // invalid float ops + BitXor => unreachable!(), + BitAnd => unreachable!(), + BitOr => unreachable!(), + Shl => unreachable!(), + Shr => unreachable!(), + + // directly comparing floats is questionable + // miri could forbid it, or at least miri as rust const eval should forbid it + Eq => Bool($l == $r), + Ne => Bool($l != $r), + Lt => Bool($l < $r), + Le => Bool($l <= $r), + Gt => Bool($l > $r), + Ge => Bool($l >= $r), + } + }) + } + fn unrelated_ptr_ops<'tcx>(bin_op: mir::BinOp) -> EvalResult<'tcx, PrimVal> { use rustc::mir::repr::BinOp::*; match bin_op { @@ -128,6 +158,8 @@ macro_rules! shift { (U16(l), U16(r)) => int_binops!(U16, l, r), (U32(l), U32(r)) => int_binops!(U32, l, r), (U64(l), U64(r)) => int_binops!(U64, l, r), + (F32(l), F32(r)) => float_binops!(F32, l, r), + (F64(l), F64(r)) => float_binops!(F64, l, r), (Char(l), Char(r)) => match bin_op { Eq => Bool(l == r), Ne => Bool(l != r), @@ -211,6 +243,9 @@ pub fn unary_op<'tcx>(un_op: mir::UnOp, val: PrimVal) -> EvalResult<'tcx, PrimVa (Not, U16(n)) => Ok(U16(!n)), (Not, U32(n)) => Ok(U32(!n)), (Not, U64(n)) => Ok(U64(!n)), + + (Neg, F64(n)) => Ok(F64(-n)), + (Neg, F32(n)) => Ok(F32(-n)), _ => Err(EvalError::Unimplemented(format!("unimplemented unary op: {:?}, {:?}", un_op, val))), } } diff --git a/tests/run-pass/floats.rs b/tests/run-pass/floats.rs new file mode 100644 index 00000000000..504fffca756 --- /dev/null +++ b/tests/run-pass/floats.rs @@ -0,0 +1,8 @@ + +fn main() { + assert_eq!(6.0_f32*6.0_f32, 36.0_f32); + assert_eq!(6.0_f64*6.0_f64, 36.0_f64); + assert_eq!(-{5.0_f32}, -5.0_f32); + assert!((5.0_f32/0.0).is_infinite()); + assert!((-5.0_f32).sqrt().is_nan()); +}