Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 9861df4

Browse files
committedAug 2, 2017
rustc_const_math: use apfloat::ieee::{Single,Double} in ConstFloat.
1 parent 1409d20 commit 9861df4

File tree

7 files changed

+180
-144
lines changed

7 files changed

+180
-144
lines changed
 

‎src/librustc/ich/impls_const_math.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@
1111
//! This module contains `HashStable` implementations for various data types
1212
//! from `rustc_const_math` in no particular order.
1313
14-
impl_stable_hash_for!(enum ::rustc_const_math::ConstFloat {
15-
F32(val),
16-
F64(val)
14+
impl_stable_hash_for!(struct ::rustc_const_math::ConstFloat {
15+
ty,
16+
bits
1717
});
1818

1919
impl_stable_hash_for!(enum ::rustc_const_math::ConstInt {

‎src/librustc_const_eval/eval.rs

Lines changed: 27 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use rustc::util::nodemap::DefIdMap;
2626

2727
use syntax::abi::Abi;
2828
use syntax::ast;
29+
use syntax::attr;
2930
use rustc::hir::{self, Expr};
3031
use syntax_pos::Span;
3132

@@ -560,8 +561,15 @@ fn cast_const_int<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
560561
ty::TyUint(ast::UintTy::Us) => {
561562
Ok(Integral(Usize(ConstUsize::new_truncating(v, tcx.sess.target.uint_type))))
562563
},
563-
ty::TyFloat(ast::FloatTy::F64) => Ok(Float(F64(val.to_f64()))),
564-
ty::TyFloat(ast::FloatTy::F32) => Ok(Float(F32(val.to_f32()))),
564+
ty::TyFloat(fty) => {
565+
if let Some(i) = val.to_u128() {
566+
Ok(Float(ConstFloat::from_u128(i, fty)))
567+
} else {
568+
// The value must be negative, go through signed integers.
569+
let i = val.to_u128_unchecked() as i128;
570+
Ok(Float(ConstFloat::from_i128(i, fty)))
571+
}
572+
}
565573
ty::TyRawPtr(_) => Err(ErrKind::UnimplementedConstVal("casting an address to a raw ptr")),
566574
ty::TyChar => match val {
567575
U8(u) => Ok(Char(u as char)),
@@ -574,30 +582,25 @@ fn cast_const_int<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
574582
fn cast_const_float<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
575583
val: ConstFloat,
576584
ty: Ty<'tcx>) -> CastResult<'tcx> {
585+
let int_width = |ty| {
586+
ty::layout::Integer::from_attr(tcx, ty).size().bits() as usize
587+
};
577588
match ty.sty {
578-
ty::TyInt(_) | ty::TyUint(_) => {
579-
let i = match val {
580-
F32(f) if f >= 0.0 => U128(f as u128),
581-
F64(f) if f >= 0.0 => U128(f as u128),
582-
583-
F32(f) => I128(f as i128),
584-
F64(f) => I128(f as i128)
585-
};
586-
587-
if let (I128(_), &ty::TyUint(_)) = (i, &ty.sty) {
588-
return Err(CannotCast);
589+
ty::TyInt(ity) => {
590+
if let Some(i) = val.to_i128(int_width(attr::SignedInt(ity))) {
591+
cast_const_int(tcx, I128(i), ty)
592+
} else {
593+
Err(CannotCast)
594+
}
595+
}
596+
ty::TyUint(uty) => {
597+
if let Some(i) = val.to_u128(int_width(attr::UnsignedInt(uty))) {
598+
cast_const_int(tcx, U128(i), ty)
599+
} else {
600+
Err(CannotCast)
589601
}
590-
591-
cast_const_int(tcx, i, ty)
592602
}
593-
ty::TyFloat(ast::FloatTy::F64) => Ok(Float(F64(match val {
594-
F32(f) => f as f64,
595-
F64(f) => f
596-
}))),
597-
ty::TyFloat(ast::FloatTy::F32) => Ok(Float(F32(match val {
598-
F64(f) => f as f32,
599-
F32(f) => f
600-
}))),
603+
ty::TyFloat(fty) => Ok(Float(val.convert(fty))),
601604
_ => Err(CannotCast),
602605
}
603606
}
@@ -691,11 +694,7 @@ fn lit_to_const<'a, 'tcx>(lit: &ast::LitKind,
691694

692695
fn parse_float<'tcx>(num: &str, fty: ast::FloatTy)
693696
-> Result<ConstFloat, ErrKind<'tcx>> {
694-
let val = match fty {
695-
ast::FloatTy::F32 => num.parse::<f32>().map(F32),
696-
ast::FloatTy::F64 => num.parse::<f64>().map(F64)
697-
};
698-
val.map_err(|_| {
697+
ConstFloat::from_str(num, fty).map_err(|_| {
699698
// FIXME(#31407) this is only necessary because float parsing is buggy
700699
UnimplementedConstVal("could not evaluate float literal (see issue #31407)")
701700
})

‎src/librustc_const_math/float.rs

Lines changed: 138 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -9,102 +9,164 @@
99
// except according to those terms.
1010

1111
use std::cmp::Ordering;
12-
use std::hash;
13-
use std::mem::transmute;
12+
use std::num::ParseFloatError;
13+
14+
use syntax::ast;
15+
16+
use rustc_apfloat::{Float, FloatConvert, Status};
17+
use rustc_apfloat::ieee::{Single, Double};
1418

1519
use super::err::*;
1620

17-
#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable)]
18-
pub enum ConstFloat {
19-
F32(f32),
20-
F64(f64)
21+
// Note that equality for `ConstFloat` means that the it is the same
22+
// constant, not that the rust values are equal. In particular, `NaN
23+
// == NaN` (at least if it's the same NaN; distinct encodings for NaN
24+
// are considering unequal).
25+
#[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
26+
pub struct ConstFloat {
27+
pub ty: ast::FloatTy,
28+
29+
// This is a bit inefficient but it makes conversions below more
30+
// ergonomic, and all of this will go away once `miri` is merged.
31+
pub bits: u128,
2132
}
22-
pub use self::ConstFloat::*;
2333

2434
impl ConstFloat {
2535
/// Description of the type, not the value
2636
pub fn description(&self) -> &'static str {
27-
match *self {
28-
F32(_) => "f32",
29-
F64(_) => "f64",
30-
}
37+
self.ty.ty_to_string()
3138
}
3239

3340
pub fn is_nan(&self) -> bool {
34-
match *self {
35-
F32(f) => f.is_nan(),
36-
F64(f) => f.is_nan(),
41+
match self.ty {
42+
ast::FloatTy::F32 => Single::from_bits(self.bits).is_nan(),
43+
ast::FloatTy::F64 => Double::from_bits(self.bits).is_nan(),
3744
}
3845
}
3946

4047
/// Compares the values if they are of the same type
4148
pub fn try_cmp(self, rhs: Self) -> Result<Ordering, ConstMathErr> {
42-
match (self, rhs) {
43-
(F64(a), F64(b)) => {
49+
match (self.ty, rhs.ty) {
50+
(ast::FloatTy::F64, ast::FloatTy::F64) => {
51+
let a = Double::from_bits(self.bits);
52+
let b = Double::from_bits(rhs.bits);
4453
// This is pretty bad but it is the existing behavior.
45-
Ok(if a == b {
46-
Ordering::Equal
47-
} else if a < b {
48-
Ordering::Less
49-
} else {
50-
Ordering::Greater
51-
})
54+
Ok(a.partial_cmp(&b).unwrap_or(Ordering::Greater))
5255
}
5356

54-
(F32(a), F32(b)) => {
55-
Ok(if a == b {
56-
Ordering::Equal
57-
} else if a < b {
58-
Ordering::Less
59-
} else {
60-
Ordering::Greater
61-
})
57+
(ast::FloatTy::F32, ast::FloatTy::F32) => {
58+
let a = Single::from_bits(self.bits);
59+
let b = Single::from_bits(rhs.bits);
60+
Ok(a.partial_cmp(&b).unwrap_or(Ordering::Greater))
6261
}
6362

6463
_ => Err(CmpBetweenUnequalTypes),
6564
}
6665
}
67-
}
6866

69-
/// Note that equality for `ConstFloat` means that the it is the same
70-
/// constant, not that the rust values are equal. In particular, `NaN
71-
/// == NaN` (at least if it's the same NaN; distinct encodings for NaN
72-
/// are considering unequal).
73-
impl PartialEq for ConstFloat {
74-
fn eq(&self, other: &Self) -> bool {
75-
match (*self, *other) {
76-
(F64(a), F64(b)) => {
77-
unsafe{transmute::<_,u64>(a) == transmute::<_,u64>(b)}
67+
pub fn from_i128(input: i128, ty: ast::FloatTy) -> Self {
68+
let bits = match ty {
69+
ast::FloatTy::F32 => Single::from_i128(input).value.to_bits(),
70+
ast::FloatTy::F64 => Double::from_i128(input).value.to_bits()
71+
};
72+
ConstFloat { bits, ty }
73+
}
74+
75+
pub fn from_u128(input: u128, ty: ast::FloatTy) -> Self {
76+
let bits = match ty {
77+
ast::FloatTy::F32 => Single::from_u128(input).value.to_bits(),
78+
ast::FloatTy::F64 => Double::from_u128(input).value.to_bits()
79+
};
80+
ConstFloat { bits, ty }
81+
}
82+
83+
pub fn from_str(num: &str, ty: ast::FloatTy) -> Result<Self, ParseFloatError> {
84+
let bits = match ty {
85+
ast::FloatTy::F32 => {
86+
let rust_bits = num.parse::<f32>()?.to_bits() as u128;
87+
let apfloat = num.parse::<Single>().unwrap_or_else(|e| {
88+
panic!("apfloat::ieee::Single failed to parse `{}`: {:?}", num, e);
89+
});
90+
let apfloat_bits = apfloat.to_bits();
91+
assert!(rust_bits == apfloat_bits,
92+
"apfloat::ieee::Single gave different result for `{}`: \
93+
{}({:#x}) vs Rust's {}({:#x})",
94+
num, apfloat, apfloat_bits,
95+
Single::from_bits(rust_bits), rust_bits);
96+
apfloat_bits
7897
}
79-
(F32(a), F32(b)) => {
80-
unsafe{transmute::<_,u32>(a) == transmute::<_,u32>(b)}
98+
ast::FloatTy::F64 => {
99+
let rust_bits = num.parse::<f64>()?.to_bits() as u128;
100+
let apfloat = num.parse::<Double>().unwrap_or_else(|e| {
101+
panic!("apfloat::ieee::Double failed to parse `{}`: {:?}", num, e);
102+
});
103+
let apfloat_bits = apfloat.to_bits();
104+
assert!(rust_bits == apfloat_bits,
105+
"apfloat::ieee::Double gave different result for `{}`: \
106+
{}({:#x}) vs Rust's {}({:#x})",
107+
num, apfloat, apfloat_bits,
108+
Double::from_bits(rust_bits), rust_bits);
109+
apfloat_bits
81110
}
82-
_ => false
111+
};
112+
Ok(ConstFloat { bits, ty })
113+
}
114+
115+
pub fn to_i128(self, width: usize) -> Option<i128> {
116+
assert!(width <= 128);
117+
let r = match self.ty {
118+
ast::FloatTy::F32 => Single::from_bits(self.bits).to_i128(width),
119+
ast::FloatTy::F64 => Double::from_bits(self.bits).to_i128(width)
120+
};
121+
if r.status.intersects(Status::INVALID_OP) {
122+
None
123+
} else {
124+
Some(r.value)
83125
}
84126
}
85-
}
86127

87-
impl Eq for ConstFloat {}
128+
pub fn to_u128(self, width: usize) -> Option<u128> {
129+
assert!(width <= 128);
130+
let r = match self.ty {
131+
ast::FloatTy::F32 => Single::from_bits(self.bits).to_u128(width),
132+
ast::FloatTy::F64 => Double::from_bits(self.bits).to_u128(width)
133+
};
134+
if r.status.intersects(Status::INVALID_OP) {
135+
None
136+
} else {
137+
Some(r.value)
138+
}
139+
}
88140

89-
impl hash::Hash for ConstFloat {
90-
fn hash<H: hash::Hasher>(&self, state: &mut H) {
91-
match *self {
92-
F64(a) => {
93-
unsafe { transmute::<_,u64>(a) }.hash(state)
141+
pub fn convert(self, to: ast::FloatTy) -> Self {
142+
let bits = match (self.ty, to) {
143+
(ast::FloatTy::F32, ast::FloatTy::F32) |
144+
(ast::FloatTy::F64, ast::FloatTy::F64) => return self,
145+
146+
(ast::FloatTy::F32, ast::FloatTy::F64) => {
147+
Double::to_bits(Single::from_bits(self.bits).convert(&mut false).value)
94148
}
95-
F32(a) => {
96-
unsafe { transmute::<_,u32>(a) }.hash(state)
149+
(ast::FloatTy::F64, ast::FloatTy::F32) => {
150+
Single::to_bits(Double::from_bits(self.bits).convert(&mut false).value)
97151
}
98-
}
152+
};
153+
ConstFloat { bits, ty: to }
99154
}
100155
}
101156

102157
impl ::std::fmt::Display for ConstFloat {
103158
fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
104-
match *self {
105-
F32(f) => write!(fmt, "{}f32", f),
106-
F64(f) => write!(fmt, "{}f64", f),
159+
match self.ty {
160+
ast::FloatTy::F32 => write!(fmt, "{:#}", Single::from_bits(self.bits))?,
161+
ast::FloatTy::F64 => write!(fmt, "{:#}", Double::from_bits(self.bits))?,
107162
}
163+
write!(fmt, "{}", self.ty)
164+
}
165+
}
166+
167+
impl ::std::fmt::Debug for ConstFloat {
168+
fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
169+
::std::fmt::Display::fmt(self, fmt)
108170
}
109171
}
110172

@@ -113,11 +175,20 @@ macro_rules! derive_binop {
113175
impl ::std::ops::$op for ConstFloat {
114176
type Output = Result<Self, ConstMathErr>;
115177
fn $func(self, rhs: Self) -> Result<Self, ConstMathErr> {
116-
match (self, rhs) {
117-
(F32(a), F32(b)) => Ok(F32(a.$func(b))),
118-
(F64(a), F64(b)) => Ok(F64(a.$func(b))),
119-
_ => Err(UnequalTypes(Op::$op)),
120-
}
178+
let bits = match (self.ty, rhs.ty) {
179+
(ast::FloatTy::F32, ast::FloatTy::F32) =>{
180+
let a = Single::from_bits(self.bits);
181+
let b = Single::from_bits(rhs.bits);
182+
a.$func(b).value.to_bits()
183+
}
184+
(ast::FloatTy::F64, ast::FloatTy::F64) => {
185+
let a = Double::from_bits(self.bits);
186+
let b = Double::from_bits(rhs.bits);
187+
a.$func(b).value.to_bits()
188+
}
189+
_ => return Err(UnequalTypes(Op::$op)),
190+
};
191+
Ok(ConstFloat { bits, ty: self.ty })
121192
}
122193
}
123194
}
@@ -132,9 +203,10 @@ derive_binop!(Rem, rem);
132203
impl ::std::ops::Neg for ConstFloat {
133204
type Output = Self;
134205
fn neg(self) -> Self {
135-
match self {
136-
F32(f) => F32(-f),
137-
F64(f) => F64(-f),
138-
}
206+
let bits = match self.ty {
207+
ast::FloatTy::F32 => (-Single::from_bits(self.bits)).to_bits(),
208+
ast::FloatTy::F64 => (-Double::from_bits(self.bits)).to_bits(),
209+
};
210+
ConstFloat { bits, ty: self.ty }
139211
}
140212
}

‎src/librustc_const_math/int.rs

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -211,48 +211,6 @@ impl ConstInt {
211211
}
212212
}
213213

214-
pub fn to_f32(self) -> f32 {
215-
match self {
216-
I8(i) => i as f32,
217-
I16(i) => i as f32,
218-
I32(i) => i as f32,
219-
I64(i) => i as f32,
220-
I128(i) => i as f32,
221-
Isize(Is16(i)) => i as f32,
222-
Isize(Is32(i)) => i as f32,
223-
Isize(Is64(i)) => i as f32,
224-
U8(i) => i as f32,
225-
U16(i) => i as f32,
226-
U32(i) => i as f32,
227-
U64(i) => i as f32,
228-
U128(i) => i as f32,
229-
Usize(Us16(i)) => i as f32,
230-
Usize(Us32(i)) => i as f32,
231-
Usize(Us64(i)) => i as f32,
232-
}
233-
}
234-
235-
pub fn to_f64(self) -> f64 {
236-
match self {
237-
I8(i) => i as f64,
238-
I16(i) => i as f64,
239-
I32(i) => i as f64,
240-
I64(i) => i as f64,
241-
I128(i) => i as f64,
242-
Isize(Is16(i)) => i as f64,
243-
Isize(Is32(i)) => i as f64,
244-
Isize(Is64(i)) => i as f64,
245-
U8(i) => i as f64,
246-
U16(i) => i as f64,
247-
U32(i) => i as f64,
248-
U64(i) => i as f64,
249-
U128(i) => i as f64,
250-
Usize(Us16(i)) => i as f64,
251-
Usize(Us32(i)) => i as f64,
252-
Usize(Us64(i)) => i as f64,
253-
}
254-
}
255-
256214
pub fn is_negative(&self) -> bool {
257215
match *self {
258216
I8(v) => v < 0,

‎src/librustc_const_math/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
#![feature(i128)]
2727
#![feature(i128_type)]
2828

29+
extern crate rustc_apfloat;
30+
2931
extern crate syntax;
3032

3133
extern crate serialize as rustc_serialize; // used by deriving

‎src/librustc_trans/mir/constant.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
use llvm::{self, ValueRef};
1212
use rustc::middle::const_val::{ConstEvalErr, ConstVal, ErrKind};
1313
use rustc_const_math::ConstInt::*;
14-
use rustc_const_math::ConstFloat::*;
14+
use rustc_const_math::ConstFloat;
1515
use rustc_const_math::{ConstInt, ConstMathErr};
1616
use rustc::hir::def_id::DefId;
1717
use rustc::infer::TransNormalize;
@@ -95,8 +95,13 @@ impl<'tcx> Const<'tcx> {
9595
-> Const<'tcx> {
9696
let llty = type_of::type_of(ccx, ty);
9797
let val = match cv {
98-
ConstVal::Float(F32(v)) => C_floating_f64(v as f64, llty),
99-
ConstVal::Float(F64(v)) => C_floating_f64(v, llty),
98+
ConstVal::Float(v) => {
99+
let v_f64 = match v {
100+
ConstFloat::F32(v) => f32::from_bits(v) as f64,
101+
ConstFloat::F64(v) => f64::from_bits(v)
102+
};
103+
C_floating_f64(v_f64, llty)
104+
}
100105
ConstVal::Bool(v) => C_bool(ccx, v),
101106
ConstVal::Integral(ref i) => return Const::from_constint(ccx, i),
102107
ConstVal::Str(ref v) => C_str_slice(ccx, v.clone()),

‎src/test/mir-opt/deaggregator_test.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ fn main() {}
2525
// bb0: {
2626
// _2 = _1;
2727
// _3 = _2;
28-
// _0 = Baz { x: _3, y: const F32(0), z: const false };
28+
// _0 = Baz { x: _3, y: const 0f32, z: const false };
2929
// return;
3030
// }
3131
// END rustc.node13.Deaggregator.before.mir
@@ -34,7 +34,7 @@ fn main() {}
3434
// _2 = _1;
3535
// _3 = _2;
3636
// (_0.0: usize) = _3;
37-
// (_0.1: f32) = const F32(0);
37+
// (_0.1: f32) = const 0f32;
3838
// (_0.2: bool) = const false;
3939
// return;
4040
// }

0 commit comments

Comments
 (0)
Please sign in to comment.