Skip to content

APFloat: Rewrite It In Rust and use it for deterministic floating-point CTFE. #43554

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Aug 5, 2017
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions src/librustc/ich/impls_const_math.rs
Original file line number Diff line number Diff line change
@@ -11,9 +11,9 @@
//! This module contains `HashStable` implementations for various data types
//! from `rustc_const_math` in no particular order.
impl_stable_hash_for!(enum ::rustc_const_math::ConstFloat {
F32(val),
F64(val)
impl_stable_hash_for!(struct ::rustc_const_math::ConstFloat {
ty,
bits
});

impl_stable_hash_for!(enum ::rustc_const_math::ConstInt {
11 changes: 11 additions & 0 deletions src/librustc_apfloat/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
authors = ["The Rust Project Developers"]
name = "rustc_apfloat"
version = "0.0.0"

[lib]
name = "rustc_apfloat"
path = "lib.rs"

[dependencies]
rustc_bitflags = { path = "../librustc_bitflags" }
2,733 changes: 2,733 additions & 0 deletions src/librustc_apfloat/ieee.rs

Large diffs are not rendered by default.

693 changes: 693 additions & 0 deletions src/librustc_apfloat/lib.rs

Large diffs are not rendered by default.

461 changes: 461 additions & 0 deletions src/librustc_apfloat/ppc.rs

Large diffs are not rendered by default.

6,891 changes: 6,891 additions & 0 deletions src/librustc_apfloat/tests/ieee.rs

Large diffs are not rendered by default.

655 changes: 655 additions & 0 deletions src/librustc_apfloat/tests/ppc.rs

Large diffs are not rendered by default.

55 changes: 27 additions & 28 deletions src/librustc_const_eval/eval.rs
Original file line number Diff line number Diff line change
@@ -26,6 +26,7 @@ use rustc::util::nodemap::DefIdMap;

use syntax::abi::Abi;
use syntax::ast;
use syntax::attr;
use rustc::hir::{self, Expr};
use syntax_pos::Span;

@@ -560,8 +561,15 @@ fn cast_const_int<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
ty::TyUint(ast::UintTy::Us) => {
Ok(Integral(Usize(ConstUsize::new_truncating(v, tcx.sess.target.uint_type))))
},
ty::TyFloat(ast::FloatTy::F64) => Ok(Float(F64(val.to_f64()))),
ty::TyFloat(ast::FloatTy::F32) => Ok(Float(F32(val.to_f32()))),
ty::TyFloat(fty) => {
if let Some(i) = val.to_u128() {
Ok(Float(ConstFloat::from_u128(i, fty)))
} else {
// The value must be negative, go through signed integers.
let i = val.to_u128_unchecked() as i128;
Ok(Float(ConstFloat::from_i128(i, fty)))
}
}
ty::TyRawPtr(_) => Err(ErrKind::UnimplementedConstVal("casting an address to a raw ptr")),
ty::TyChar => match val {
U8(u) => Ok(Char(u as char)),
@@ -574,30 +582,25 @@ fn cast_const_int<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
fn cast_const_float<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
val: ConstFloat,
ty: Ty<'tcx>) -> CastResult<'tcx> {
let int_width = |ty| {
ty::layout::Integer::from_attr(tcx, ty).size().bits() as usize
};
match ty.sty {
ty::TyInt(_) | ty::TyUint(_) => {
let i = match val {
F32(f) if f >= 0.0 => U128(f as u128),
F64(f) if f >= 0.0 => U128(f as u128),

F32(f) => I128(f as i128),
F64(f) => I128(f as i128)
};

if let (I128(_), &ty::TyUint(_)) = (i, &ty.sty) {
return Err(CannotCast);
ty::TyInt(ity) => {
if let Some(i) = val.to_i128(int_width(attr::SignedInt(ity))) {
cast_const_int(tcx, I128(i), ty)
} else {
Err(CannotCast)
}
}
ty::TyUint(uty) => {
if let Some(i) = val.to_u128(int_width(attr::UnsignedInt(uty))) {
cast_const_int(tcx, U128(i), ty)
} else {
Err(CannotCast)
}

cast_const_int(tcx, i, ty)
}
ty::TyFloat(ast::FloatTy::F64) => Ok(Float(F64(match val {
F32(f) => f as f64,
F64(f) => f
}))),
ty::TyFloat(ast::FloatTy::F32) => Ok(Float(F32(match val {
F64(f) => f as f32,
F32(f) => f
}))),
ty::TyFloat(fty) => Ok(Float(val.convert(fty))),
_ => Err(CannotCast),
}
}
@@ -691,11 +694,7 @@ fn lit_to_const<'a, 'tcx>(lit: &ast::LitKind,

fn parse_float<'tcx>(num: &str, fty: ast::FloatTy)
-> Result<ConstFloat, ErrKind<'tcx>> {
let val = match fty {
ast::FloatTy::F32 => num.parse::<f32>().map(F32),
ast::FloatTy::F64 => num.parse::<f64>().map(F64)
};
val.map_err(|_| {
ConstFloat::from_str(num, fty).map_err(|_| {
// FIXME(#31407) this is only necessary because float parsing is buggy
UnimplementedConstVal("could not evaluate float literal (see issue #31407)")
})
1 change: 1 addition & 0 deletions src/librustc_const_math/Cargo.toml
Original file line number Diff line number Diff line change
@@ -9,5 +9,6 @@ path = "lib.rs"
crate-type = ["dylib"]

[dependencies]
rustc_apfloat = { path = "../librustc_apfloat" }
serialize = { path = "../libserialize" }
syntax = { path = "../libsyntax" }
204 changes: 138 additions & 66 deletions src/librustc_const_math/float.rs
Original file line number Diff line number Diff line change
@@ -9,102 +9,164 @@
// except according to those terms.

use std::cmp::Ordering;
use std::hash;
use std::mem::transmute;
use std::num::ParseFloatError;

use syntax::ast;

use rustc_apfloat::{Float, FloatConvert, Status};
use rustc_apfloat::ieee::{Single, Double};

use super::err::*;

#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable)]
pub enum ConstFloat {
F32(f32),
F64(f64)
// Note that equality for `ConstFloat` means that the it is the same
// constant, not that the rust values are equal. In particular, `NaN
// == NaN` (at least if it's the same NaN; distinct encodings for NaN
// are considering unequal).
#[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
pub struct ConstFloat {
pub ty: ast::FloatTy,

// This is a bit inefficient but it makes conversions below more
// ergonomic, and all of this will go away once `miri` is merged.
pub bits: u128,
}
pub use self::ConstFloat::*;

impl ConstFloat {
/// Description of the type, not the value
pub fn description(&self) -> &'static str {
match *self {
F32(_) => "f32",
F64(_) => "f64",
}
self.ty.ty_to_string()
}

pub fn is_nan(&self) -> bool {
match *self {
F32(f) => f.is_nan(),
F64(f) => f.is_nan(),
match self.ty {
ast::FloatTy::F32 => Single::from_bits(self.bits).is_nan(),
ast::FloatTy::F64 => Double::from_bits(self.bits).is_nan(),
}
}

/// Compares the values if they are of the same type
pub fn try_cmp(self, rhs: Self) -> Result<Ordering, ConstMathErr> {
match (self, rhs) {
(F64(a), F64(b)) => {
match (self.ty, rhs.ty) {
(ast::FloatTy::F64, ast::FloatTy::F64) => {
let a = Double::from_bits(self.bits);
let b = Double::from_bits(rhs.bits);
// This is pretty bad but it is the existing behavior.
Ok(if a == b {
Ordering::Equal
} else if a < b {
Ordering::Less
} else {
Ordering::Greater
})
Ok(a.partial_cmp(&b).unwrap_or(Ordering::Greater))
}

(F32(a), F32(b)) => {
Ok(if a == b {
Ordering::Equal
} else if a < b {
Ordering::Less
} else {
Ordering::Greater
})
(ast::FloatTy::F32, ast::FloatTy::F32) => {
let a = Single::from_bits(self.bits);
let b = Single::from_bits(rhs.bits);
Ok(a.partial_cmp(&b).unwrap_or(Ordering::Greater))
}

_ => Err(CmpBetweenUnequalTypes),
}
}
}

/// Note that equality for `ConstFloat` means that the it is the same
/// constant, not that the rust values are equal. In particular, `NaN
/// == NaN` (at least if it's the same NaN; distinct encodings for NaN
/// are considering unequal).
impl PartialEq for ConstFloat {
fn eq(&self, other: &Self) -> bool {
match (*self, *other) {
(F64(a), F64(b)) => {
unsafe{transmute::<_,u64>(a) == transmute::<_,u64>(b)}
pub fn from_i128(input: i128, ty: ast::FloatTy) -> Self {
let bits = match ty {
ast::FloatTy::F32 => Single::from_i128(input).value.to_bits(),
ast::FloatTy::F64 => Double::from_i128(input).value.to_bits()
};
ConstFloat { bits, ty }
}

pub fn from_u128(input: u128, ty: ast::FloatTy) -> Self {
let bits = match ty {
ast::FloatTy::F32 => Single::from_u128(input).value.to_bits(),
ast::FloatTy::F64 => Double::from_u128(input).value.to_bits()
};
ConstFloat { bits, ty }
}

pub fn from_str(num: &str, ty: ast::FloatTy) -> Result<Self, ParseFloatError> {
let bits = match ty {
ast::FloatTy::F32 => {
let rust_bits = num.parse::<f32>()?.to_bits() as u128;
let apfloat = num.parse::<Single>().unwrap_or_else(|e| {
panic!("apfloat::ieee::Single failed to parse `{}`: {:?}", num, e);
});
let apfloat_bits = apfloat.to_bits();
assert!(rust_bits == apfloat_bits,
"apfloat::ieee::Single gave different result for `{}`: \
{}({:#x}) vs Rust's {}({:#x})",
num, apfloat, apfloat_bits,
Single::from_bits(rust_bits), rust_bits);
apfloat_bits
}
(F32(a), F32(b)) => {
unsafe{transmute::<_,u32>(a) == transmute::<_,u32>(b)}
ast::FloatTy::F64 => {
let rust_bits = num.parse::<f64>()?.to_bits() as u128;
let apfloat = num.parse::<Double>().unwrap_or_else(|e| {
panic!("apfloat::ieee::Double failed to parse `{}`: {:?}", num, e);
});
let apfloat_bits = apfloat.to_bits();
assert!(rust_bits == apfloat_bits,
"apfloat::ieee::Double gave different result for `{}`: \
{}({:#x}) vs Rust's {}({:#x})",
num, apfloat, apfloat_bits,
Double::from_bits(rust_bits), rust_bits);
apfloat_bits
}
_ => false
};
Ok(ConstFloat { bits, ty })
}

pub fn to_i128(self, width: usize) -> Option<i128> {
assert!(width <= 128);
let r = match self.ty {
ast::FloatTy::F32 => Single::from_bits(self.bits).to_i128(width),
ast::FloatTy::F64 => Double::from_bits(self.bits).to_i128(width)
};
if r.status.intersects(Status::INVALID_OP) {
None
} else {
Some(r.value)
}
}
}

impl Eq for ConstFloat {}
pub fn to_u128(self, width: usize) -> Option<u128> {
assert!(width <= 128);
let r = match self.ty {
ast::FloatTy::F32 => Single::from_bits(self.bits).to_u128(width),
ast::FloatTy::F64 => Double::from_bits(self.bits).to_u128(width)
};
if r.status.intersects(Status::INVALID_OP) {
None
} else {
Some(r.value)
}
}

impl hash::Hash for ConstFloat {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
match *self {
F64(a) => {
unsafe { transmute::<_,u64>(a) }.hash(state)
pub fn convert(self, to: ast::FloatTy) -> Self {
let bits = match (self.ty, to) {
(ast::FloatTy::F32, ast::FloatTy::F32) |
(ast::FloatTy::F64, ast::FloatTy::F64) => return self,

(ast::FloatTy::F32, ast::FloatTy::F64) => {
Double::to_bits(Single::from_bits(self.bits).convert(&mut false).value)
}
F32(a) => {
unsafe { transmute::<_,u32>(a) }.hash(state)
(ast::FloatTy::F64, ast::FloatTy::F32) => {
Single::to_bits(Double::from_bits(self.bits).convert(&mut false).value)
}
}
};
ConstFloat { bits, ty: to }
}
}

impl ::std::fmt::Display for ConstFloat {
fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
match *self {
F32(f) => write!(fmt, "{}f32", f),
F64(f) => write!(fmt, "{}f64", f),
match self.ty {
ast::FloatTy::F32 => write!(fmt, "{:#}", Single::from_bits(self.bits))?,
ast::FloatTy::F64 => write!(fmt, "{:#}", Double::from_bits(self.bits))?,
}
write!(fmt, "{}", self.ty)
}
}

impl ::std::fmt::Debug for ConstFloat {
fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
::std::fmt::Display::fmt(self, fmt)
}
}

@@ -113,11 +175,20 @@ macro_rules! derive_binop {
impl ::std::ops::$op for ConstFloat {
type Output = Result<Self, ConstMathErr>;
fn $func(self, rhs: Self) -> Result<Self, ConstMathErr> {
match (self, rhs) {
(F32(a), F32(b)) => Ok(F32(a.$func(b))),
(F64(a), F64(b)) => Ok(F64(a.$func(b))),
_ => Err(UnequalTypes(Op::$op)),
}
let bits = match (self.ty, rhs.ty) {
(ast::FloatTy::F32, ast::FloatTy::F32) =>{
let a = Single::from_bits(self.bits);
let b = Single::from_bits(rhs.bits);
a.$func(b).value.to_bits()
}
(ast::FloatTy::F64, ast::FloatTy::F64) => {
let a = Double::from_bits(self.bits);
let b = Double::from_bits(rhs.bits);
a.$func(b).value.to_bits()
}
_ => return Err(UnequalTypes(Op::$op)),
};
Ok(ConstFloat { bits, ty: self.ty })
}
}
}
@@ -132,9 +203,10 @@ derive_binop!(Rem, rem);
impl ::std::ops::Neg for ConstFloat {
type Output = Self;
fn neg(self) -> Self {
match self {
F32(f) => F32(-f),
F64(f) => F64(-f),
}
let bits = match self.ty {
ast::FloatTy::F32 => (-Single::from_bits(self.bits)).to_bits(),
ast::FloatTy::F64 => (-Double::from_bits(self.bits)).to_bits(),
};
ConstFloat { bits, ty: self.ty }
}
}
42 changes: 0 additions & 42 deletions src/librustc_const_math/int.rs
Original file line number Diff line number Diff line change
@@ -211,48 +211,6 @@ impl ConstInt {
}
}

pub fn to_f32(self) -> f32 {
match self {
I8(i) => i as f32,
I16(i) => i as f32,
I32(i) => i as f32,
I64(i) => i as f32,
I128(i) => i as f32,
Isize(Is16(i)) => i as f32,
Isize(Is32(i)) => i as f32,
Isize(Is64(i)) => i as f32,
U8(i) => i as f32,
U16(i) => i as f32,
U32(i) => i as f32,
U64(i) => i as f32,
U128(i) => i as f32,
Usize(Us16(i)) => i as f32,
Usize(Us32(i)) => i as f32,
Usize(Us64(i)) => i as f32,
}
}

pub fn to_f64(self) -> f64 {
match self {
I8(i) => i as f64,
I16(i) => i as f64,
I32(i) => i as f64,
I64(i) => i as f64,
I128(i) => i as f64,
Isize(Is16(i)) => i as f64,
Isize(Is32(i)) => i as f64,
Isize(Is64(i)) => i as f64,
U8(i) => i as f64,
U16(i) => i as f64,
U32(i) => i as f64,
U64(i) => i as f64,
U128(i) => i as f64,
Usize(Us16(i)) => i as f64,
Usize(Us32(i)) => i as f64,
Usize(Us64(i)) => i as f64,
}
}

pub fn is_negative(&self) -> bool {
match *self {
I8(v) => v < 0,
2 changes: 2 additions & 0 deletions src/librustc_const_math/lib.rs
Original file line number Diff line number Diff line change
@@ -26,6 +26,8 @@
#![feature(i128)]
#![feature(i128_type)]

extern crate rustc_apfloat;

extern crate syntax;

extern crate serialize as rustc_serialize; // used by deriving
1 change: 0 additions & 1 deletion src/librustc_llvm/ffi.rs
Original file line number Diff line number Diff line change
@@ -598,7 +598,6 @@ extern "C" {
// Operations on scalar constants
pub fn LLVMConstInt(IntTy: TypeRef, N: c_ulonglong, SignExtend: Bool) -> ValueRef;
pub fn LLVMConstIntOfArbitraryPrecision(IntTy: TypeRef, Wn: c_uint, Ws: *const u64) -> ValueRef;
pub fn LLVMConstReal(RealTy: TypeRef, N: f64) -> ValueRef;
pub fn LLVMConstIntGetZExtValue(ConstantVal: ValueRef) -> c_ulonglong;
pub fn LLVMConstIntGetSExtValue(ConstantVal: ValueRef) -> c_longlong;
pub fn LLVMRustConstInt128Get(ConstantVal: ValueRef, SExt: bool,
3 changes: 1 addition & 2 deletions src/librustc_trans/base.rs
Original file line number Diff line number Diff line change
@@ -844,8 +844,7 @@ fn create_imps(sess: &Session,
let imp = llvm::LLVMAddGlobal(llvm_module.llmod,
i8p_ty.to_ref(),
imp_name.as_ptr() as *const _);
let init = llvm::LLVMConstBitCast(val, i8p_ty.to_ref());
llvm::LLVMSetInitializer(imp, init);
llvm::LLVMSetInitializer(imp, consts::ptrcast(val, i8p_ty));
llvm::LLVMRustSetLinkage(imp, llvm::Linkage::ExternalLinkage);
}
}
6 changes: 0 additions & 6 deletions src/librustc_trans/common.rs
Original file line number Diff line number Diff line change
@@ -223,12 +223,6 @@ pub fn C_big_integral(t: Type, u: u128) -> ValueRef {
}
}

pub fn C_floating_f64(f: f64, t: Type) -> ValueRef {
unsafe {
llvm::LLVMConstReal(t.to_ref(), f)
}
}

pub fn C_nil(ccx: &CrateContext) -> ValueRef {
C_struct(ccx, &[], false)
}
6 changes: 6 additions & 0 deletions src/librustc_trans/consts.rs
Original file line number Diff line number Diff line change
@@ -36,6 +36,12 @@ pub fn ptrcast(val: ValueRef, ty: Type) -> ValueRef {
}
}

pub fn bitcast(val: ValueRef, ty: Type) -> ValueRef {
unsafe {
llvm::LLVMConstBitCast(val, ty.to_ref())
}
}

pub fn addr_of_mut(ccx: &CrateContext,
cv: ValueRef,
align: machine::llalign,
13 changes: 9 additions & 4 deletions src/librustc_trans/mir/constant.rs
Original file line number Diff line number Diff line change
@@ -11,7 +11,6 @@
use llvm::{self, ValueRef};
use rustc::middle::const_val::{ConstEvalErr, ConstVal, ErrKind};
use rustc_const_math::ConstInt::*;
use rustc_const_math::ConstFloat::*;
use rustc_const_math::{ConstInt, ConstMathErr};
use rustc::hir::def_id::DefId;
use rustc::infer::TransNormalize;
@@ -27,7 +26,7 @@ use abi::{self, Abi};
use callee;
use builder::Builder;
use common::{self, CrateContext, const_get_elt, val_ty};
use common::{C_array, C_bool, C_bytes, C_floating_f64, C_integral, C_big_integral};
use common::{C_array, C_bool, C_bytes, C_integral, C_big_integral, C_u32, C_u64};
use common::{C_null, C_struct, C_str_slice, C_undef, C_uint, C_vector, is_undef};
use common::const_to_opt_u128;
use consts;
@@ -37,6 +36,7 @@ use type_::Type;
use value::Value;

use syntax_pos::Span;
use syntax::ast;

use std::fmt;
use std::ptr;
@@ -95,8 +95,13 @@ impl<'tcx> Const<'tcx> {
-> Const<'tcx> {
let llty = type_of::type_of(ccx, ty);
let val = match cv {
ConstVal::Float(F32(v)) => C_floating_f64(v as f64, llty),
ConstVal::Float(F64(v)) => C_floating_f64(v, llty),
ConstVal::Float(v) => {
let bits = match v.ty {
ast::FloatTy::F32 => C_u32(ccx, v.bits as u32),
ast::FloatTy::F64 => C_u64(ccx, v.bits as u64)
};
consts::bitcast(bits, llty)
}
ConstVal::Bool(v) => C_bool(ccx, v),
ConstVal::Integral(ref i) => return Const::from_constint(ccx, i),
ConstVal::Str(ref v) => C_str_slice(ccx, v.clone()),
4 changes: 2 additions & 2 deletions src/test/mir-opt/deaggregator_test.rs
Original file line number Diff line number Diff line change
@@ -25,7 +25,7 @@ fn main() {}
// bb0: {
// _2 = _1;
// _3 = _2;
// _0 = Baz { x: _3, y: const F32(0), z: const false };
// _0 = Baz { x: _3, y: const 0f32, z: const false };
// return;
// }
// END rustc.node13.Deaggregator.before.mir
@@ -34,7 +34,7 @@ fn main() {}
// _2 = _1;
// _3 = _2;
// (_0.0: usize) = _3;
// (_0.1: f32) = const F32(0);
// (_0.1: f32) = const 0f32;
// (_0.2: bool) = const false;
// return;
// }