Skip to content

Instantly share code, notes, and snippets.

@bjorn3
Created October 31, 2020 14:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bjorn3/de955bd055b0dbd10ba97cac700ec484 to your computer and use it in GitHub Desktop.
Save bjorn3/de955bd055b0dbd10ba97cac700ec484 to your computer and use it in GitHub Desktop.
Repro for rustc_codegen_cranelift#1097
diff --git a/compiler/rustc_apfloat/src/lib.rs b/compiler/rustc_apfloat/src/lib.rs
index d3832ac4657..577818edc23 100644
--- a/compiler/rustc_apfloat/src/lib.rs
+++ b/compiler/rustc_apfloat/src/lib.rs
@@ -418,13 +418,7 @@ impl IeeeFloat<SingleS> {
let mut carry = val;
for x in &mut dec_sig {
- let [low, mut high] = sig::widening_mul(*x, multiplier);
-
- let (low, overflow) = low.overflowing_add(carry);
- high += overflow as Limb;
-
- *x = low;
- carry = high;
+ unreachable!();
}
if carry > 0 {
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![no_std]
#![forbid(unsafe_code)]
#![feature(nll)]
#![feature(or_patterns)]
extern crate alloc;
use core::ops::Neg;
bitflags::bitflags! {
#[must_use]
struct Status: u8 {
const OK = 0x00;
const INVALID_OP = 0x01;
const DIV_BY_ZERO = 0x02;
const OVERFLOW = 0x04;
const UNDERFLOW = 0x08;
const INEXACT = 0x10;
}
}
#[must_use]
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
struct StatusAnd<T> {
status: Status,
value: T,
}
impl Status {
fn and<T>(self, value: T) -> StatusAnd<T> {
StatusAnd { status: self, value }
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum Category {
Infinity,
NaN,
Normal,
Zero,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum Round {
NearestTiesToEven,
TowardPositive,
TowardNegative,
TowardZero,
NearestTiesToAway,
}
impl Neg for Round {
type Output = Round;
fn neg(self) -> Round {
match self {
Round::TowardPositive => Round::TowardNegative,
Round::TowardNegative => Round::TowardPositive,
Round::NearestTiesToEven | Round::TowardZero | Round::NearestTiesToAway => self,
}
}
}
type ExpInt = i16;
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct ParseError(pub &'static str);
use core::cmp;
use core::mem;
use core::marker::PhantomData;
use smallvec::{smallvec, SmallVec};
#[must_use]
pub struct IeeeFloat<S> {
sig: [Limb; 1],
exp: ExpInt,
category: Category,
sign: bool,
marker: PhantomData<S>,
}
type Limb = u128;
const LIMB_BITS: usize = 128;
fn limbs_for_bits(bits: usize) -> usize {
(bits + LIMB_BITS - 1) / LIMB_BITS
}
#[must_use]
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum Loss {
ExactlyZero, // 000000
LessThanHalf, // 0xxxxx x's not all zero
ExactlyHalf, // 100000
MoreThanHalf, // 1xxxxx x's not all zero
}
trait Semantics: Sized {
const BITS: usize;
const PRECISION: usize;
const MAX_EXP: ExpInt;
const MIN_EXP: ExpInt = -Self::MAX_EXP + 1;
const QNAN_BIT: usize = Self::PRECISION - 2;
const QNAN_SIGNIFICAND: Limb = 1 << Self::QNAN_BIT;
}
impl<S> Copy for IeeeFloat<S> {}
impl<S> Clone for IeeeFloat<S> {
fn clone(&self) -> Self {
*self
}
}
macro_rules! ieee_semantics {
($($name:ident = $sem:ident($bits:tt : $exp_bits:tt)),*) => {
$(pub struct $sem;)*
$(pub type $name = IeeeFloat<$sem>;)*
$(impl Semantics for $sem {
const BITS: usize = $bits;
const PRECISION: usize = ($bits - 1 - $exp_bits) + 1;
const MAX_EXP: ExpInt = (1 << ($exp_bits - 1)) - 1;
})*
}
}
ieee_semantics! {
Single = SingleS(32:8)
}
impl ::core::str::FromStr for IeeeFloat<SingleS> {
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, ParseError> {
Self::from_str_r(s, Round::NearestTiesToEven).map(|x| x.value)
}
}
impl<S> Neg for IeeeFloat<S> {
type Output = Self;
fn neg(mut self) -> Self {
self.sign = !self.sign;
self
}
}
impl IeeeFloat<SingleS> {
fn is_finite(self) -> bool {
!self.is_nan() && !self.is_infinite()
}
fn is_zero(self) -> bool {
self.category() == Category::Zero
}
fn is_infinite(self) -> bool {
self.category() == Category::Infinity
}
fn is_nan(self) -> bool {
self.category() == Category::NaN
}
fn is_finite_non_zero(self) -> bool {
self.is_finite() && !self.is_zero()
}
const ZERO: Self = IeeeFloat {
sig: [0],
exp: SingleS::MIN_EXP - 1,
category: Category::Zero,
sign: false,
marker: PhantomData,
};
const INFINITY: Self = IeeeFloat {
sig: [0],
exp: SingleS::MAX_EXP + 1,
category: Category::Infinity,
sign: false,
marker: PhantomData,
};
const NAN: Self = IeeeFloat {
sig: [SingleS::QNAN_SIGNIFICAND],
exp: SingleS::MAX_EXP + 1,
category: Category::NaN,
sign: false,
marker: PhantomData,
};
fn largest() -> Self {
IeeeFloat {
sig: [(1 << SingleS::PRECISION) - 1],
exp: SingleS::MAX_EXP,
category: Category::Normal,
sign: false,
marker: PhantomData,
}
}
const SMALLEST: Self = IeeeFloat {
sig: [1],
exp: SingleS::MIN_EXP,
category: Category::Normal,
sign: false,
marker: PhantomData,
};
fn from_str_r(s: &str, round: Round) -> Result<StatusAnd<Self>, ParseError> {
if s.is_empty() {
return Err(ParseError("Invalid string length"));
}
match s {
"inf" | "INFINITY" => return Ok(Status::OK.and(Self::INFINITY)),
"-inf" | "-INFINITY" => return Ok(Status::OK.and(-Self::INFINITY)),
"nan" | "NaN" => return Ok(Status::OK.and(Self::NAN)),
"-nan" | "-NaN" => return Ok(Status::OK.and(-Self::NAN)),
_ => {}
}
if s.starts_with('-') || s.starts_with('+') {
unreachable!();
}
let r = if s.starts_with("0x") || s.starts_with("0X") {
unreachable!(); //Self::from_hexadecimal_string(s, round)?
} else {
Self::from_decimal_string(s, round)?
};
Ok(r)
}
fn category(self) -> Category {
self.category
}
}
impl IeeeFloat<SingleS> {
fn overflow_result(round: Round) -> StatusAnd<Self> {
match round {
Round::NearestTiesToEven | Round::NearestTiesToAway | Round::TowardPositive => {
(Status::OVERFLOW | Status::INEXACT).and(Self::INFINITY)
}
Round::TowardNegative | Round::TowardZero => Status::INEXACT.and(Self::largest()),
}
}
fn round_away_from_zero(&self, round: Round, loss: Loss, bit: usize) -> bool {
assert!(self.is_finite_non_zero() || self.is_zero());
assert_ne!(loss, Loss::ExactlyZero);
match round {
Round::NearestTiesToAway => loss == Loss::ExactlyHalf || loss == Loss::MoreThanHalf,
Round::NearestTiesToEven => {
if loss == Loss::MoreThanHalf {
return true;
}
if loss == Loss::ExactlyHalf && self.category != Category::Zero {
return sig::get_bit(&self.sig, bit);
}
false
}
Round::TowardZero => false,
Round::TowardPositive => !self.sign,
Round::TowardNegative => self.sign,
}
}
fn normalize(mut self, round: Round, loss: Loss) -> StatusAnd<Self> {
if !self.is_finite_non_zero() {
return Status::OK.and(self);
}
let mut omsb = sig::omsb(&self.sig);
if omsb > 0 {
let final_exp =
self.exp.saturating_add(omsb as ExpInt - SingleS::PRECISION as ExpInt);
if final_exp > SingleS::MAX_EXP {
unreachable!();
}
if final_exp < SingleS::MIN_EXP {
unreachable!();
}
if final_exp < self.exp {
unreachable!();
}
if final_exp > self.exp {
unreachable!();
}
}
if loss == Loss::ExactlyZero {
unreachable!();
}
if self.round_away_from_zero(round, loss, 0) {
if omsb == 0 {
unreachable!();
}
assert_eq!(sig::increment(&mut self.sig), 0);
omsb = sig::omsb(&self.sig);
if omsb == SingleS::PRECISION + 1 {
unreachable!();
}
}
if omsb == SingleS::PRECISION {
return Status::INEXACT.and(self);
}
unreachable!();
}
fn from_decimal_string(s: &str, round: Round) -> Result<StatusAnd<Self>, ParseError> {
let mut any_digits = false;
let mut dec_exp = 0i32;
let mut first_sig_digit = None;
let mut last_sig_digit = 0;
let mut dot = s.len();
for (p, c) in s.char_indices() {
if c == '.' {
if dot != s.len() {
return Err(ParseError("String contains multiple dots"));
}
dot = p;
} else if let Some(dec_value) = c.to_digit(10) {
any_digits = true;
if dec_value != 0 {
if first_sig_digit.is_none() {
first_sig_digit = Some(p);
}
last_sig_digit = p;
}
} else {
unreachable!();
}
}
if !any_digits {
return Err(ParseError("Significand has no digits"));
}
let first_sig_digit = match first_sig_digit {
Some(p) => p,
None => return Ok(Status::OK.and(Self::ZERO)),
};
if dot > last_sig_digit {
dec_exp = dec_exp.saturating_add((dot - last_sig_digit - 1) as i32);
} else {
dec_exp = dec_exp.saturating_sub((last_sig_digit - dot) as i32);
}
let significand_digits = last_sig_digit - first_sig_digit + 1
- (dot > first_sig_digit && dot < last_sig_digit) as usize;
let normalized_exp = dec_exp.saturating_add(significand_digits as i32 - 1);
if normalized_exp.saturating_sub(1).saturating_mul(42039) >= 12655 * SingleS::MAX_EXP as i32
{
return Ok(Self::overflow_result(round));
}
if normalized_exp.saturating_add(1).saturating_mul(28738)
<= 8651 * (SingleS::MIN_EXP as i32 - SingleS::PRECISION as i32)
{
let r =
if round == Round::TowardPositive { IeeeFloat::SMALLEST } else { IeeeFloat::ZERO };
return Ok((Status::UNDERFLOW | Status::INEXACT).and(r));
}
let max_limbs = limbs_for_bits(1 + 196 * significand_digits / 59);
let mut dec_sig: SmallVec<[Limb; 1]> = SmallVec::with_capacity(max_limbs);
let mut chars = s[first_sig_digit..=last_sig_digit].chars();
loop {
let mut val = 0;
let mut multiplier = 1;
loop {
let dec_value = match chars.next() {
Some('.') => continue,
Some(c) => c.to_digit(10).unwrap(),
None => break,
};
multiplier *= 10;
val = val * 10 + dec_value as Limb;
if multiplier > (!0 - 9) / 10 {
break;
}
}
if multiplier == 1 {
break;
}
let mut carry = val;
for x in &mut dec_sig {
let [low, mut high] = sig::widening_mul(*x, multiplier);
let (low, overflow) = low.overflowing_add(carry);
high += overflow as Limb;
*x = low;
carry = high;
}
if carry > 0 {
dec_sig.push(carry);
}
}
let (pow5_full, mut pow5_calc, mut sig_calc, mut sig_scratch_calc) = {
let mut power = dec_exp.abs() as usize;
const FIRST_EIGHT_POWERS: [Limb; 8] = [1, 5, 25, 125, 625, 3125, 15625, 78125];
let p5_scratch = smallvec![];
let p5: SmallVec<[Limb; 1]> = smallvec![FIRST_EIGHT_POWERS[4]];
let r_scratch = smallvec![];
let r: SmallVec<[Limb; 1]> = smallvec![FIRST_EIGHT_POWERS[power & 7]];
power >>= 3;
while power > 0 {
unreachable!();
}
(r, r_scratch, p5, p5_scratch)
};
let mut attempt = 0;
loop {
let calc_precision = (LIMB_BITS << attempt) - 1;
attempt += 1;
let calc_normal_from_limbs = |sig: &mut SmallVec<[Limb; 1]>,
limbs: &[Limb]|
-> StatusAnd<ExpInt> {
sig.resize(limbs_for_bits(calc_precision), 0);
let (mut loss, mut exp) = sig::from_limbs(sig, limbs, calc_precision);
let mut omsb = sig::omsb(sig);
assert_ne!(omsb, 0);
let final_exp = exp.saturating_add(omsb as ExpInt - calc_precision as ExpInt);
if final_exp < exp {
assert_eq!(loss, Loss::ExactlyZero);
let exp_change = (exp - final_exp) as usize;
sig::shift_left(sig, &mut exp, exp_change);
return Status::OK.and(exp);
}
if final_exp > exp {
unreachable!();
}
assert_eq!(omsb, calc_precision);
if loss == Loss::ExactlyZero {
unreachable!();
}
if loss == Loss::MoreThanHalf || loss == Loss::ExactlyHalf && sig::get_bit(sig, 0) {
unreachable!();
}
Status::INEXACT.and(exp)
};
let (status, mut exp) = match calc_normal_from_limbs(&mut sig_calc, &dec_sig) {
StatusAnd { status, value } => (status, value),
};
let (pow5_status, pow5_exp) = match calc_normal_from_limbs(&mut pow5_calc, &pow5_full) {
StatusAnd { status, value } => (status, value),
};
exp += dec_exp as ExpInt;
let mut used_bits = SingleS::PRECISION;
let mut truncated_bits = calc_precision - used_bits;
let half_ulp_err1 = (status != Status::OK) as Limb;
let (calc_loss, half_ulp_err2);
if dec_exp >= 0 {
unreachable!();
} else {
exp -= pow5_exp;
sig_scratch_calc.resize(sig_calc.len(), 0);
calc_loss = sig::div(
&mut sig_scratch_calc,
&mut exp,
&mut sig_calc,
&mut pow5_calc,
calc_precision,
);
mem::swap(&mut sig_calc, &mut sig_scratch_calc);
if exp < SingleS::MIN_EXP {
truncated_bits += (SingleS::MIN_EXP - exp) as usize;
used_bits = calc_precision.saturating_sub(truncated_bits);
}
half_ulp_err2 =
2 * (pow5_status != Status::OK || calc_loss != Loss::ExactlyZero) as Limb;
}
assert!(sig::get_bit(&sig_calc, calc_precision - 1));
assert!(half_ulp_err1 < 2 || half_ulp_err2 < 2 || (half_ulp_err1 + half_ulp_err2 < 8));
let inexact = (calc_loss != Loss::ExactlyZero) as Limb;
let half_ulp_err = if half_ulp_err1 + half_ulp_err2 == 0 {
unreachable!();
} else {
inexact + 2 * (half_ulp_err1 + half_ulp_err2)
};
let ulps_from_boundary = {
let bits = calc_precision - used_bits - 1;
let i = bits / LIMB_BITS;
let limb = sig_calc[i] & (!0 >> (LIMB_BITS - 1 - bits % LIMB_BITS));
let boundary = match round {
Round::NearestTiesToEven | Round::NearestTiesToAway => 1 << (bits % LIMB_BITS),
_ => 0,
};
if i == 0 {
let delta = limb.wrapping_sub(boundary);
cmp::min(delta, delta.wrapping_neg())
} else {
unreachable!();
}
};
if ulps_from_boundary.saturating_mul(2) >= half_ulp_err {
let mut r = IeeeFloat {
sig: [0],
exp,
category: Category::Normal,
sign: false,
marker: PhantomData,
};
sig::extract(&mut r.sig, &sig_calc, used_bits, calc_precision - used_bits);
r.exp += (SingleS::PRECISION - used_bits) as ExpInt;
let loss = Loss::through_truncation(&sig_calc, truncated_bits);
return Ok(r.normalize(round, loss));
}
}
}
}
impl Loss {
fn combine(self, less_significant: Loss) -> Loss {
let mut more_significant = self;
if less_significant != Loss::ExactlyZero {
if more_significant == Loss::ExactlyZero {
more_significant = Loss::LessThanHalf;
} else if more_significant == Loss::ExactlyHalf {
more_significant = Loss::MoreThanHalf;
}
}
more_significant
}
fn through_truncation(limbs: &[Limb], bits: usize) -> Loss {
if bits == 0 {
return Loss::ExactlyZero;
}
let half_bit = bits - 1;
let half_limb = half_bit / LIMB_BITS;
let (half_limb, rest) = if half_limb < limbs.len() {
(limbs[half_limb], &limbs[..half_limb])
} else {
unreachable!();
};
let half = 1 << (half_bit % LIMB_BITS);
let has_half = half_limb & half != 0;
let has_rest = half_limb & (half - 1) != 0 || !sig::is_all_zeros(rest);
match (has_half, has_rest) {
(false, false) => Loss::ExactlyZero,
(false, true) => Loss::LessThanHalf,
(true, false) => Loss::ExactlyHalf,
(true, true) => Loss::MoreThanHalf,
}
}
}
mod sig {
use super::{limbs_for_bits, ExpInt, Limb, Loss, LIMB_BITS};
use core::cmp::Ordering;
pub(super) fn is_all_zeros(limbs: &[Limb]) -> bool {
limbs.iter().all(|&l| l == 0)
}
pub(super) fn olsb(limbs: &[Limb]) -> usize {
limbs
.iter()
.enumerate()
.find(|(_, &limb)| limb != 0)
.map_or(0, |(i, limb)| i * LIMB_BITS + limb.trailing_zeros() as usize + 1)
}
pub(super) fn omsb(limbs: &[Limb]) -> usize {
limbs
.iter()
.enumerate()
.rfind(|(_, &limb)| limb != 0)
.map_or(0, |(i, limb)| (i + 1) * LIMB_BITS - limb.leading_zeros() as usize)
}
pub(super) fn cmp(a: &[Limb], b: &[Limb]) -> Ordering {
assert_eq!(a.len(), b.len());
for (a, b) in a.iter().zip(b).rev() {
match a.cmp(b) {
Ordering::Equal => {}
o => return o,
}
}
Ordering::Equal
}
pub(super) fn get_bit(limbs: &[Limb], bit: usize) -> bool {
limbs[bit / LIMB_BITS] & (1 << (bit % LIMB_BITS)) != 0
}
pub(super) fn shift_left(dst: &mut [Limb], exp: &mut ExpInt, bits: usize) {
if bits > 0 {
*exp = exp.checked_sub(bits as ExpInt).unwrap();
let jump = bits / LIMB_BITS;
let shift = bits % LIMB_BITS;
for i in (0..dst.len()).rev() {
let mut limb;
if i < jump {
limb = 0;
} else {
limb = dst[i - jump];
if shift > 0 {
limb <<= shift;
if i > jump {
unreachable!();
}
}
}
dst[i] = limb;
}
}
}
pub(super) fn shift_right(dst: &mut [Limb], exp: &mut ExpInt, bits: usize) -> Loss {
let loss = Loss::through_truncation(dst, bits);
if bits > 0 {
*exp = exp.checked_add(bits as ExpInt).unwrap();
let jump = bits / LIMB_BITS;
let shift = bits % LIMB_BITS;
for i in 0..dst.len() {
let mut limb;
if i + jump >= dst.len() {
unreachable!();
} else {
limb = dst[i + jump];
if shift > 0 {
limb >>= shift;
if i + jump + 1 < dst.len() {
unreachable!();
}
}
}
dst[i] = limb;
}
}
loss
}
pub(super) fn extract(dst: &mut [Limb], src: &[Limb], src_bits: usize, src_lsb: usize) {
if src_bits == 0 {
unreachable!();
}
let dst_limbs = limbs_for_bits(src_bits);
assert!(dst_limbs <= dst.len());
let src = &src[src_lsb / LIMB_BITS..];
dst[..dst_limbs].copy_from_slice(&src[..dst_limbs]);
let shift = src_lsb % LIMB_BITS;
let _: Loss = shift_right(&mut dst[..dst_limbs], &mut 0, shift);
let n = dst_limbs * LIMB_BITS - shift;
if n < src_bits {
unreachable!();
} else if n > src_bits && src_bits % LIMB_BITS > 0 {
dst[dst_limbs - 1] &= (1 << (src_bits % LIMB_BITS)) - 1;
}
for _ in &mut dst[dst_limbs..] {
unreachable!();
}
}
pub(super) fn from_limbs(dst: &mut [Limb], src: &[Limb], precision: usize) -> (Loss, ExpInt) {
let omsb = omsb(src);
if precision <= omsb {
unreachable!();
} else {
extract(dst, src, omsb, 0);
(Loss::ExactlyZero, precision as ExpInt - 1)
}
}
pub(super) fn each_chunk<F: FnMut(Limb) -> Limb>(limbs: &mut [Limb], bits: usize, mut f: F) {
assert_eq!(LIMB_BITS % bits, 0);
for limb in limbs.iter_mut().rev() {
let mut r = 0;
for i in (0..LIMB_BITS / bits).rev() {
r |= f((*limb >> (i * bits)) & ((1 << bits) - 1)) << (i * bits);
}
*limb = r;
}
}
pub(super) fn increment(dst: &mut [Limb]) -> Limb {
for x in dst {
*x = x.wrapping_add(1);
if *x != 0 {
return 0;
}
}
1
}
pub(super) fn widening_mul(a: Limb, b: Limb) -> [Limb; 2] {
let wide = [0, 0];
if a == 0 || b == 0 {
return wide;
}
unreachable!();
}
pub(super) fn div(
quotient: &mut [Limb],
exp: &mut ExpInt,
dividend: &mut [Limb],
divisor: &mut [Limb],
precision: usize,
) -> Loss {
let bits = precision - omsb(divisor);
shift_left(divisor, &mut 0, bits);
*exp += bits as ExpInt;
let bits = precision - omsb(dividend);
shift_left(dividend, exp, bits);
let olsb_divisor = olsb(divisor);
if olsb_divisor == precision {
unreachable!();
}
if cmp(dividend, divisor) == Ordering::Less {
shift_left(dividend, exp, 1);
assert_ne!(cmp(dividend, divisor), Ordering::Less)
}
let lost_fraction = |dividend: &[Limb], divisor: &[Limb]| match cmp(dividend, divisor) {
Ordering::Greater => Loss::MoreThanHalf,
Ordering::Equal => Loss::ExactlyHalf,
Ordering::Less => {
if is_all_zeros(dividend) {
Loss::ExactlyZero
} else {
Loss::LessThanHalf
}
}
};
let divisor_bits = precision - (olsb_divisor - 1);
macro_rules! try_short_div {
($W:ty, $H:ty, $half:expr) => {
if divisor_bits * 2 <= $half {
let _: Loss = shift_right(divisor, &mut 0, olsb_divisor - 1);
let divisor = divisor[0] as $H as $W;
let top_limb = *dividend.last().unwrap();
let mut rem = (top_limb >> (LIMB_BITS - (divisor_bits - 1))) as $H;
shift_left(dividend, &mut 0, divisor_bits - 1);
each_chunk(dividend, $half, |chunk| {
let chunk = chunk as $H;
let combined = ((rem as $W) << $half) | (chunk as $W);
rem = (combined % divisor) as $H;
(combined / divisor) as $H as Limb
});
quotient.copy_from_slice(dividend);
return lost_fraction(&[(rem as Limb) << 1], &[divisor as Limb]);
}
};
}
try_short_div!(u32, u16, 16);
unreachable!();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment