use llvm_native_core::x86::{
x86_calling_convention::X86CallingConvention, x86_register_info::X86RegisterInfo,
x86_subtarget::X86Subtarget,
};
#[inline]
fn decompose_f32(x: f32) -> (bool, i32, u32) {
let bits = x.to_bits();
let sign = (bits >> 31) != 0;
let exponent = ((bits >> 23) & 0xFF) as i32;
let mantissa = bits & 0x007F_FFFF;
(sign, exponent, mantissa)
}
#[inline]
fn compose_f32(sign: bool, exponent: i32, mantissa: u32) -> f32 {
let s = if sign { 1u32 << 31 } else { 0 };
let e = ((exponent as u32) & 0xFF) << 23;
let m = mantissa & 0x007F_FFFF;
f32::from_bits(s | e | m)
}
#[inline]
fn decompose_f64(x: f64) -> (bool, i32, u64) {
let bits = x.to_bits();
let sign = (bits >> 63) != 0;
let exponent = ((bits >> 52) & 0x7FF) as i32;
let mantissa = bits & 0x000F_FFFF_FFFF_FFFF;
(sign, exponent, mantissa)
}
#[inline]
fn compose_f64(sign: bool, exponent: i32, mantissa: u64) -> f64 {
let s = if sign { 1u64 << 63 } else { 0 };
let e = ((exponent as u64) & 0x7FF) << 52;
let m = mantissa & 0x000F_FFFF_FFFF_FFFF;
f64::from_bits(s | e | m)
}
const F64_SIGN_MASK: u64 = 1u64 << 63;
const F64_EXP_MASK: u64 = 0x7FF << 52;
const F64_MANT_MASK: u64 = 0x000F_FFFF_FFFF_FFFF;
const F64_EXP_BIAS: i32 = 1023;
const F64_MANT_BITS: u32 = 52;
const F32_SIGN_MASK: u32 = 1u32 << 31;
const F32_EXP_MASK: u32 = 0xFF << 23;
const F32_MANT_MASK: u32 = 0x007F_FFFF;
const F32_EXP_BIAS: i32 = 127;
const F32_MANT_BITS: u32 = 23;
#[inline]
fn f32_is_nan(x: f32) -> bool {
let (_, e, m) = decompose_f32(x);
e == 0xFF && m != 0
}
#[inline]
fn f32_is_inf(x: f32) -> bool {
let (_, e, m) = decompose_f32(x);
e == 0xFF && m == 0
}
#[inline]
fn f32_is_nan_or_inf(x: f32) -> bool {
let (_, e, _) = decompose_f32(x);
e == 0xFF
}
#[inline]
fn f64_is_nan(x: f64) -> bool {
let (_, e, m) = decompose_f64(x);
e == 0x7FF && m != 0
}
#[inline]
fn f64_is_inf(x: f64) -> bool {
let (_, e, m) = decompose_f64(x);
e == 0x7FF && m == 0
}
#[inline]
fn f64_is_nan_or_inf(x: f64) -> bool {
let (_, e, _) = decompose_f64(x);
e == 0x7FF
}
#[inline]
fn f32_is_zero(x: f32) -> bool {
let (_, e, m) = decompose_f32(x);
e == 0 && m == 0
}
#[inline]
fn f64_is_zero(x: f64) -> bool {
let (_, e, m) = decompose_f64(x);
e == 0 && m == 0
}
#[inline]
fn addtf3_impl(a: f64, a_lo: f64, b: f64, b_lo: f64) -> (f64, f64) {
let sh = a + b;
let (a_err, b_err) = if a.abs() > b.abs() {
((a - sh) + b, (b - sh) + a)
} else {
((b - sh) + a, (a - sh) + b)
};
let t1 = a + b;
let e = if a.abs() > b.abs() {
(a - t1) + b
} else {
(b - t1) + a
};
let t2 = a_lo + b_lo + e;
let hi = t1 + t2;
let lo = t2 - (hi - t1);
(hi, lo)
}
#[derive(Debug, Clone)]
pub struct X86CompilerRT {
pub subtarget: X86Subtarget,
pub reg_info: X86RegisterInfo,
pub calling_convention: X86CallingConvention,
pub has_fpu: bool,
pub is_64bit: bool,
pub personality_fn: String,
pub stack_guard: u64,
}
impl X86CompilerRT {
pub fn new(subtarget: X86Subtarget, is_64bit: bool) -> Self {
let reg_info = X86RegisterInfo;
let calling_convention = if is_64bit {
X86CallingConvention::X86_64_SysV
} else {
X86CallingConvention::C
};
let has_fpu = subtarget.has_feature("sse") || subtarget.has_feature("mmx");
let stack_guard = 0x0A0B_0C0D_0E0F_1011u64;
X86CompilerRT {
subtarget,
reg_info,
calling_convention,
has_fpu,
is_64bit,
personality_fn: "__gxx_personality_v0".to_string(),
stack_guard,
}
}
pub fn default_x86_64() -> Self {
Self::new(
X86Subtarget::new("x86_64-unknown-linux-gnu", "generic", ""),
true,
)
}
pub fn default_i386() -> Self {
Self::new(
X86Subtarget::new("i386-unknown-linux-gnu", "generic", ""),
false,
)
}
}
impl X86CompilerRT {
pub fn addsf3(&self, a: f32, b: f32) -> f32 {
if !self.has_fpu {
self.soft_add_f32(a, b)
} else {
a + b
}
}
pub fn subsf3(&self, a: f32, b: f32) -> f32 {
self.addsf3(a, self.negsf2(b))
}
pub fn mulsf3(&self, a: f32, b: f32) -> f32 {
if !self.has_fpu {
self.soft_mul_f32(a, b)
} else {
a * b
}
}
pub fn divsf3(&self, a: f32, b: f32) -> f32 {
self.soft_div_f32(a, b)
}
pub fn adddf3(&self, a: f64, b: f64) -> f64 {
if !self.has_fpu {
self.soft_add_f64(a, b)
} else {
a + b
}
}
pub fn subdf3(&self, a: f64, b: f64) -> f64 {
self.adddf3(a, self.negdf2(b))
}
pub fn muldf3(&self, a: f64, b: f64) -> f64 {
if !self.has_fpu {
self.soft_mul_f64(a, b)
} else {
a * b
}
}
pub fn divdf3(&self, a: f64, b: f64) -> f64 {
self.soft_div_f64(a, b)
}
pub fn addtf3(&self, a_hi: f64, a_lo: f64, b_hi: f64, b_lo: f64) -> (f64, f64) {
addtf3_impl(a_hi, a_lo, b_hi, b_lo)
}
pub fn subtf3(&self, a_hi: f64, a_lo: f64, b_hi: f64, b_lo: f64) -> (f64, f64) {
self.addtf3(a_hi, a_lo, -b_hi, -b_lo)
}
pub fn multf3(&self, a_hi: f64, a_lo: f64, b_hi: f64, b_lo: f64) -> (f64, f64) {
let p1 = a_hi * b_hi;
let p2 = a_hi * b_lo;
let p3 = a_lo * b_hi;
let p4 = a_lo * b_lo;
let s = p2 + p3;
let t = p1 + s;
let lo = (p1 - t) + s + p4;
let hi = t + lo;
(hi, lo - (hi - t))
}
pub fn divtf3(&self, a_hi: f64, a_lo: f64, b_hi: f64, b_lo: f64) -> (f64, f64) {
let q1 = a_hi / b_hi;
let (p_hi, p_lo) = self.multf3(q1, 0.0, b_hi, b_lo);
let (r_hi, r_lo) = self.subtf3(a_hi, a_lo, p_hi, p_lo);
let q2 = r_hi / b_hi;
addtf3_impl(q1, 0.0, q2, 0.0)
}
pub fn negsf2(&self, a: f32) -> f32 {
let bits = a.to_bits();
if f32_is_nan(a) {
a } else {
f32::from_bits(bits ^ F32_SIGN_MASK)
}
}
pub fn negdf2(&self, a: f64) -> f64 {
let bits = a.to_bits();
if f64_is_nan(a) {
a
} else {
f64::from_bits(bits ^ F64_SIGN_MASK)
}
}
pub fn negtf2(&self, a_hi: f64, a_lo: f64) -> (f64, f64) {
(-a_hi, -a_lo)
}
pub fn cmpsf2(&self, a: f32, b: f32) -> i32 {
if f32_is_nan(a) || f32_is_nan(b) {
return -2; }
if a < b {
-1
} else if a > b {
1
} else {
0
}
}
pub fn cmpdf2(&self, a: f64, b: f64) -> i32 {
if f64_is_nan(a) || f64_is_nan(b) {
return -2;
}
if a < b {
-1
} else if a > b {
1
} else {
0
}
}
pub fn eqsf2(&self, a: f32, b: f32) -> i32 {
if f32_is_nan(a) || f32_is_nan(b) {
1
} else if a == b {
0
} else {
1
}
}
pub fn eqdf2(&self, a: f64, b: f64) -> i32 {
if f64_is_nan(a) || f64_is_nan(b) {
1
} else if a == b {
0
} else {
1
}
}
pub fn nesf2(&self, a: f32, b: f32) -> i32 {
if f32_is_nan(a) || f32_is_nan(b) {
1
} else if a != b {
0
} else {
1
}
}
pub fn nedf2(&self, a: f64, b: f64) -> i32 {
if f64_is_nan(a) || f64_is_nan(b) {
1
} else if a != b {
0
} else {
1
}
}
pub fn gesf2(&self, a: f32, b: f32) -> i32 {
if f32_is_nan(a) || f32_is_nan(b) {
-1
} else if a >= b {
0
} else {
-1
}
}
pub fn gedf2(&self, a: f64, b: f64) -> i32 {
if f64_is_nan(a) || f64_is_nan(b) {
-1
} else if a >= b {
0
} else {
-1
}
}
pub fn ltsf2(&self, a: f32, b: f32) -> i32 {
if f32_is_nan(a) || f32_is_nan(b) {
1
} else if a < b {
0
} else {
1
}
}
pub fn ltdf2(&self, a: f64, b: f64) -> i32 {
if f64_is_nan(a) || f64_is_nan(b) {
1
} else if a < b {
0
} else {
1
}
}
pub fn lesf2(&self, a: f32, b: f32) -> i32 {
if f32_is_nan(a) || f32_is_nan(b) {
1
} else if a <= b {
0
} else {
1
}
}
pub fn ledf2(&self, a: f64, b: f64) -> i32 {
if f64_is_nan(a) || f64_is_nan(b) {
1
} else if a <= b {
0
} else {
1
}
}
pub fn extendsfdf2(&self, a: f32) -> f64 {
if f32_is_nan(a) {
return f64::NAN;
}
if a == f32::INFINITY {
return f64::INFINITY;
}
if a == f32::NEG_INFINITY {
return f64::NEG_INFINITY;
}
a as f64
}
pub fn truncdfsf2(&self, a: f64) -> f32 {
if f64_is_nan(a) {
return f32::NAN;
}
if a == f64::INFINITY {
return f32::INFINITY;
}
if a == f64::NEG_INFINITY {
return f32::NEG_INFINITY;
}
let (s, e, _m) = decompose_f64(a);
if e >= F32_EXP_BIAS + 0xFF {
return if s { f32::NEG_INFINITY } else { f32::INFINITY };
}
if e <= F32_EXP_BIAS - F32_MANT_BITS as i32 - 1 {
return if s { -0.0f32 } else { 0.0f32 };
}
a as f32
}
pub fn fixsfsi(&self, a: f32) -> i32 {
if f32_is_nan(a) {
return 0; }
if a >= (i32::MAX as f32) + 1.0 {
return i32::MIN;
}
if a <= (i32::MIN as f32) - 1.0 {
return i32::MIN;
}
a as i32
}
pub fn fixdfsi(&self, a: f64) -> i32 {
if f64_is_nan(a) {
return 0;
}
if a >= (i32::MAX as f64) + 1.0 {
return i32::MIN;
}
if a <= (i32::MIN as f64) - 1.0 {
return i32::MIN;
}
a as i32
}
pub fn fixsfdi(&self, a: f32) -> i64 {
if f32_is_nan(a) {
return 0;
}
if a >= (i64::MAX as f32) {
return i64::MIN;
}
if a <= (i64::MIN as f32) {
return i64::MIN;
}
a as i64
}
pub fn fixdfdi(&self, a: f64) -> i64 {
if f64_is_nan(a) {
return 0;
}
if a >= (i64::MAX as f64) {
return i64::MIN;
}
if a <= (i64::MIN as f64) {
return i64::MIN;
}
a as i64
}
pub fn floatsisf(&self, a: i32) -> f32 {
a as f32
}
pub fn floatsidf(&self, a: i32) -> f64 {
a as f64
}
pub fn floatdisf(&self, a: i64) -> f32 {
a as f32
}
pub fn floatdidf(&self, a: i64) -> f64 {
a as f64
}
pub fn fixunssfsi(&self, a: f32) -> u32 {
if f32_is_nan(a) || a < 0.0 {
return 0;
}
if a >= (u32::MAX as f32) + 1.0 {
return u32::MAX;
}
a as u32
}
pub fn fixunsdfsi(&self, a: f64) -> u32 {
if f64_is_nan(a) || a < 0.0 {
return 0;
}
if a >= (u32::MAX as f64) + 1.0 {
return u32::MAX;
}
a as u32
}
pub fn fixunssfdi(&self, a: f32) -> u64 {
if f32_is_nan(a) || a < 0.0 {
return 0;
}
if a >= (u64::MAX as f32) {
return u64::MAX;
}
a as u64
}
pub fn fixunsdfdi(&self, a: f64) -> u64 {
if f64_is_nan(a) || a < 0.0 {
return 0;
}
if a >= (u64::MAX as f64) {
return u64::MAX;
}
a as u64
}
pub fn floatunsisf(&self, a: u32) -> f32 {
a as f32
}
pub fn floatunsidf(&self, a: u32) -> f64 {
a as f64
}
pub fn floatundisf(&self, a: u64) -> f32 {
a as f32
}
pub fn floatundidf(&self, a: u64) -> f64 {
a as f64
}
pub fn powisf2(&self, base: f32, exp: i32) -> f32 {
if exp == 0 {
return 1.0f32;
}
if base == 0.0f32 {
if exp < 0 {
return f32::INFINITY;
}
return 0.0f32;
}
let mut result = 1.0f32;
let mut b = base;
let mut e = if exp < 0 { -exp } else { exp };
while e > 0 {
if e & 1 != 0 {
result = self.mulsf3(result, b);
}
e >>= 1;
if e > 0 {
b = self.mulsf3(b, b);
}
}
if exp < 0 {
self.divsf3(1.0f32, result)
} else {
result
}
}
pub fn powidf2(&self, base: f64, exp: i32) -> f64 {
if exp == 0 {
return 1.0f64;
}
if base == 0.0f64 {
if exp < 0 {
return f64::INFINITY;
}
return 0.0f64;
}
let mut result = 1.0f64;
let mut b = base;
let mut e = if exp < 0 { -exp } else { exp };
while e > 0 {
if e & 1 != 0 {
result = self.muldf3(result, b);
}
e >>= 1;
if e > 0 {
b = self.muldf3(b, b);
}
}
if exp < 0 {
self.divdf3(1.0f64, result)
} else {
result
}
}
}
impl X86CompilerRT {
fn soft_add_f32(&self, a: f32, b: f32) -> f32 {
if f32_is_nan(a) {
return a;
}
if f32_is_nan(b) {
return b;
}
let a_inf = f32_is_inf(a);
let b_inf = f32_is_inf(b);
if a_inf && b_inf {
let sign_a = (a.to_bits() >> 31) != 0;
let sign_b = (b.to_bits() >> 31) != 0;
if sign_a != sign_b {
return f32::NAN; }
return a;
}
if a_inf {
return a;
}
if b_inf {
return b;
}
let a_zero = f32_is_zero(a);
let b_zero = f32_is_zero(b);
if a_zero && b_zero {
let sign_a = (a.to_bits() >> 31) != 0;
let sign_b = (b.to_bits() >> 31) != 0;
return if sign_a && sign_b { -0.0f32 } else { 0.0f32 };
}
if a_zero {
return b;
}
if b_zero {
return a;
}
let (sign_a, exp_a, mant_a) = decompose_f32(a);
let (sign_b, exp_b, mant_b) = decompose_f32(b);
let ma = if exp_a == 0 {
mant_a
} else {
mant_a | (1u32 << 23)
};
let mb = if exp_b == 0 {
mant_b
} else {
mant_b | (1u32 << 23)
};
let (mut sign, mut exp, mut ma_aligned, mut mb_aligned) = if exp_a > exp_b {
let shift = (exp_a - exp_b) as u32;
(sign_a, exp_a, ma << 3, (mb << 3) >> shift.min(26))
} else if exp_b > exp_a {
let shift = (exp_b - exp_a) as u32;
(sign_b, exp_b, mb << 3, (ma << 3) >> shift.min(26))
} else {
if ma >= mb {
(sign_a, exp_a, ma << 3, mb << 3)
} else {
(sign_b, exp_b, mb << 3, ma << 3)
}
};
let mut mant: u32;
if sign_a == sign_b {
mant = ma_aligned + mb_aligned;
} else {
mant = ma_aligned - mb_aligned;
if mant == 0 {
return 0.0f32; }
}
while mant >= (1u32 << 27) {
mant >>= 1;
exp += 1;
}
while mant != 0 && mant < (1u32 << 26) {
mant <<= 1;
exp -= 1;
}
if exp >= 255 {
return if sign {
f32::NEG_INFINITY
} else {
f32::INFINITY
};
}
if exp <= 0 {
if exp <= -24 {
return if sign { -0.0f32 } else { 0.0f32 };
}
mant >>= (-exp + 1) as u32;
exp = 0;
}
let mut result_mant = (mant >> 3) & 0x007F_FFFF;
let round_bits = mant & 0x7;
let sticky = false;
if round_bits > 4 || (round_bits == 4 && (result_mant & 1 != 0 || sticky)) {
result_mant += 1;
if result_mant >= (1u32 << 23) {
result_mant = 0;
exp += 1;
if exp >= 255 {
return if sign {
f32::NEG_INFINITY
} else {
f32::INFINITY
};
}
}
}
compose_f32(sign, exp, result_mant)
}
fn soft_add_f64(&self, a: f64, b: f64) -> f64 {
if f64_is_nan(a) {
return a;
}
if f64_is_nan(b) {
return b;
}
let a_inf = f64_is_inf(a);
let b_inf = f64_is_inf(b);
if a_inf && b_inf {
let sign_a = (a.to_bits() >> 63) != 0;
let sign_b = (b.to_bits() >> 63) != 0;
if sign_a != sign_b {
return f64::NAN;
}
return a;
}
if a_inf {
return a;
}
if b_inf {
return b;
}
let a_zero = f64_is_zero(a);
let b_zero = f64_is_zero(b);
if a_zero && b_zero {
let sign_a = (a.to_bits() >> 63) != 0;
let sign_b = (b.to_bits() >> 63) != 0;
return if sign_a && sign_b { -0.0f64 } else { 0.0f64 };
}
if a_zero {
return b;
}
if b_zero {
return a;
}
let (sign_a, exp_a, mant_a) = decompose_f64(a);
let (sign_b, exp_b, mant_b) = decompose_f64(b);
let ma = if exp_a == 0 {
mant_a
} else {
mant_a | (1u64 << 52)
};
let mb = if exp_b == 0 {
mant_b
} else {
mant_b | (1u64 << 52)
};
let (mut sign, mut exp, mut ma_aligned, mut mb_aligned) = if exp_a > exp_b {
let shift = (exp_a - exp_b) as u32;
(sign_a, exp_a, ma << 3, (mb << 3) >> shift.min(55))
} else if exp_b > exp_a {
let shift = (exp_b - exp_a) as u32;
(sign_b, exp_b, mb << 3, (ma << 3) >> shift.min(55))
} else {
if ma >= mb {
(sign_a, exp_a, ma << 3, mb << 3)
} else {
(sign_b, exp_b, mb << 3, ma << 3)
}
};
let mut mant: u64;
if sign_a == sign_b {
mant = ma_aligned + mb_aligned;
} else {
mant = ma_aligned - mb_aligned;
if mant == 0 {
return 0.0f64;
}
}
while mant >= (1u64 << 56) {
mant >>= 1;
exp += 1;
}
while mant != 0 && mant < (1u64 << 55) {
mant <<= 1;
exp -= 1;
}
if exp >= 2047 {
return if sign {
f64::NEG_INFINITY
} else {
f64::INFINITY
};
}
if exp <= 0 {
if exp <= -53 {
return if sign { -0.0f64 } else { 0.0f64 };
}
mant >>= (-exp + 1) as u32;
exp = 0;
}
let mut result_mant = (mant >> 3) & 0x000F_FFFF_FFFF_FFFF;
let round_bits = mant & 0x7;
if round_bits > 4 || (round_bits == 4 && (result_mant & 1 != 0)) {
result_mant += 1;
if result_mant >= (1u64 << 52) {
result_mant = 0;
exp += 1;
if exp >= 2047 {
return if sign {
f64::NEG_INFINITY
} else {
f64::INFINITY
};
}
}
}
compose_f64(sign, exp, result_mant)
}
fn soft_mul_f32(&self, a: f32, b: f32) -> f32 {
if f32_is_nan(a) {
return a;
}
if f32_is_nan(b) {
return b;
}
let (sign_a, exp_a, mant_a) = decompose_f32(a);
let (sign_b, exp_b, mant_b) = decompose_f32(b);
let sign = sign_a ^ sign_b;
if f32_is_inf(a) || f32_is_inf(b) {
if f32_is_zero(a) || f32_is_zero(b) {
return f32::NAN; }
return if sign {
f32::NEG_INFINITY
} else {
f32::INFINITY
};
}
if f32_is_zero(a) || f32_is_zero(b) {
return if sign { -0.0f32 } else { 0.0f32 };
}
let ma = if exp_a == 0 {
mant_a
} else {
mant_a | (1u32 << 23)
};
let mb = if exp_b == 0 {
mant_b
} else {
mant_b | (1u32 << 23)
};
let mut exp = exp_a + exp_b - F32_EXP_BIAS;
let mut product = (ma as u64) * (mb as u64);
if product >= (1u64 << 48) {
product >>= 1;
exp += 1;
}
if product == 0 {
return if sign { -0.0f32 } else { 0.0f32 };
}
if exp >= 255 {
return if sign {
f32::NEG_INFINITY
} else {
f32::INFINITY
};
}
if exp <= 0 {
if exp <= -24 {
return if sign { -0.0f32 } else { 0.0f32 };
}
product >>= (-exp + 1) as u32;
exp = 0;
}
let mut mant = ((product >> 24) & 0x007F_FFFF) as u32;
let round_bits = (product >> 23) & 1;
let sticky = (product & ((1u64 << 23) - 1)) != 0;
if round_bits != 0 && (sticky || (mant & 1 != 0)) {
mant += 1;
if mant >= (1u32 << 23) {
mant = 0;
exp += 1;
if exp >= 255 {
return if sign {
f32::NEG_INFINITY
} else {
f32::INFINITY
};
}
}
}
compose_f32(sign, exp, mant)
}
fn soft_mul_f64(&self, a: f64, b: f64) -> f64 {
if f64_is_nan(a) {
return a;
}
if f64_is_nan(b) {
return b;
}
let (sign_a, exp_a, mant_a) = decompose_f64(a);
let (sign_b, exp_b, mant_b) = decompose_f64(b);
let sign = sign_a ^ sign_b;
if f64_is_inf(a) || f64_is_inf(b) {
if f64_is_zero(a) || f64_is_zero(b) {
return f64::NAN;
}
return if sign {
f64::NEG_INFINITY
} else {
f64::INFINITY
};
}
if f64_is_zero(a) || f64_is_zero(b) {
return if sign { -0.0f64 } else { 0.0f64 };
}
let ma = if exp_a == 0 {
mant_a
} else {
mant_a | (1u64 << 52)
};
let mb = if exp_b == 0 {
mant_b
} else {
mant_b | (1u64 << 52)
};
let mut exp = exp_a + exp_b - F64_EXP_BIAS;
let product = (ma as u128) * (mb as u128);
let mut hi = (product >> 104) as u64; let round_bits = (product >> 103) & 1;
let sticky = (product & ((1u128 << 103) - 1)) != 0;
if hi == 0 {
return if sign { -0.0f64 } else { 0.0f64 };
}
if hi >= (1u64 << 53) {
let full = product;
if full >= (1u128 << 105) {
let mut m = (full >> 53) as u64;
let mut e = exp + 1;
if e >= 2047 {
return if sign {
f64::NEG_INFINITY
} else {
f64::INFINITY
};
}
return compose_f64(sign, e, m & 0x000F_FFFF_FFFF_FFFF);
}
}
if exp >= 2047 {
return if sign {
f64::NEG_INFINITY
} else {
f64::INFINITY
};
}
if exp <= 0 {
if exp <= -53 {
return if sign { -0.0f64 } else { 0.0f64 };
}
let shifted = hi >> (-exp as u32);
return compose_f64(sign, 0, shifted & 0x000F_FFFF_FFFF_FFFF);
}
let mut mant = hi & 0x000F_FFFF_FFFF_FFFF;
if round_bits != 0 && (sticky || (mant & 1 != 0)) {
mant += 1;
if mant >= (1u64 << 52) {
mant = 0;
exp += 1;
if exp >= 2047 {
return if sign {
f64::NEG_INFINITY
} else {
f64::INFINITY
};
}
}
}
compose_f64(sign, exp, mant)
}
fn soft_div_f32(&self, a: f32, b: f32) -> f32 {
if f32_is_nan(a) {
return a;
}
if f32_is_nan(b) {
return b;
}
let (sign_a, exp_a, mant_a) = decompose_f32(a);
let (sign_b, exp_b, mant_b) = decompose_f32(b);
let sign = sign_a ^ sign_b;
let a_inf = f32_is_inf(a);
let b_inf = f32_is_inf(b);
let a_zero = f32_is_zero(a);
let b_zero = f32_is_zero(b);
if a_inf && b_inf {
return f32::NAN;
}
if a_inf {
return if sign {
f32::NEG_INFINITY
} else {
f32::INFINITY
};
}
if b_inf {
return if sign { -0.0f32 } else { 0.0f32 };
}
if a_zero && b_zero {
return f32::NAN;
}
if b_zero {
return if sign {
f32::NEG_INFINITY
} else {
f32::INFINITY
};
}
if a_zero {
return if sign { -0.0f32 } else { 0.0f32 };
}
let ma = if exp_a == 0 {
mant_a
} else {
mant_a | (1u32 << 23)
};
let mb = if exp_b == 0 {
mant_b
} else {
mant_b | (1u32 << 23)
};
let mut exp = exp_a - exp_b + F32_EXP_BIAS;
let mut dividend = (ma as u64) << 24;
let divisor = mb as u64;
let mut mant: u32 = 0;
for _ in 0..25 {
mant <<= 1;
if dividend >= divisor {
dividend -= divisor;
mant |= 1;
}
dividend <<= 1;
}
if mant == 0 {
return if sign { -0.0f32 } else { 0.0f32 };
}
while mant < (1u32 << 24) {
mant <<= 1;
exp -= 1;
}
while mant >= (1u32 << 25) {
mant >>= 1;
exp += 1;
}
if exp >= 255 {
return if sign {
f32::NEG_INFINITY
} else {
f32::INFINITY
};
}
if exp <= 0 {
if exp <= -24 {
return if sign { -0.0f32 } else { 0.0f32 };
}
mant >>= (-exp + 1) as u32;
exp = 0;
}
let mut result_mant = (mant >> 1) & 0x007F_FFFF;
let round_bit = mant & 1;
let sticky = dividend != 0;
if round_bit != 0 && (sticky || (result_mant & 1 != 0)) {
result_mant += 1;
if result_mant >= (1u32 << 23) {
result_mant = 0;
exp += 1;
if exp >= 255 {
return if sign {
f32::NEG_INFINITY
} else {
f32::INFINITY
};
}
}
}
compose_f32(sign, exp, result_mant)
}
fn soft_div_f64(&self, a: f64, b: f64) -> f64 {
if f64_is_nan(a) {
return a;
}
if f64_is_nan(b) {
return b;
}
let (sign_a, exp_a, mant_a) = decompose_f64(a);
let (sign_b, exp_b, mant_b) = decompose_f64(b);
let sign = sign_a ^ sign_b;
let a_inf = f64_is_inf(a);
let b_inf = f64_is_inf(b);
let a_zero = f64_is_zero(a);
let b_zero = f64_is_zero(b);
if a_inf && b_inf {
return f64::NAN;
}
if a_inf {
return if sign {
f64::NEG_INFINITY
} else {
f64::INFINITY
};
}
if b_inf {
return if sign { -0.0f64 } else { 0.0f64 };
}
if a_zero && b_zero {
return f64::NAN;
}
if b_zero {
return if sign {
f64::NEG_INFINITY
} else {
f64::INFINITY
};
}
if a_zero {
return if sign { -0.0f64 } else { 0.0f64 };
}
let ma = if exp_a == 0 {
mant_a
} else {
mant_a | (1u64 << 52)
};
let mb = if exp_b == 0 {
mant_b
} else {
mant_b | (1u64 << 52)
};
let mut exp = exp_a - exp_b + F64_EXP_BIAS;
let mut dividend = ma;
let divisor = mb;
let mut mant: u64 = 0;
for _ in 0..54 {
mant <<= 1;
if dividend >= divisor {
dividend -= divisor;
mant |= 1;
}
dividend <<= 1;
}
if mant == 0 {
return if sign { -0.0f64 } else { 0.0f64 };
}
while mant < (1u64 << 53) {
mant <<= 1;
exp -= 1;
}
while mant >= (1u64 << 54) {
mant >>= 1;
exp += 1;
}
if exp >= 2047 {
return if sign {
f64::NEG_INFINITY
} else {
f64::INFINITY
};
}
if exp <= 0 {
if exp <= -53 {
return if sign { -0.0f64 } else { 0.0f64 };
}
mant >>= (-exp + 1) as u32;
exp = 0;
}
let mut result_mant = (mant >> 1) & 0x000F_FFFF_FFFF_FFFF;
let round_bit = mant & 1;
let sticky = dividend != 0;
if round_bit != 0 && (sticky || (result_mant & 1 != 0)) {
result_mant += 1;
if result_mant >= (1u64 << 52) {
result_mant = 0;
exp += 1;
if exp >= 2047 {
return if sign {
f64::NEG_INFINITY
} else {
f64::INFINITY
};
}
}
}
compose_f64(sign, exp, result_mant)
}
}
impl X86CompilerRT {
pub fn divdi3(&self, a: i64, b: i64) -> i64 {
if b == 0 {
return i64::MIN;
}
if a == i64::MIN && b == -1 {
return i64::MIN; }
a / b
}
pub fn moddi3(&self, a: i64, b: i64) -> i64 {
if b == 0 {
return 0;
}
if a == i64::MIN && b == -1 {
return 0;
}
a % b
}
pub fn divmoddi4(&self, a: i64, b: i64) -> (i64, i64) {
(self.divdi3(a, b), self.moddi3(a, b))
}
pub fn udivdi3(&self, a: u64, b: u64) -> u64 {
if b == 0 {
return u64::MAX;
}
a / b
}
pub fn umoddi3(&self, a: u64, b: u64) -> u64 {
if b == 0 {
return a;
}
a % b
}
pub fn udivmoddi4(&self, a: u64, b: u64) -> (u64, u64) {
(self.udivdi3(a, b), self.umoddi3(a, b))
}
pub fn divsi3(&self, a: i32, b: i32) -> i32 {
if b == 0 {
return i32::MIN;
}
if a == i32::MIN && b == -1 {
return i32::MIN; }
a / b
}
pub fn modsi3(&self, a: i32, b: i32) -> i32 {
if b == 0 {
return 0;
}
if a == i32::MIN && b == -1 {
return 0;
}
a % b
}
pub fn divmodsi4(&self, a: i32, b: i32) -> (i32, i32) {
(self.divsi3(a, b), self.modsi3(a, b))
}
pub fn udivsi3(&self, a: u32, b: u32) -> u32 {
if b == 0 {
return 0;
}
a / b
}
pub fn umodsi3(&self, a: u32, b: u32) -> u32 {
if b == 0 {
return 0;
}
a % b
}
pub fn udivmodsi4(&self, a: u32, b: u32) -> (u32, u32) {
(self.udivsi3(a, b), self.umodsi3(a, b))
}
pub fn divti3(&self, a_hi: i64, a_lo: u64, b_hi: i64, b_lo: u64) -> (i64, u64) {
let sign_a = a_hi < 0;
let sign_b = b_hi < 0;
let result_sign = sign_a ^ sign_b;
let (ua_hi, ua_lo) = if sign_a {
let (h, l) = self.neg_i128(a_hi as u64, a_lo);
(h, l)
} else {
(a_hi as u64, a_lo)
};
let (ub_hi, ub_lo) = if sign_b {
let (h, l) = self.neg_i128(b_hi as u64, b_lo);
(h, l)
} else {
(b_hi as u64, b_lo)
};
if ub_hi == 0 && ub_lo == 0 {
return (0, 0); }
let (q_hi, q_lo, _r_hi, _r_lo) = self.udivmodti4_raw(ua_hi, ua_lo, ub_hi, ub_lo);
if result_sign && (q_hi != 0 || q_lo != 0) {
self.neg_i128(q_hi, q_lo)
} else {
(q_hi as i64, q_lo)
}
}
pub fn modti3(&self, a_hi: i64, a_lo: u64, b_hi: i64, b_lo: u64) -> (i64, u64) {
let sign_a = a_hi < 0;
let (ua_hi, ua_lo) = if sign_a {
let (h, l) = self.neg_i128(a_hi as u64, a_lo);
(h, l)
} else {
(a_hi as u64, a_lo)
};
let (ub_hi, ub_lo) = if b_hi < 0 {
let (h, l) = self.neg_i128(b_hi as u64, b_lo);
(h, l)
} else {
(b_hi as u64, b_lo)
};
if ub_hi == 0 && ub_lo == 0 {
return (0, 0);
}
let (_q_hi, _q_lo, r_hi, r_lo) = self.udivmodti4_raw(ua_hi, ua_lo, ub_hi, ub_lo);
if sign_a && (r_hi != 0 || r_lo != 0) {
self.neg_i128(r_hi, r_lo)
} else {
(r_hi as i64, r_lo)
}
}
pub fn udivti3(&self, a_hi: u64, a_lo: u64, b_hi: u64, b_lo: u64) -> (u64, u64) {
if b_hi == 0 && b_lo == 0 {
return (u64::MAX, u64::MAX);
}
let (q_hi, q_lo, _, _) = self.udivmodti4_raw(a_hi, a_lo, b_hi, b_lo);
(q_hi, q_lo)
}
pub fn umodti3(&self, a_hi: u64, a_lo: u64, b_hi: u64, b_lo: u64) -> (u64, u64) {
if b_hi == 0 && b_lo == 0 {
return (0, 0);
}
let (_, _, r_hi, r_lo) = self.udivmodti4_raw(a_hi, a_lo, b_hi, b_lo);
(r_hi, r_lo)
}
pub fn muldi3(&self, a: i64, b: i64) -> i64 {
a.wrapping_mul(b)
}
pub fn multi3(&self, a_hi: i64, a_lo: u64, b_hi: i64, b_lo: u64) -> (i64, u64) {
let sign_a = a_hi < 0;
let sign_b = b_hi < 0;
let result_sign = sign_a ^ sign_b;
let (ua_hi, ua_lo) = if sign_a {
let (h, l) = self.neg_i128(a_hi as u64, a_lo);
(h, l)
} else {
(a_hi as u64, a_lo)
};
let (ub_hi, ub_lo) = if sign_b {
let (h, l) = self.neg_i128(b_hi as u64, b_lo);
(h, l)
} else {
(b_hi as u64, b_lo)
};
let (p_hi, p_lo) = self.umulti3(ua_hi, ua_lo, ub_hi, ub_lo);
if result_sign && (p_hi != 0 || p_lo != 0) {
self.neg_i128(p_hi, p_lo)
} else {
(p_hi as i64, p_lo)
}
}
fn umulti3(&self, a_hi: u64, a_lo: u64, b_hi: u64, b_lo: u64) -> (u64, u64) {
let a = ((a_hi as u128) << 64) | (a_lo as u128);
let b = ((b_hi as u128) << 64) | (b_lo as u128);
let product = a.wrapping_mul(b);
((product >> 64) as u64, product as u64)
}
pub fn ashldi3(&self, a: i64, b: i32) -> i64 {
if b >= 64 {
0
} else if b <= 0 {
a
} else {
a.wrapping_shl(b as u32)
}
}
pub fn ashrdi3(&self, a: i64, b: i32) -> i64 {
if b >= 64 {
a >> 63 } else if b <= 0 {
a
} else {
a >> b
}
}
pub fn lshrdi3(&self, a: u64, b: i32) -> u64 {
if b >= 64 {
0
} else if b <= 0 {
a
} else {
a >> b
}
}
pub fn ashlti3(&self, a_hi: u64, a_lo: u64, b: i32) -> (u64, u64) {
if b >= 128 {
(0, 0)
} else if b <= 0 {
(a_hi, a_lo)
} else if b >= 64 {
(a_lo << (b - 64), 0)
} else {
let hi = (a_hi << b) | (a_lo >> (64 - b));
let lo = a_lo << b;
(hi, lo)
}
}
pub fn ashrti3(&self, a_hi: u64, a_lo: u64, b: i32) -> (u64, u64) {
let sign_bit = (a_hi >> 63) as i64;
if b >= 128 {
let fill = if sign_bit != 0 { u64::MAX } else { 0 };
(fill, fill)
} else if b <= 0 {
(a_hi, a_lo)
} else if b >= 64 {
let hi = if sign_bit != 0 { u64::MAX } else { 0 };
let lo = ((a_hi as i64) >> (b - 64)) as u64;
(hi, lo)
} else {
let hi = ((a_hi as i64) >> b) as u64;
let lo = (a_hi << (64 - b)) | (a_lo >> b);
(hi, lo)
}
}
pub fn lshrti3(&self, a_hi: u64, a_lo: u64, b: i32) -> (u64, u64) {
if b >= 128 {
(0, 0)
} else if b <= 0 {
(a_hi, a_lo)
} else if b >= 64 {
(0, a_hi >> (b - 64))
} else {
let hi = a_hi >> b;
let lo = (a_hi << (64 - b)) | (a_lo >> b);
(hi, lo)
}
}
pub fn clzsi2(&self, a: u32) -> i32 {
if a == 0 {
return 32;
}
a.leading_zeros() as i32
}
pub fn clzdi2(&self, a: u64) -> i32 {
if a == 0 {
return 64;
}
a.leading_zeros() as i32
}
pub fn clzti2(&self, a_hi: u64, a_lo: u64) -> i32 {
if a_hi != 0 {
a_hi.leading_zeros() as i32
} else if a_lo != 0 {
64 + a_lo.leading_zeros() as i32
} else {
128
}
}
pub fn ctzsi2(&self, a: u32) -> i32 {
if a == 0 {
return 32;
}
a.trailing_zeros() as i32
}
pub fn ctzdi2(&self, a: u64) -> i32 {
if a == 0 {
return 64;
}
a.trailing_zeros() as i32
}
pub fn ctzti2(&self, a_hi: u64, a_lo: u64) -> i32 {
if a_lo != 0 {
a_lo.trailing_zeros() as i32
} else if a_hi != 0 {
64 + a_hi.trailing_zeros() as i32
} else {
128
}
}
pub fn popcountsi2(&self, a: u32) -> i32 {
a.count_ones() as i32
}
pub fn popcountdi2(&self, a: u64) -> i32 {
a.count_ones() as i32
}
pub fn popcountti2(&self, a_hi: u64, a_lo: u64) -> i32 {
a_hi.count_ones() as i32 + a_lo.count_ones() as i32
}
pub fn paritysi2(&self, a: u32) -> i32 {
(a.count_ones() & 1) as i32
}
pub fn paritydi2(&self, a: u64) -> i32 {
(a.count_ones() & 1) as i32
}
pub fn parityti2(&self, a_hi: u64, a_lo: u64) -> i32 {
((a_hi.count_ones() + a_lo.count_ones()) & 1) as i32
}
pub fn bswapsi2(&self, a: u32) -> u32 {
a.swap_bytes()
}
pub fn bswapdi2(&self, a: u64) -> u64 {
a.swap_bytes()
}
fn neg_i128(&self, hi: u64, lo: u64) -> (i64, u64) {
let (lo_neg, carry) = (!lo).overflowing_add(1);
let hi_neg = (!hi).wrapping_add(if carry { 1 } else { 0 });
(hi_neg as i64, lo_neg)
}
fn udivmodti4_raw(&self, a_hi: u64, a_lo: u64, b_hi: u64, b_lo: u64) -> (u64, u64, u64, u64) {
if b_hi == 0 && b_lo == 0 {
return (0, 0, 0, 0);
}
if a_hi == 0 && b_hi == 0 {
let q = a_lo / b_lo;
let r = a_lo % b_lo;
return (0, q, 0, r);
}
let mut q_hi: u64 = 0;
let mut q_lo: u64 = 0;
let mut r_hi: u64 = 0;
let mut r_lo: u64 = 0;
for i in (0..128).rev() {
let carry = r_hi >> 63;
r_hi = (r_hi << 1) | (r_lo >> 63);
r_lo = r_lo << 1;
let bit = if i >= 64 {
(a_hi >> (i - 64)) & 1
} else {
(a_lo >> i) & 1
};
r_lo |= bit;
if r_hi > b_hi || (r_hi == b_hi && r_lo >= b_lo) {
let (new_lo, borrow1) = r_lo.overflowing_sub(b_lo);
let (new_hi, borrow2) = r_hi.overflowing_sub(b_hi + if borrow1 { 1 } else { 0 });
let _ = borrow2;
r_hi = new_hi;
r_lo = new_lo;
if i >= 64 {
q_hi |= 1 << (i - 64);
} else {
q_lo |= 1 << i;
}
}
}
(q_hi, q_lo, r_hi, r_lo)
}
}
static ATOMIC_MUTEX: std::sync::Mutex<()> = std::sync::Mutex::new(());
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum AtomicOrdering {
Relaxed = 0,
Consume = 1,
Acquire = 2,
Release = 3,
AcqRel = 4,
SeqCst = 5,
}
impl From<u32> for AtomicOrdering {
fn from(v: u32) -> Self {
match v {
0 => AtomicOrdering::Relaxed,
1 => AtomicOrdering::Consume,
2 => AtomicOrdering::Acquire,
3 => AtomicOrdering::Release,
4 => AtomicOrdering::AcqRel,
5 => AtomicOrdering::SeqCst,
_ => AtomicOrdering::SeqCst,
}
}
}
impl X86CompilerRT {
pub fn atomic_load(
&self,
ptr: *const u8,
size: usize,
_order: AtomicOrdering,
result: *mut u8,
) {
unsafe {
let _lock = ATOMIC_MUTEX.lock().unwrap();
std::ptr::copy_nonoverlapping(ptr, result, size);
}
}
pub fn atomic_load_1(&self, ptr: *const u8) -> u8 {
unsafe {
let _lock = ATOMIC_MUTEX.lock().unwrap();
*ptr
}
}
pub fn atomic_load_2(&self, ptr: *const u8) -> u16 {
unsafe {
let _lock = ATOMIC_MUTEX.lock().unwrap();
(ptr as *const u16).read_unaligned()
}
}
pub fn atomic_load_4(&self, ptr: *const u8) -> u32 {
unsafe {
let _lock = ATOMIC_MUTEX.lock().unwrap();
(ptr as *const u32).read_unaligned()
}
}
pub fn atomic_load_8(&self, ptr: *const u8) -> u64 {
unsafe {
let _lock = ATOMIC_MUTEX.lock().unwrap();
(ptr as *const u64).read_unaligned()
}
}
pub fn atomic_load_16(&self, ptr: *const u8, result: *mut u8) {
unsafe {
let _lock = ATOMIC_MUTEX.lock().unwrap();
std::ptr::copy_nonoverlapping(ptr, result, 16);
}
}
pub fn atomic_store(
&self,
ptr: *mut u8,
size: usize,
_order: AtomicOrdering,
value: *const u8,
) {
unsafe {
let _lock = ATOMIC_MUTEX.lock().unwrap();
std::ptr::copy_nonoverlapping(value, ptr, size);
}
}
pub fn atomic_store_1(&self, ptr: *mut u8, val: u8) {
unsafe {
let _lock = ATOMIC_MUTEX.lock().unwrap();
*ptr = val;
}
}
pub fn atomic_store_2(&self, ptr: *mut u8, val: u16) {
unsafe {
let _lock = ATOMIC_MUTEX.lock().unwrap();
(ptr as *mut u16).write_unaligned(val);
}
}
pub fn atomic_store_4(&self, ptr: *mut u8, val: u32) {
unsafe {
let _lock = ATOMIC_MUTEX.lock().unwrap();
(ptr as *mut u32).write_unaligned(val);
}
}
pub fn atomic_store_8(&self, ptr: *mut u8, val: u64) {
unsafe {
let _lock = ATOMIC_MUTEX.lock().unwrap();
(ptr as *mut u64).write_unaligned(val);
}
}
pub fn atomic_store_16(&self, ptr: *mut u8, value: *const u8) {
unsafe {
let _lock = ATOMIC_MUTEX.lock().unwrap();
std::ptr::copy_nonoverlapping(value, ptr, 16);
}
}
pub fn atomic_exchange(
&self,
ptr: *mut u8,
size: usize,
new_val: *const u8,
old: *mut u8,
_order: AtomicOrdering,
) {
unsafe {
let _lock = ATOMIC_MUTEX.lock().unwrap();
std::ptr::copy_nonoverlapping(ptr, old, size);
std::ptr::copy_nonoverlapping(new_val, ptr, size);
}
}
pub fn atomic_exchange_4(&self, ptr: *mut u32, val: u32) -> u32 {
unsafe {
let _lock = ATOMIC_MUTEX.lock().unwrap();
let old = *ptr;
*ptr = val;
old
}
}
pub fn atomic_exchange_8(&self, ptr: *mut u64, val: u64) -> u64 {
unsafe {
let _lock = ATOMIC_MUTEX.lock().unwrap();
let old = *ptr;
*ptr = val;
old
}
}
pub fn atomic_compare_exchange(
&self,
ptr: *mut u8,
size: usize,
expected: *mut u8,
desired: *const u8,
_success_order: AtomicOrdering,
_failure_order: AtomicOrdering,
_weak: bool,
) -> bool {
unsafe {
let _lock = ATOMIC_MUTEX.lock().unwrap();
let equal =
std::slice::from_raw_parts(ptr, size) == std::slice::from_raw_parts(expected, size);
if equal {
std::ptr::copy_nonoverlapping(desired, ptr, size);
} else {
std::ptr::copy_nonoverlapping(ptr, expected, size);
}
equal
}
}
pub fn atomic_compare_exchange_4(
&self,
ptr: *mut u32,
expected: *mut u32,
desired: u32,
_weak: bool,
) -> bool {
unsafe {
let _lock = ATOMIC_MUTEX.lock().unwrap();
if *ptr == *expected {
*ptr = desired;
true
} else {
*expected = *ptr;
false
}
}
}
pub fn atomic_compare_exchange_8(
&self,
ptr: *mut u64,
expected: *mut u64,
desired: u64,
_weak: bool,
) -> bool {
unsafe {
let _lock = ATOMIC_MUTEX.lock().unwrap();
if *ptr == *expected {
*ptr = desired;
true
} else {
*expected = *ptr;
false
}
}
}
pub fn atomic_compare_exchange_16(
&self,
ptr: *mut u8,
expected: *mut u8,
desired: *const u8,
_weak: bool,
) -> bool {
unsafe {
let _lock = ATOMIC_MUTEX.lock().unwrap();
let equal =
std::slice::from_raw_parts(ptr, 16) == std::slice::from_raw_parts(expected, 16);
if equal {
std::ptr::copy_nonoverlapping(desired, ptr, 16);
} else {
std::ptr::copy_nonoverlapping(ptr, expected, 16);
}
equal
}
}
pub fn atomic_fetch_add_4(&self, ptr: *mut u32, val: u32) -> u32 {
unsafe {
let _lock = ATOMIC_MUTEX.lock().unwrap();
let old = *ptr;
*ptr = old.wrapping_add(val);
old
}
}
pub fn atomic_fetch_add_8(&self, ptr: *mut u64, val: u64) -> u64 {
unsafe {
let _lock = ATOMIC_MUTEX.lock().unwrap();
let old = *ptr;
*ptr = old.wrapping_add(val);
old
}
}
pub fn atomic_fetch_sub_4(&self, ptr: *mut u32, val: u32) -> u32 {
unsafe {
let _lock = ATOMIC_MUTEX.lock().unwrap();
let old = *ptr;
*ptr = old.wrapping_sub(val);
old
}
}
pub fn atomic_fetch_sub_8(&self, ptr: *mut u64, val: u64) -> u64 {
unsafe {
let _lock = ATOMIC_MUTEX.lock().unwrap();
let old = *ptr;
*ptr = old.wrapping_sub(val);
old
}
}
pub fn atomic_fetch_and_4(&self, ptr: *mut u32, val: u32) -> u32 {
unsafe {
let _lock = ATOMIC_MUTEX.lock().unwrap();
let old = *ptr;
*ptr = old & val;
old
}
}
pub fn atomic_fetch_and_8(&self, ptr: *mut u64, val: u64) -> u64 {
unsafe {
let _lock = ATOMIC_MUTEX.lock().unwrap();
let old = *ptr;
*ptr = old & val;
old
}
}
pub fn atomic_fetch_or_4(&self, ptr: *mut u32, val: u32) -> u32 {
unsafe {
let _lock = ATOMIC_MUTEX.lock().unwrap();
let old = *ptr;
*ptr = old | val;
old
}
}
pub fn atomic_fetch_or_8(&self, ptr: *mut u64, val: u64) -> u64 {
unsafe {
let _lock = ATOMIC_MUTEX.lock().unwrap();
let old = *ptr;
*ptr = old | val;
old
}
}
pub fn atomic_fetch_xor_4(&self, ptr: *mut u32, val: u32) -> u32 {
unsafe {
let _lock = ATOMIC_MUTEX.lock().unwrap();
let old = *ptr;
*ptr = old ^ val;
old
}
}
pub fn atomic_fetch_xor_8(&self, ptr: *mut u64, val: u64) -> u64 {
unsafe {
let _lock = ATOMIC_MUTEX.lock().unwrap();
let old = *ptr;
*ptr = old ^ val;
old
}
}
pub fn atomic_fetch_nand_4(&self, ptr: *mut u32, val: u32) -> u32 {
unsafe {
let _lock = ATOMIC_MUTEX.lock().unwrap();
let old = *ptr;
*ptr = !(old & val);
old
}
}
pub fn atomic_fetch_nand_8(&self, ptr: *mut u64, val: u64) -> u64 {
unsafe {
let _lock = ATOMIC_MUTEX.lock().unwrap();
let old = *ptr;
*ptr = !(old & val);
old
}
}
pub fn atomic_test_and_set(&self, ptr: *mut u8) -> u8 {
unsafe {
let _lock = ATOMIC_MUTEX.lock().unwrap();
let old = *ptr;
*ptr = 1;
old
}
}
pub fn atomic_clear(&self, ptr: *mut u8) {
unsafe {
let _lock = ATOMIC_MUTEX.lock().unwrap();
*ptr = 0;
}
}
pub fn sync_fetch_and_add_4(&self, ptr: *mut u32, val: u32) -> u32 {
self.atomic_fetch_add_4(ptr, val)
}
pub fn sync_fetch_and_add_8(&self, ptr: *mut u64, val: u64) -> u64 {
self.atomic_fetch_add_8(ptr, val)
}
pub fn sync_fetch_and_sub_4(&self, ptr: *mut u32, val: u32) -> u32 {
self.atomic_fetch_sub_4(ptr, val)
}
pub fn sync_fetch_and_sub_8(&self, ptr: *mut u64, val: u64) -> u64 {
self.atomic_fetch_sub_8(ptr, val)
}
pub fn sync_fetch_and_and_4(&self, ptr: *mut u32, val: u32) -> u32 {
self.atomic_fetch_and_4(ptr, val)
}
pub fn sync_fetch_and_and_8(&self, ptr: *mut u64, val: u64) -> u64 {
self.atomic_fetch_and_8(ptr, val)
}
pub fn sync_fetch_and_or_4(&self, ptr: *mut u32, val: u32) -> u32 {
self.atomic_fetch_or_4(ptr, val)
}
pub fn sync_fetch_and_or_8(&self, ptr: *mut u64, val: u64) -> u64 {
self.atomic_fetch_or_8(ptr, val)
}
pub fn sync_fetch_and_xor_4(&self, ptr: *mut u32, val: u32) -> u32 {
self.atomic_fetch_xor_4(ptr, val)
}
pub fn sync_fetch_and_xor_8(&self, ptr: *mut u64, val: u64) -> u64 {
self.atomic_fetch_xor_8(ptr, val)
}
pub fn sync_fetch_and_nand_4(&self, ptr: *mut u32, val: u32) -> u32 {
self.atomic_fetch_nand_4(ptr, val)
}
pub fn sync_fetch_and_nand_8(&self, ptr: *mut u64, val: u64) -> u64 {
self.atomic_fetch_nand_8(ptr, val)
}
pub fn sync_val_compare_and_swap_4(&self, ptr: *mut u32, oldval: u32, newval: u32) -> u32 {
let mut expected = oldval;
self.atomic_compare_exchange_4(ptr, &mut expected, newval, false);
expected
}
pub fn sync_val_compare_and_swap_8(&self, ptr: *mut u64, oldval: u64, newval: u64) -> u64 {
let mut expected = oldval;
self.atomic_compare_exchange_8(ptr, &mut expected, newval, false);
expected
}
pub fn sync_bool_compare_and_swap_4(&self, ptr: *mut u32, oldval: u32, newval: u32) -> bool {
let mut expected = oldval;
self.atomic_compare_exchange_4(ptr, &mut expected, newval, false)
}
pub fn sync_bool_compare_and_swap_8(&self, ptr: *mut u64, oldval: u64, newval: u64) -> bool {
let mut expected = oldval;
self.atomic_compare_exchange_8(ptr, &mut expected, newval, false)
}
pub fn sync_lock_test_and_set_4(&self, ptr: *mut u32, val: u32) -> u32 {
self.atomic_exchange_4(ptr, val)
}
pub fn sync_lock_test_and_set_8(&self, ptr: *mut u64, val: u64) -> u64 {
self.atomic_exchange_8(ptr, val)
}
pub fn sync_lock_release_4(&self, ptr: *mut u32) {
self.atomic_store_4(ptr as *mut u8, 0);
}
pub fn sync_lock_release_8(&self, ptr: *mut u64) {
self.atomic_store_8(ptr as *mut u8, 0);
}
pub fn sync_synchronize(&self) {
std::sync::atomic::fence(std::sync::atomic::Ordering::SeqCst);
}
}
impl X86CompilerRT {
pub fn addvsi3(&self, a: i32, b: i32) -> i32 {
let (result, overflow) = a.overflowing_add(b);
if overflow {
self.abort_on_overflow("__addvsi3 overflow");
}
result
}
pub fn addvdi3(&self, a: i64, b: i64) -> i64 {
let (result, overflow) = a.overflowing_add(b);
if overflow {
self.abort_on_overflow("__addvdi3 overflow");
}
result
}
pub fn subvsi3(&self, a: i32, b: i32) -> i32 {
let (result, overflow) = a.overflowing_sub(b);
if overflow {
self.abort_on_overflow("__subvsi3 overflow");
}
result
}
pub fn subvdi3(&self, a: i64, b: i64) -> i64 {
let (result, overflow) = a.overflowing_sub(b);
if overflow {
self.abort_on_overflow("__subvdi3 overflow");
}
result
}
pub fn mulvsi3(&self, a: i32, b: i32) -> i32 {
let (result, overflow) = a.overflowing_mul(b);
if overflow {
self.abort_on_overflow("__mulvsi3 overflow");
}
result
}
pub fn mulvdi3(&self, a: i64, b: i64) -> i64 {
let (result, overflow) = a.overflowing_mul(b);
if overflow {
self.abort_on_overflow("__mulvdi3 overflow");
}
result
}
pub fn negvsi2(&self, a: i32) -> i32 {
if a == i32::MIN {
self.abort_on_overflow("__negvsi2 overflow");
}
-a
}
pub fn negvdi2(&self, a: i64) -> i64 {
if a == i64::MIN {
self.abort_on_overflow("__negvdi2 overflow");
}
-a
}
fn abort_on_overflow(&self, msg: &str) {
#[cfg(debug_assertions)]
panic!("{}", msg);
#[cfg(not(debug_assertions))]
unsafe {
libc::abort();
}
}
}
impl X86CompilerRT {
pub fn sin_f64(&self, x: f64) -> f64 {
self.x87_sin(x)
}
pub fn cos_f64(&self, x: f64) -> f64 {
self.x87_cos(x)
}
pub fn tan_f64(&self, x: f64) -> f64 {
self.x87_tan(x)
}
pub fn asin_f64(&self, x: f64) -> f64 {
self.x87_asin(x)
}
pub fn acos_f64(&self, x: f64) -> f64 {
self.x87_acos(x)
}
pub fn atan_f64(&self, x: f64) -> f64 {
self.x87_atan(x)
}
pub fn atan2_f64(&self, y: f64, x: f64) -> f64 {
self.x87_atan2(y, x)
}
pub fn sinh_f64(&self, x: f64) -> f64 {
self.x87_sinh(x)
}
pub fn cosh_f64(&self, x: f64) -> f64 {
self.x87_cosh(x)
}
pub fn tanh_f64(&self, x: f64) -> f64 {
self.x87_tanh(x)
}
pub fn exp_f64(&self, x: f64) -> f64 {
self.x87_exp(x)
}
pub fn exp2_f64(&self, x: f64) -> f64 {
self.x87_exp2(x)
}
pub fn log_f64(&self, x: f64) -> f64 {
self.x87_log(x)
}
pub fn log2_f64(&self, x: f64) -> f64 {
self.x87_log2(x)
}
pub fn log10_f64(&self, x: f64) -> f64 {
self.x87_log10(x)
}
pub fn pow_f64(&self, x: f64, y: f64) -> f64 {
self.x87_pow(x, y)
}
pub fn sqrt_f64(&self, x: f64) -> f64 {
if x < 0.0 {
return f64::NAN;
}
x.sqrt()
}
pub fn fmod_f64(&self, x: f64, y: f64) -> f64 {
if y == 0.0 {
return f64::NAN;
}
x % y
}
pub fn remainder_f64(&self, x: f64, y: f64) -> f64 {
if y == 0.0 {
return f64::NAN;
}
let n = (x / y).round();
x - n * y
}
pub fn ceil_f64(&self, x: f64) -> f64 {
x.ceil()
}
pub fn floor_f64(&self, x: f64) -> f64 {
x.floor()
}
pub fn trunc_f64(&self, x: f64) -> f64 {
x.trunc()
}
pub fn round_f64(&self, x: f64) -> f64 {
x.round()
}
pub fn rint_f64(&self, x: f64) -> f64 {
let floor = x.floor();
let frac = x - floor;
if frac < 0.5 {
floor
} else if frac > 0.5 {
floor + 1.0
} else {
if floor as i64 & 1 == 0 {
floor
} else {
floor + 1.0
}
}
}
pub fn fabs_f64(&self, x: f64) -> f64 {
f64::from_bits(x.to_bits() & !F64_SIGN_MASK)
}
pub fn fmin_f64(&self, x: f64, y: f64) -> f64 {
if f64_is_nan(x) {
return y;
}
if f64_is_nan(y) {
return x;
}
if x == 0.0 && y == 0.0 {
let x_neg = (x.to_bits() >> 63) != 0;
let y_neg = (y.to_bits() >> 63) != 0;
if x_neg {
return x;
}
if y_neg {
return y;
}
return x;
}
if x < y {
x
} else {
y
}
}
pub fn fmax_f64(&self, x: f64, y: f64) -> f64 {
if f64_is_nan(x) {
return y;
}
if f64_is_nan(y) {
return x;
}
if x == 0.0 && y == 0.0 {
let x_neg = (x.to_bits() >> 63) != 0;
let y_neg = (y.to_bits() >> 63) != 0;
if !x_neg {
return x;
}
if !y_neg {
return y;
}
return x;
}
if x > y {
x
} else {
y
}
}
pub fn copysign_f64(&self, to: f64, from: f64) -> f64 {
let sign_bit = from.to_bits() & F64_SIGN_MASK;
f64::from_bits((to.to_bits() & !F64_SIGN_MASK) | sign_bit)
}
pub fn ldexp_f64(&self, x: f64, exp: i32) -> f64 {
if x == 0.0 || f64_is_nan(x) || f64_is_inf(x) {
return x;
}
let (sign, x_exp, mant) = decompose_f64(x);
let new_exp = x_exp + exp;
if new_exp >= 2047 {
return if sign {
f64::NEG_INFINITY
} else {
f64::INFINITY
};
}
if new_exp <= 0 {
if new_exp <= -53 {
return if sign { -0.0f64 } else { 0.0f64 };
}
let m = if x_exp == 0 {
mant
} else {
mant | (1u64 << 52)
};
let shifted = m >> (-new_exp + 1) as u32;
return compose_f64(sign, 0, shifted & 0x000F_FFFF_FFFF_FFFF);
}
compose_f64(sign, new_exp, mant)
}
pub fn frexp_f64(&self, x: f64) -> (f64, i32) {
if x == 0.0 || f64_is_nan(x) || f64_is_inf(x) {
return (x, 0);
}
let (sign, exp, mant) = decompose_f64(x);
let frac_mant = if exp == 0 {
let shift = mant.leading_zeros() as i32 - 11;
mant << shift
} else {
mant | (1u64 << 52)
};
let frac_exp = if exp == 0 {
F64_EXP_BIAS - 1 - (mant.leading_zeros() as i32 - 11)
} else {
F64_EXP_BIAS - 1
};
let frac = compose_f64(sign, frac_exp, frac_mant & 0x000F_FFFF_FFFF_FFFF);
let exponent = if exp == 0 {
exp - F64_EXP_BIAS + 1 - (63 - mant.leading_zeros() as i32)
} else {
exp - F64_EXP_BIAS + 1
};
(frac, exponent)
}
pub fn modf_f64(&self, x: f64) -> (f64, f64) {
let integral = x.trunc();
let fractional = x - integral;
(fractional, integral)
}
pub fn scalbn_f64(&self, x: f64, n: i32) -> f64 {
self.ldexp_f64(x, n)
}
}
impl X86CompilerRT {
fn x87_sin(&self, x: f64) -> f64 {
if f64_is_nan(x) {
return f64::NAN;
}
if f64_is_inf(x) {
return f64::NAN;
}
let pi = std::f64::consts::PI;
let two_pi = 2.0 * pi;
let mut x = x % two_pi;
if x > pi {
x -= two_pi;
}
if x < -pi {
x += two_pi;
}
let x2 = x * x;
let x3 = x2 * x;
let x5 = x3 * x2;
let x7 = x5 * x2;
let x9 = x7 * x2;
let x11 = x9 * x2;
x - x3 / 6.0 + x5 / 120.0 - x7 / 5040.0 + x9 / 362880.0 - x11 / 39916800.0
}
fn x87_cos(&self, x: f64) -> f64 {
if f64_is_nan(x) {
return f64::NAN;
}
if f64_is_inf(x) {
return f64::NAN;
}
let pi = std::f64::consts::PI;
let two_pi = 2.0 * pi;
let mut x = x % two_pi;
if x > pi {
x -= two_pi;
}
if x < -pi {
x += two_pi;
}
let x2 = x * x;
let x4 = x2 * x2;
let x6 = x4 * x2;
let x8 = x6 * x2;
let x10 = x8 * x2;
let x12 = x10 * x2;
1.0 - x2 / 2.0 + x4 / 24.0 - x6 / 720.0 + x8 / 40320.0 - x10 / 3628800.0 + x12 / 479001600.0
}
fn x87_tan(&self, x: f64) -> f64 {
let s = self.x87_sin(x);
let c = self.x87_cos(x);
if c.abs() < 1e-15 {
if s > 0.0 {
f64::INFINITY
} else {
f64::NEG_INFINITY
}
} else {
s / c
}
}
fn x87_asin(&self, x: f64) -> f64 {
if x < -1.0 || x > 1.0 {
return f64::NAN;
}
if x == 1.0 {
return std::f64::consts::FRAC_PI_2;
}
if x == -1.0 {
return -std::f64::consts::FRAC_PI_2;
}
if x.abs() <= 0.5 {
let x2 = x * x;
let x3 = x2 * x;
let x5 = x3 * x2;
let x7 = x5 * x2;
let x9 = x7 * x2;
x + x3 / 6.0 + 3.0 * x5 / 40.0 + 5.0 * x7 / 112.0 + 35.0 * x9 / 1152.0
} else {
let pi_half = std::f64::consts::FRAC_PI_2;
let t = ((1.0 - x) / 2.0).sqrt();
pi_half - 2.0 * self.x87_asin(t)
}
}
fn x87_acos(&self, x: f64) -> f64 {
if x < -1.0 || x > 1.0 {
return f64::NAN;
}
std::f64::consts::FRAC_PI_2 - self.x87_asin(x)
}
fn x87_atan(&self, x: f64) -> f64 {
if f64_is_nan(x) {
return f64::NAN;
}
if x.is_infinite() {
if x.is_sign_positive() {
return std::f64::consts::FRAC_PI_2;
} else {
return -std::f64::consts::FRAC_PI_2;
}
}
if x.abs() <= 1.0 {
let x2 = x * x;
let x3 = x2 * x;
let x5 = x3 * x2;
let x7 = x5 * x2;
let x9 = x7 * x2;
let x11 = x9 * x2;
x - x3 / 3.0 + x5 / 5.0 - x7 / 7.0 + x9 / 9.0 - x11 / 11.0
} else {
let pi_half = std::f64::consts::FRAC_PI_2;
let sign = if x > 0.0 { 1.0 } else { -1.0 };
sign * pi_half - self.x87_atan(1.0 / x)
}
}
fn x87_atan2(&self, y: f64, x: f64) -> f64 {
if x == 0.0 && y == 0.0 {
return 0.0;
}
if x > 0.0 {
self.x87_atan(y / x)
} else if x < 0.0 {
let at = self.x87_atan(y / x);
if y >= 0.0 {
at + std::f64::consts::PI
} else {
at - std::f64::consts::PI
}
} else {
if y > 0.0 {
std::f64::consts::FRAC_PI_2
} else if y < 0.0 {
-std::f64::consts::FRAC_PI_2
} else {
0.0
}
}
}
fn x87_sinh(&self, x: f64) -> f64 {
if f64_is_nan(x) {
return f64::NAN;
}
if f64_is_inf(x) {
return x;
}
let e = self.x87_exp(x);
let em = 1.0 / e;
(e - em) * 0.5
}
fn x87_cosh(&self, x: f64) -> f64 {
if f64_is_nan(x) {
return f64::NAN;
}
if f64_is_inf(x) {
return f64::INFINITY;
}
let e = self.x87_exp(x);
let em = 1.0 / e;
(e + em) * 0.5
}
fn x87_tanh(&self, x: f64) -> f64 {
if f64_is_nan(x) {
return f64::NAN;
}
if f64_is_inf(x) {
if x.is_sign_positive() {
return 1.0;
} else {
return -1.0;
}
}
let s = self.x87_sinh(x);
let c = self.x87_cosh(x);
s / c
}
fn x87_exp(&self, x: f64) -> f64 {
if f64_is_nan(x) {
return f64::NAN;
}
if x == f64::NEG_INFINITY {
return 0.0;
}
if x == f64::INFINITY {
return f64::INFINITY;
}
if x == 0.0 {
return 1.0;
}
let log2_e = std::f64::consts::LOG2_E;
let n = (x * log2_e).round();
let f = x - n * std::f64::consts::LN_2;
let mut result = 1.0;
let mut term = 1.0;
for i in 1..=20 {
term = term * f / (i as f64);
result += term;
}
result * 2.0f64.powi(n as i32)
}
fn x87_exp2(&self, x: f64) -> f64 {
if f64_is_nan(x) {
return f64::NAN;
}
if x == f64::NEG_INFINITY {
return 0.0;
}
if x == f64::INFINITY {
return f64::INFINITY;
}
let n = x.floor();
let f = x - n;
let mut result = 1.0;
let mut term = 1.0;
let ln2 = std::f64::consts::LN_2;
for i in 1..=20 {
term = term * f * ln2 / (i as f64);
result += term;
}
result * 2.0f64.powi(n as i32)
}
fn x87_log(&self, x: f64) -> f64 {
if x < 0.0 || f64_is_nan(x) {
return f64::NAN;
}
if x == 0.0 {
return f64::NEG_INFINITY;
}
if x == f64::INFINITY {
return f64::INFINITY;
}
if x == 1.0 {
return 0.0;
}
self.x87_log2(x) * std::f64::consts::LN_2
}
fn x87_log2(&self, x: f64) -> f64 {
if x < 0.0 || f64_is_nan(x) {
return f64::NAN;
}
if x == 0.0 {
return f64::NEG_INFINITY;
}
if x == f64::INFINITY {
return f64::INFINITY;
}
if x == 1.0 {
return 0.0;
}
let (_, exp, mant) = decompose_f64(x);
let m = if exp == 0 {
let shift = mant.leading_zeros() as i32 - 11;
let shifted = (mant << shift) >> 12;
(shifted as f64) / (1u64 << 52) as f64
} else {
(mant | (1u64 << 52)) as f64 / (1u64 << 53) as f64
};
let e = if exp == 0 {
-1022 - (63 - mant.leading_zeros() as i32)
} else {
exp - F64_EXP_BIAS
};
let z = m - 1.0;
let log2_e = 1.0 / std::f64::consts::LN_2;
let z2 = z * z;
let z3 = z2 * z;
let z4 = z3 * z;
let z5 = z4 * z;
let log2_1pz = log2_e * (z - z2 / 2.0 + z3 / 3.0 - z4 / 4.0 + z5 / 5.0);
(e as f64) + log2_1pz
}
fn x87_log10(&self, x: f64) -> f64 {
self.x87_log2(x) / (10.0f64.log2())
}
fn x87_pow(&self, x: f64, y: f64) -> f64 {
if x < 0.0 && y.fract() != 0.0 {
return f64::NAN;
}
if x == 0.0 {
if y == 0.0 {
return 1.0;
}
if y < 0.0 {
return f64::INFINITY;
}
return 0.0;
}
if x == 1.0 || y == 0.0 {
return 1.0;
}
if f64_is_inf(x) {
if y < 0.0 {
return 0.0;
}
return f64::INFINITY;
}
if f64_is_inf(y) {
if x < 1.0 && x > -1.0 {
return 0.0;
}
return f64::INFINITY;
}
self.x87_exp(y * self.x87_log(x.abs())) * if x < 0.0 { -1.0f64.powi(y as i32) } else { 1.0 }
}
}
#[repr(C)]
pub struct CxaExceptionHeader {
pub reference_count: i64,
pub exception_type: *const u8,
pub exception_destructor: Option<unsafe extern "C" fn(*mut u8)>,
pub unexpected_handler: Option<unsafe extern "C" fn()>,
pub terminate_handler: Option<unsafe extern "C" fn()>,
pub next_exception: *mut CxaExceptionHeader,
pub handler_switch_value: i32,
pub action_record: *const u8,
pub language_specific_data: *const u8,
pub catch_temp: *mut u8,
pub adjusted_ptr: *mut u8,
pub exception_size: usize,
}
static mut TERMINATE_HANDLER: Option<unsafe extern "C" fn()> = None;
static mut UNEXPECTED_HANDLER: Option<unsafe extern "C" fn()> = None;
#[repr(C, align(128))]
struct EmergencyBuffer {
data: [u8; 1024],
}
static mut EMERGENCY_BUFFER: EmergencyBuffer = EmergencyBuffer { data: [0u8; 1024] };
static mut EMERGENCY_BUFFER_IN_USE: bool = false;
impl X86CompilerRT {
pub fn cxa_allocate_exception(&self, size: usize) -> *mut u8 {
let header_size = std::mem::size_of::<CxaExceptionHeader>();
let total_size = header_size + size;
let layout = std::alloc::Layout::from_size_align(total_size, 16).unwrap_or_else(|_| {
std::alloc::Layout::from_size_align(header_size + size, 8).unwrap()
});
unsafe {
let ptr = std::alloc::alloc(layout);
if ptr.is_null() {
if !EMERGENCY_BUFFER_IN_USE {
EMERGENCY_BUFFER_IN_USE = true;
return EMERGENCY_BUFFER.data.as_mut_ptr();
}
self.cxa_call_terminate();
return std::ptr::null_mut();
}
let header = ptr as *mut CxaExceptionHeader;
(*header).reference_count = 0;
(*header).exception_type = std::ptr::null();
(*header).exception_destructor = None;
(*header).unexpected_handler = UNEXPECTED_HANDLER;
(*header).terminate_handler = TERMINATE_HANDLER;
(*header).next_exception = std::ptr::null_mut();
(*header).handler_switch_value = 0;
(*header).action_record = std::ptr::null();
(*header).language_specific_data = std::ptr::null();
(*header).catch_temp = std::ptr::null_mut();
(*header).adjusted_ptr = std::ptr::null_mut();
(*header).exception_size = size;
ptr.add(header_size)
}
}
pub fn cxa_throw(
&self,
thrown_object: *mut u8,
tinfo: *const u8,
dest: Option<unsafe extern "C" fn(*mut u8)>,
) -> ! {
let header_size = std::mem::size_of::<CxaExceptionHeader>();
unsafe {
let header = thrown_object.sub(header_size) as *mut CxaExceptionHeader;
(*header).exception_type = tinfo;
(*header).exception_destructor = dest;
(*header).reference_count += 1;
}
self.cxa_call_terminate();
loop {
std::hint::spin_loop();
}
}
pub fn cxa_begin_catch(&self, exception_object: *mut u8) -> *mut u8 {
unsafe {
let header = self.get_exception_header(exception_object);
if !header.is_null() {
(*header).handler_switch_value += 1;
if !(*header).adjusted_ptr.is_null() {
return (*header).adjusted_ptr;
}
}
}
exception_object
}
pub fn cxa_end_catch(&self) {
}
pub fn cxa_rethrow(&self) -> ! {
self.cxa_call_terminate();
loop {
std::hint::spin_loop();
}
}
pub fn cxa_guard_acquire(&self, guard: *mut u64) -> i32 {
unsafe {
let guard_byte = guard as *mut u8;
if *guard_byte == 1 {
return 0;
}
let old = std::ptr::read_volatile(guard_byte);
if old == 0 {
std::ptr::write_volatile(guard_byte, 0xFF);
std::sync::atomic::fence(std::sync::atomic::Ordering::Acquire);
return 1;
}
if old == 0xFF {
while std::ptr::read_volatile(guard_byte) == 0xFF {
std::hint::spin_loop();
}
return 0;
}
0
}
}
pub fn cxa_guard_release(&self, guard: *mut u64) {
unsafe {
let guard_byte = guard as *mut u8;
std::sync::atomic::fence(std::sync::atomic::Ordering::Release);
std::ptr::write_volatile(guard_byte, 1);
}
}
pub fn cxa_guard_abort(&self, guard: *mut u64) {
unsafe {
let guard_byte = guard as *mut u8;
std::sync::atomic::fence(std::sync::atomic::Ordering::Release);
std::ptr::write_volatile(guard_byte, 0);
}
}
pub fn cxa_pure_virtual(&self) -> ! {
#[cfg(debug_assertions)]
panic!("pure virtual function called");
#[cfg(not(debug_assertions))]
unsafe {
libc::abort();
}
}
pub fn cxa_deleted_virtual(&self) -> ! {
#[cfg(debug_assertions)]
panic!("deleted virtual function called");
#[cfg(not(debug_assertions))]
unsafe {
libc::abort();
}
}
pub fn unwind_raise_exception(&self, exception_object: *mut u8) -> u32 {
5 }
pub fn unwind_forced_unwind(
&self,
exception_object: *mut u8,
stop: Option<unsafe extern "C" fn(i32, u32, u64, *mut u8, *mut u8) -> u32>,
stop_arg: *mut u8,
) -> u32 {
5 }
pub fn unwind_resume(&self, exception_object: *mut u8) -> ! {
self.cxa_call_terminate();
loop {
std::hint::spin_loop();
}
}
pub fn unwind_delete_exception(&self, exception_object: *mut u8) {
let header_size = std::mem::size_of::<CxaExceptionHeader>();
unsafe {
let header = exception_object.sub(header_size) as *mut CxaExceptionHeader;
if let Some(destructor) = (*header).exception_destructor {
destructor(header.add(1) as *mut u8);
}
let ptr = header as *mut u8;
if ptr == EMERGENCY_BUFFER.data.as_mut_ptr() {
EMERGENCY_BUFFER_IN_USE = false;
} else {
let total_size = header_size + (*header).exception_size;
let layout = std::alloc::Layout::from_size_align(total_size, 16).unwrap();
std::alloc::dealloc(ptr, layout);
}
}
}
pub fn unwind_get_gr(&self, ctx: *const u8, reg_index: i32) -> u64 {
unsafe {
let reg_ptr = ctx.add(reg_index as usize * 8);
std::ptr::read_unaligned(reg_ptr as *const u64)
}
}
pub fn unwind_set_gr(&self, ctx: *mut u8, reg_index: i32, value: u64) {
unsafe {
let reg_ptr = ctx.add(reg_index as usize * 8);
std::ptr::write_unaligned(reg_ptr as *mut u64, value);
}
}
pub fn unwind_get_ip(&self, ctx: *const u8) -> u64 {
unsafe {
let ip_ptr = ctx.add(128);
std::ptr::read_unaligned(ip_ptr as *const u64)
}
}
pub fn unwind_set_ip(&self, ctx: *mut u8, value: u64) {
unsafe {
let ip_ptr = ctx.add(128);
std::ptr::write_unaligned(ip_ptr as *mut u64, value);
}
}
pub fn unwind_get_cfa(&self, ctx: *const u8) -> u64 {
unsafe {
let cfa_ptr = ctx.add(96);
std::ptr::read_unaligned(cfa_ptr as *const u64)
}
}
pub fn unwind_get_language_specific_data(&self, ip: u64) -> *const u8 {
std::ptr::null()
}
pub fn unwind_get_region_start(&self, ip: u64) -> u64 {
0
}
pub fn gxx_personality_v0(
&self,
version: i32,
actions: u32,
exception_class: u64,
exception_object: *mut u8,
context: *mut u8,
) -> u32 {
if version != 1 {
return 9; }
const UA_SEARCH_PHASE: u32 = 1;
const UA_CLEANUP_PHASE: u32 = 2;
const UA_HANDLER_FRAME: u32 = 4;
const UA_FORCE_UNWIND: u32 = 8;
let is_force_unwind = (actions & UA_FORCE_UNWIND) != 0;
match actions & 3 {
UA_SEARCH_PHASE => {
8 }
UA_CLEANUP_PHASE => {
if (actions & UA_HANDLER_FRAME) != 0 {
7 } else if is_force_unwind {
8 } else {
8 }
}
_ => 8, }
}
pub fn gcc_personality_v0(
&self,
version: i32,
actions: u32,
_exception_class: u64,
_exception_object: *mut u8,
_context: *mut u8,
) -> u32 {
if version != 1 {
return 9;
}
8 }
pub fn cxa_demangle(
&self,
mangled_name: *const u8,
_output_buffer: *mut u8,
_length: *mut usize,
status: *mut i32,
) -> *mut u8 {
if mangled_name.is_null() {
unsafe {
if !status.is_null() {
*status = -2; }
}
return std::ptr::null_mut();
}
let c_str = unsafe {
let len = (0usize..).find(|&i| *mangled_name.add(i) == 0).unwrap_or(0);
std::slice::from_raw_parts(mangled_name, len)
};
let mangled = String::from_utf8_lossy(c_str);
let demangled = self.x86_demangle_itanium(&mangled);
match demangled {
Some(demangled) => {
unsafe {
if !status.is_null() {
*status = 0; }
}
let len = demangled.len() + 1;
let layout = std::alloc::Layout::from_size_align(len, 1).unwrap();
unsafe {
let buf = std::alloc::alloc(layout);
if buf.is_null() {
if !status.is_null() {
*status = -1; }
return std::ptr::null_mut();
}
std::ptr::copy_nonoverlapping(demangled.as_ptr(), buf, demangled.len());
*buf.add(demangled.len()) = 0; buf
}
}
None => {
unsafe {
if !status.is_null() {
*status = -2; }
}
std::ptr::null_mut()
}
}
}
fn x86_demangle_itanium(&self, mangled: &str) -> Option<String> {
if mangled.is_empty() {
return None;
}
if !mangled.starts_with("_Z") {
return None;
}
let s = &mangled[2..]; let mut result = String::new();
self.demangle_itanium_inner(s, &mut result)?;
Some(result)
}
fn demangle_itanium_inner(&self, s: &str, result: &mut String) -> Option<()> {
if s.is_empty() {
return Some(());
}
let bytes = s.as_bytes();
match bytes[0] {
b'N' => {
let end = s.find('E')?;
let inner = &s[1..end];
self.demangle_itanium_inner(inner, result)?;
self.demangle_itanium_inner(&s[end + 1..], result)
}
b'0'..=b'9' => {
let mut i = 0;
while i < bytes.len() && bytes[i].is_ascii_digit() {
i += 1;
}
let len: usize = s[..i].parse().ok()?;
let name = &s[i..i + len];
if !result.is_empty() && !result.ends_with("::") {
result.push_str("::");
}
result.push_str(name);
self.demangle_itanium_inner(&s[i + len..], result)
}
b'i' => {
result.push_str("int");
self.demangle_itanium_inner(&s[1..], result)
}
b'f' => {
result.push_str("float");
self.demangle_itanium_inner(&s[1..], result)
}
b'd' => {
result.push_str("double");
self.demangle_itanium_inner(&s[1..], result)
}
b'v' => {
result.push_str("void");
self.demangle_itanium_inner(&s[1..], result)
}
b'c' => {
result.push_str("char");
self.demangle_itanium_inner(&s[1..], result)
}
b'l' => {
result.push_str("long");
self.demangle_itanium_inner(&s[1..], result)
}
_ => {
self.demangle_itanium_inner(&s[1..], result)
}
}
}
fn get_exception_header(&self, exception_object: *mut u8) -> *mut CxaExceptionHeader {
if exception_object.is_null() {
return std::ptr::null_mut();
}
let header_size = std::mem::size_of::<CxaExceptionHeader>();
unsafe { exception_object.sub(header_size) as *mut CxaExceptionHeader }
}
fn cxa_call_terminate(&self) -> ! {
unsafe {
if let Some(handler) = TERMINATE_HANDLER {
handler();
}
libc::abort();
}
}
}
impl X86CompilerRT {
pub fn stack_chk_fail(&self) -> ! {
#[cfg(debug_assertions)]
panic!("stack smashing detected");
#[cfg(not(debug_assertions))]
unsafe {
libc::abort();
}
}
pub fn stack_chk_guard(&self) -> u64 {
self.stack_guard
}
pub fn memcpy_chk(&self, dest: *mut u8, src: *const u8, n: usize, destlen: usize) -> *mut u8 {
if n > destlen {
self.stack_chk_fail();
}
unsafe {
std::ptr::copy_nonoverlapping(src, dest, n);
}
dest
}
pub fn memmove_chk(&self, dest: *mut u8, src: *const u8, n: usize, destlen: usize) -> *mut u8 {
if n > destlen {
self.stack_chk_fail();
}
unsafe {
std::ptr::copy(src, dest, n);
}
dest
}
pub fn memset_chk(&self, dest: *mut u8, c: i32, n: usize, destlen: usize) -> *mut u8 {
if n > destlen {
self.stack_chk_fail();
}
unsafe {
std::ptr::write_bytes(dest, c as u8, n);
}
dest
}
pub fn strcpy_chk(&self, dest: *mut u8, src: *const u8, destlen: usize) -> *mut u8 {
let srclen = unsafe { self.strlen_raw(src) };
if srclen >= destlen {
self.stack_chk_fail();
}
unsafe {
std::ptr::copy_nonoverlapping(src, dest, srclen + 1);
}
dest
}
pub fn strncpy_chk(&self, dest: *mut u8, src: *const u8, n: usize, destlen: usize) -> *mut u8 {
if n > destlen {
self.stack_chk_fail();
}
unsafe {
let srclen = (0usize..n).find(|&i| *src.add(i) == 0).unwrap_or(n);
let copy_len = srclen.min(n);
std::ptr::copy_nonoverlapping(src, dest, copy_len);
if copy_len < n {
std::ptr::write_bytes(dest.add(copy_len), 0, n - copy_len);
}
}
dest
}
pub fn strcat_chk(&self, dest: *mut u8, src: *const u8, destlen: usize) -> *mut u8 {
let dest_len = unsafe { self.strlen_raw(dest) };
let src_len = unsafe { self.strlen_raw(src) };
if dest_len + src_len >= destlen {
self.stack_chk_fail();
}
unsafe {
std::ptr::copy_nonoverlapping(src, dest.add(dest_len), src_len + 1);
}
dest
}
pub fn strncat_chk(&self, dest: *mut u8, src: *const u8, n: usize, destlen: usize) -> *mut u8 {
let dest_len = unsafe { self.strlen_raw(dest) };
let remaining = destlen.saturating_sub(dest_len);
if n > remaining {
self.stack_chk_fail();
}
unsafe {
let copy_len = (0usize..n).find(|&i| *src.add(i) == 0).unwrap_or(n);
let actual = copy_len.min(n);
std::ptr::copy_nonoverlapping(src, dest.add(dest_len), actual);
*dest.add(dest_len + actual) = 0; }
dest
}
pub fn sprintf_chk(
&self,
dest: *mut u8,
_flag: i32,
destlen: usize,
fmt: *const u8,
) -> i32 {
if dest.is_null() || fmt.is_null() {
return -1;
}
if destlen == 0 {
self.stack_chk_fail();
}
unsafe {
*dest = 0;
}
0
}
pub fn snprintf_chk(
&self,
dest: *mut u8,
n: usize,
_flag: i32,
destlen: usize,
fmt: *const u8,
) -> i32 {
if n > destlen {
self.stack_chk_fail();
}
if dest.is_null() || fmt.is_null() {
return -1;
}
if n > 0 {
unsafe {
*dest = 0;
}
}
0
}
pub fn vsprintf_chk(
&self,
dest: *mut u8,
_flag: i32,
destlen: usize,
fmt: *const u8,
_ap: *mut u8, ) -> i32 {
if dest.is_null() || fmt.is_null() {
return -1;
}
if destlen == 0 {
self.stack_chk_fail();
}
unsafe {
*dest = 0;
}
0
}
pub fn vsnprintf_chk(
&self,
dest: *mut u8,
n: usize,
_flag: i32,
destlen: usize,
fmt: *const u8,
_ap: *mut u8, ) -> i32 {
if n > destlen {
self.stack_chk_fail();
}
if dest.is_null() || fmt.is_null() {
return -1;
}
if n > 0 {
unsafe {
*dest = 0;
}
}
0
}
unsafe fn strlen_raw(&self, s: *const u8) -> usize {
if s.is_null() {
return 0;
}
(0usize..).find(|&i| *s.add(i) == 0).unwrap_or(0)
}
}
impl X86CompilerRT {
pub fn has_sse(&self) -> bool {
self.subtarget.has_feature("sse")
}
pub fn has_sse2(&self) -> bool {
self.subtarget.has_feature("sse2")
}
pub fn has_avx(&self) -> bool {
self.subtarget.has_feature("avx")
}
pub fn has_avx2(&self) -> bool {
self.subtarget.has_feature("avx2")
}
pub fn has_avx512(&self) -> bool {
self.subtarget.has_feature("avx512f")
}
pub fn cpu_mask(&self) -> u64 {
let mut mask: u64 = 0;
if self.has_sse() {
mask |= 1 << 0;
}
if self.has_sse2() {
mask |= 1 << 1;
}
if self.has_avx() {
mask |= 1 << 2;
}
if self.has_avx2() {
mask |= 1 << 3;
}
if self.has_avx512() {
mask |= 1 << 4;
}
mask
}
pub fn pointer_size(&self) -> usize {
if self.is_64bit {
8
} else {
4
}
}
pub fn stack_alignment(&self) -> u32 {
if self.is_64bit {
llvm_native_core::x86::X86_STACK_ALIGNMENT_64
} else {
llvm_native_core::x86::X86_STACK_ALIGNMENT_32
}
}
}
static mut GLOBAL_X86_RT: Option<X86CompilerRT> = None;
pub fn init_x86_compiler_rt(is_64bit: bool) {
unsafe {
GLOBAL_X86_RT = Some(X86CompilerRT::new(
if is_64bit {
X86Subtarget::default_x86_64()
} else {
X86Subtarget::default_i386()
},
is_64bit,
));
}
}
fn global_rt() -> &'static X86CompilerRT {
unsafe {
GLOBAL_X86_RT
.as_ref()
.expect("X86CompilerRT not initialized; call init_x86_compiler_rt first")
}
}
#[no_mangle]
pub extern "C" fn __addsf3(a: f32, b: f32) -> f32 {
global_rt().addsf3(a, b)
}
#[no_mangle]
pub extern "C" fn __adddf3(a: f64, b: f64) -> f64 {
global_rt().adddf3(a, b)
}
#[no_mangle]
pub extern "C" fn __subsf3(a: f32, b: f32) -> f32 {
global_rt().subsf3(a, b)
}
#[no_mangle]
pub extern "C" fn __subdf3(a: f64, b: f64) -> f64 {
global_rt().subdf3(a, b)
}
#[no_mangle]
pub extern "C" fn __mulsf3(a: f32, b: f32) -> f32 {
global_rt().mulsf3(a, b)
}
#[no_mangle]
pub extern "C" fn __muldf3(a: f64, b: f64) -> f64 {
global_rt().muldf3(a, b)
}
#[no_mangle]
pub extern "C" fn __divsf3(a: f32, b: f32) -> f32 {
global_rt().divsf3(a, b)
}
#[no_mangle]
pub extern "C" fn __divdf3(a: f64, b: f64) -> f64 {
global_rt().divdf3(a, b)
}
#[no_mangle]
pub extern "C" fn __negsf2(a: f32) -> f32 {
global_rt().negsf2(a)
}
#[no_mangle]
pub extern "C" fn __negdf2(a: f64) -> f64 {
global_rt().negdf2(a)
}
#[no_mangle]
pub extern "C" fn __cmpsf2(a: f32, b: f32) -> i32 {
global_rt().cmpsf2(a, b)
}
#[no_mangle]
pub extern "C" fn __cmpdf2(a: f64, b: f64) -> i32 {
global_rt().cmpdf2(a, b)
}
#[no_mangle]
pub extern "C" fn __eqsf2(a: f32, b: f32) -> i32 {
global_rt().eqsf2(a, b)
}
#[no_mangle]
pub extern "C" fn __eqdf2(a: f64, b: f64) -> i32 {
global_rt().eqdf2(a, b)
}
#[no_mangle]
pub extern "C" fn __nesf2(a: f32, b: f32) -> i32 {
global_rt().nesf2(a, b)
}
#[no_mangle]
pub extern "C" fn __nedf2(a: f64, b: f64) -> i32 {
global_rt().nedf2(a, b)
}
#[no_mangle]
pub extern "C" fn __gesf2(a: f32, b: f32) -> i32 {
global_rt().gesf2(a, b)
}
#[no_mangle]
pub extern "C" fn __gedf2(a: f64, b: f64) -> i32 {
global_rt().gedf2(a, b)
}
#[no_mangle]
pub extern "C" fn __ltsf2(a: f32, b: f32) -> i32 {
global_rt().ltsf2(a, b)
}
#[no_mangle]
pub extern "C" fn __ltdf2(a: f64, b: f64) -> i32 {
global_rt().ltdf2(a, b)
}
#[no_mangle]
pub extern "C" fn __lesf2(a: f32, b: f32) -> i32 {
global_rt().lesf2(a, b)
}
#[no_mangle]
pub extern "C" fn __ledf2(a: f64, b: f64) -> i32 {
global_rt().ledf2(a, b)
}
#[no_mangle]
pub extern "C" fn __extendsfdf2(a: f32) -> f64 {
global_rt().extendsfdf2(a)
}
#[no_mangle]
pub extern "C" fn __truncdfsf2(a: f64) -> f32 {
global_rt().truncdfsf2(a)
}
#[no_mangle]
pub extern "C" fn __fixsfsi(a: f32) -> i32 {
global_rt().fixsfsi(a)
}
#[no_mangle]
pub extern "C" fn __fixdfsi(a: f64) -> i32 {
global_rt().fixdfsi(a)
}
#[no_mangle]
pub extern "C" fn __fixsfdi(a: f32) -> i64 {
global_rt().fixsfdi(a)
}
#[no_mangle]
pub extern "C" fn __fixdfdi(a: f64) -> i64 {
global_rt().fixdfdi(a)
}
#[no_mangle]
pub extern "C" fn __floatsisf(a: i32) -> f32 {
global_rt().floatsisf(a)
}
#[no_mangle]
pub extern "C" fn __floatsidf(a: i32) -> f64 {
global_rt().floatsidf(a)
}
#[no_mangle]
pub extern "C" fn __floatdisf(a: i64) -> f32 {
global_rt().floatdisf(a)
}
#[no_mangle]
pub extern "C" fn __floatdidf(a: i64) -> f64 {
global_rt().floatdidf(a)
}
#[no_mangle]
pub extern "C" fn __fixunssfsi(a: f32) -> u32 {
global_rt().fixunssfsi(a)
}
#[no_mangle]
pub extern "C" fn __fixunsdfsi(a: f64) -> u32 {
global_rt().fixunsdfsi(a)
}
#[no_mangle]
pub extern "C" fn __fixunssfdi(a: f32) -> u64 {
global_rt().fixunssfdi(a)
}
#[no_mangle]
pub extern "C" fn __fixunsdfdi(a: f64) -> u64 {
global_rt().fixunsdfdi(a)
}
#[no_mangle]
pub extern "C" fn __floatunsisf(a: u32) -> f32 {
global_rt().floatunsisf(a)
}
#[no_mangle]
pub extern "C" fn __floatunsidf(a: u32) -> f64 {
global_rt().floatunsidf(a)
}
#[no_mangle]
pub extern "C" fn __floatundisf(a: u64) -> f32 {
global_rt().floatundisf(a)
}
#[no_mangle]
pub extern "C" fn __floatundidf(a: u64) -> f64 {
global_rt().floatundidf(a)
}
#[no_mangle]
pub extern "C" fn __powisf2(a: f32, b: i32) -> f32 {
global_rt().powisf2(a, b)
}
#[no_mangle]
pub extern "C" fn __powidf2(a: f64, b: i32) -> f64 {
global_rt().powidf2(a, b)
}
#[no_mangle]
pub extern "C" fn __divdi3(a: i64, b: i64) -> i64 {
global_rt().divdi3(a, b)
}
#[no_mangle]
pub extern "C" fn __moddi3(a: i64, b: i64) -> i64 {
global_rt().moddi3(a, b)
}
#[no_mangle]
pub extern "C" fn __udivdi3(a: u64, b: u64) -> u64 {
global_rt().udivdi3(a, b)
}
#[no_mangle]
pub extern "C" fn __umoddi3(a: u64, b: u64) -> u64 {
global_rt().umoddi3(a, b)
}
#[no_mangle]
pub extern "C" fn __divsi3(a: i32, b: i32) -> i32 {
global_rt().divsi3(a, b)
}
#[no_mangle]
pub extern "C" fn __modsi3(a: i32, b: i32) -> i32 {
global_rt().modsi3(a, b)
}
#[no_mangle]
pub extern "C" fn __udivsi3(a: u32, b: u32) -> u32 {
global_rt().udivsi3(a, b)
}
#[no_mangle]
pub extern "C" fn __umodsi3(a: u32, b: u32) -> u32 {
global_rt().umodsi3(a, b)
}
#[no_mangle]
pub extern "C" fn __muldi3(a: i64, b: i64) -> i64 {
global_rt().muldi3(a, b)
}
#[no_mangle]
pub extern "C" fn __ashldi3(a: i64, b: i32) -> i64 {
global_rt().ashldi3(a, b)
}
#[no_mangle]
pub extern "C" fn __ashrdi3(a: i64, b: i32) -> i64 {
global_rt().ashrdi3(a, b)
}
#[no_mangle]
pub extern "C" fn __lshrdi3(a: u64, b: i32) -> u64 {
global_rt().lshrdi3(a, b)
}
#[no_mangle]
pub extern "C" fn __clzsi2(a: u32) -> i32 {
global_rt().clzsi2(a)
}
#[no_mangle]
pub extern "C" fn __clzdi2(a: u64) -> i32 {
global_rt().clzdi2(a)
}
#[no_mangle]
pub extern "C" fn __ctzsi2(a: u32) -> i32 {
global_rt().ctzsi2(a)
}
#[no_mangle]
pub extern "C" fn __ctzdi2(a: u64) -> i32 {
global_rt().ctzdi2(a)
}
#[no_mangle]
pub extern "C" fn __popcountsi2(a: u32) -> i32 {
global_rt().popcountsi2(a)
}
#[no_mangle]
pub extern "C" fn __popcountdi2(a: u64) -> i32 {
global_rt().popcountdi2(a)
}
#[no_mangle]
pub extern "C" fn __paritysi2(a: u32) -> i32 {
global_rt().paritysi2(a)
}
#[no_mangle]
pub extern "C" fn __paritydi2(a: u64) -> i32 {
global_rt().paritydi2(a)
}
#[no_mangle]
pub extern "C" fn __bswapsi2(a: u32) -> u32 {
global_rt().bswapsi2(a)
}
#[no_mangle]
pub extern "C" fn __bswapdi2(a: u64) -> u64 {
global_rt().bswapdi2(a)
}
#[no_mangle]
pub extern "C" fn __addvsi3(a: i32, b: i32) -> i32 {
global_rt().addvsi3(a, b)
}
#[no_mangle]
pub extern "C" fn __addvdi3(a: i64, b: i64) -> i64 {
global_rt().addvdi3(a, b)
}
#[no_mangle]
pub extern "C" fn __subvsi3(a: i32, b: i32) -> i32 {
global_rt().subvsi3(a, b)
}
#[no_mangle]
pub extern "C" fn __subvdi3(a: i64, b: i64) -> i64 {
global_rt().subvdi3(a, b)
}
#[no_mangle]
pub extern "C" fn __mulvsi3(a: i32, b: i32) -> i32 {
global_rt().mulvsi3(a, b)
}
#[no_mangle]
pub extern "C" fn __mulvdi3(a: i64, b: i64) -> i64 {
global_rt().mulvdi3(a, b)
}
#[no_mangle]
pub extern "C" fn __negvsi2(a: i32) -> i32 {
global_rt().negvsi2(a)
}
#[no_mangle]
pub extern "C" fn __negvdi2(a: i64) -> i64 {
global_rt().negvdi2(a)
}
#[no_mangle]
pub extern "C" fn __stack_chk_fail() -> ! {
global_rt().stack_chk_fail()
}
#[no_mangle]
pub extern "C" fn __stack_chk_guard() -> u64 {
global_rt().stack_chk_guard()
}
#[no_mangle]
pub extern "C" fn __memcpy_chk(dest: *mut u8, src: *const u8, n: usize, destlen: usize) -> *mut u8 {
global_rt().memcpy_chk(dest, src, n, destlen)
}
#[no_mangle]
pub extern "C" fn __memmove_chk(
dest: *mut u8,
src: *const u8,
n: usize,
destlen: usize,
) -> *mut u8 {
global_rt().memmove_chk(dest, src, n, destlen)
}
#[no_mangle]
pub extern "C" fn __memset_chk(dest: *mut u8, c: i32, n: usize, destlen: usize) -> *mut u8 {
global_rt().memset_chk(dest, c, n, destlen)
}
#[no_mangle]
pub extern "C" fn __sprintf_chk(dest: *mut u8, flag: i32, destlen: usize, fmt: *const u8) -> i32 {
global_rt().sprintf_chk(dest, flag, destlen, fmt)
}
#[no_mangle]
pub extern "C" fn __snprintf_chk(
dest: *mut u8,
n: usize,
flag: i32,
destlen: usize,
fmt: *const u8,
) -> i32 {
global_rt().snprintf_chk(dest, n, flag, destlen, fmt)
}
#[no_mangle]
pub extern "C" fn __vsprintf_chk(
dest: *mut u8,
flag: i32,
destlen: usize,
fmt: *const u8,
ap: *mut u8,
) -> i32 {
global_rt().vsprintf_chk(dest, flag, destlen, fmt, ap)
}
#[no_mangle]
pub extern "C" fn __vsnprintf_chk(
dest: *mut u8,
n: usize,
flag: i32,
destlen: usize,
fmt: *const u8,
ap: *mut u8,
) -> i32 {
global_rt().vsnprintf_chk(dest, n, flag, destlen, fmt, ap)
}
#[no_mangle]
pub extern "C" fn __strcpy_chk(dest: *mut u8, src: *const u8, destlen: usize) -> *mut u8 {
global_rt().strcpy_chk(dest, src, destlen)
}
#[no_mangle]
pub extern "C" fn __strncpy_chk(
dest: *mut u8,
src: *const u8,
n: usize,
destlen: usize,
) -> *mut u8 {
global_rt().strncpy_chk(dest, src, n, destlen)
}
#[no_mangle]
pub extern "C" fn __strcat_chk(dest: *mut u8, src: *const u8, destlen: usize) -> *mut u8 {
global_rt().strcat_chk(dest, src, destlen)
}
#[no_mangle]
pub extern "C" fn __strncat_chk(
dest: *mut u8,
src: *const u8,
n: usize,
destlen: usize,
) -> *mut u8 {
global_rt().strncat_chk(dest, src, n, destlen)
}
#[no_mangle]
pub extern "C" fn __cxa_allocate_exception(size: usize) -> *mut u8 {
global_rt().cxa_allocate_exception(size)
}
#[no_mangle]
pub extern "C" fn __cxa_throw(
thrown_object: *mut u8,
tinfo: *const u8,
dest: Option<unsafe extern "C" fn(*mut u8)>,
) -> ! {
global_rt().cxa_throw(thrown_object, tinfo, dest)
}
#[no_mangle]
pub extern "C" fn __cxa_begin_catch(exception_object: *mut u8) -> *mut u8 {
global_rt().cxa_begin_catch(exception_object)
}
#[no_mangle]
pub extern "C" fn __cxa_end_catch() {
global_rt().cxa_end_catch()
}
#[no_mangle]
pub extern "C" fn __cxa_rethrow() -> ! {
global_rt().cxa_rethrow()
}
#[no_mangle]
pub extern "C" fn __cxa_guard_acquire(guard: *mut u64) -> i32 {
global_rt().cxa_guard_acquire(guard)
}
#[no_mangle]
pub extern "C" fn __cxa_guard_release(guard: *mut u64) {
global_rt().cxa_guard_release(guard)
}
#[no_mangle]
pub extern "C" fn __cxa_guard_abort(guard: *mut u64) {
global_rt().cxa_guard_abort(guard)
}
#[no_mangle]
pub extern "C" fn __cxa_pure_virtual() -> ! {
global_rt().cxa_pure_virtual()
}
#[no_mangle]
pub extern "C" fn __cxa_deleted_virtual() -> ! {
global_rt().cxa_deleted_virtual()
}
#[no_mangle]
pub extern "C" fn __cxa_demangle(
mangled_name: *const u8,
output_buffer: *mut u8,
length: *mut usize,
status: *mut i32,
) -> *mut u8 {
global_rt().cxa_demangle(mangled_name, output_buffer, length, status)
}
#[no_mangle]
pub extern "C" fn __gxx_personality_v0(
version: i32,
actions: u32,
exception_class: u64,
exception_object: *mut u8,
context: *mut u8,
) -> u32 {
global_rt().gxx_personality_v0(version, actions, exception_class, exception_object, context)
}
#[no_mangle]
pub extern "C" fn __gcc_personality_v0(
version: i32,
actions: u32,
exception_class: u64,
exception_object: *mut u8,
context: *mut u8,
) -> u32 {
global_rt().gcc_personality_v0(version, actions, exception_class, exception_object, context)
}
#[no_mangle]
pub extern "C" fn _Unwind_RaiseException(exception_object: *mut u8) -> u32 {
global_rt().unwind_raise_exception(exception_object)
}
#[no_mangle]
pub extern "C" fn _Unwind_ForcedUnwind(
exception_object: *mut u8,
stop: Option<unsafe extern "C" fn(i32, u32, u64, *mut u8, *mut u8) -> u32>,
stop_arg: *mut u8,
) -> u32 {
global_rt().unwind_forced_unwind(exception_object, stop, stop_arg)
}
#[no_mangle]
pub extern "C" fn _Unwind_Resume(exception_object: *mut u8) -> ! {
global_rt().unwind_resume(exception_object)
}
#[no_mangle]
pub extern "C" fn _Unwind_DeleteException(exception_object: *mut u8) {
global_rt().unwind_delete_exception(exception_object)
}
#[no_mangle]
pub extern "C" fn _Unwind_GetGR(ctx: *const u8, reg_index: i32) -> u64 {
global_rt().unwind_get_gr(ctx, reg_index)
}
#[no_mangle]
pub extern "C" fn _Unwind_SetGR(ctx: *mut u8, reg_index: i32, value: u64) {
global_rt().unwind_set_gr(ctx, reg_index, value)
}
#[no_mangle]
pub extern "C" fn _Unwind_GetIP(ctx: *const u8) -> u64 {
global_rt().unwind_get_ip(ctx)
}
#[no_mangle]
pub extern "C" fn _Unwind_SetIP(ctx: *mut u8, value: u64) {
global_rt().unwind_set_ip(ctx, value)
}
#[no_mangle]
pub extern "C" fn _Unwind_GetCFA(ctx: *const u8) -> u64 {
global_rt().unwind_get_cfa(ctx)
}
#[no_mangle]
pub extern "C" fn _Unwind_GetLanguageSpecificData(ip: u64) -> *const u8 {
global_rt().unwind_get_language_specific_data(ip)
}
#[no_mangle]
pub extern "C" fn _Unwind_GetRegionStart(ip: u64) -> u64 {
global_rt().unwind_get_region_start(ip)
}
#[no_mangle]
pub extern "C" fn __sync_fetch_and_add_4(ptr: *mut u32, val: u32) -> u32 {
global_rt().sync_fetch_and_add_4(ptr, val)
}
#[no_mangle]
pub extern "C" fn __sync_fetch_and_add_8(ptr: *mut u64, val: u64) -> u64 {
global_rt().sync_fetch_and_add_8(ptr, val)
}
#[no_mangle]
pub extern "C" fn __sync_fetch_and_sub_4(ptr: *mut u32, val: u32) -> u32 {
global_rt().sync_fetch_and_sub_4(ptr, val)
}
#[no_mangle]
pub extern "C" fn __sync_fetch_and_sub_8(ptr: *mut u64, val: u64) -> u64 {
global_rt().sync_fetch_and_sub_8(ptr, val)
}
#[no_mangle]
pub extern "C" fn __sync_fetch_and_and_4(ptr: *mut u32, val: u32) -> u32 {
global_rt().sync_fetch_and_and_4(ptr, val)
}
#[no_mangle]
pub extern "C" fn __sync_fetch_and_and_8(ptr: *mut u64, val: u64) -> u64 {
global_rt().sync_fetch_and_and_8(ptr, val)
}
#[no_mangle]
pub extern "C" fn __sync_fetch_and_or_4(ptr: *mut u32, val: u32) -> u32 {
global_rt().sync_fetch_and_or_4(ptr, val)
}
#[no_mangle]
pub extern "C" fn __sync_fetch_and_or_8(ptr: *mut u64, val: u64) -> u64 {
global_rt().sync_fetch_and_or_8(ptr, val)
}
#[no_mangle]
pub extern "C" fn __sync_fetch_and_xor_4(ptr: *mut u32, val: u32) -> u32 {
global_rt().sync_fetch_and_xor_4(ptr, val)
}
#[no_mangle]
pub extern "C" fn __sync_fetch_and_xor_8(ptr: *mut u64, val: u64) -> u64 {
global_rt().sync_fetch_and_xor_8(ptr, val)
}
#[no_mangle]
pub extern "C" fn __sync_fetch_and_nand_4(ptr: *mut u32, val: u32) -> u32 {
global_rt().sync_fetch_and_nand_4(ptr, val)
}
#[no_mangle]
pub extern "C" fn __sync_fetch_and_nand_8(ptr: *mut u64, val: u64) -> u64 {
global_rt().sync_fetch_and_nand_8(ptr, val)
}
#[no_mangle]
pub extern "C" fn __sync_val_compare_and_swap_4(ptr: *mut u32, oldval: u32, newval: u32) -> u32 {
global_rt().sync_val_compare_and_swap_4(ptr, oldval, newval)
}
#[no_mangle]
pub extern "C" fn __sync_val_compare_and_swap_8(ptr: *mut u64, oldval: u64, newval: u64) -> u64 {
global_rt().sync_val_compare_and_swap_8(ptr, oldval, newval)
}
#[no_mangle]
pub extern "C" fn __sync_bool_compare_and_swap_4(ptr: *mut u32, oldval: u32, newval: u32) -> bool {
global_rt().sync_bool_compare_and_swap_4(ptr, oldval, newval)
}
#[no_mangle]
pub extern "C" fn __sync_bool_compare_and_swap_8(ptr: *mut u64, oldval: u64, newval: u64) -> bool {
global_rt().sync_bool_compare_and_swap_8(ptr, oldval, newval)
}
#[no_mangle]
pub extern "C" fn __sync_lock_test_and_set_4(ptr: *mut u32, val: u32) -> u32 {
global_rt().sync_lock_test_and_set_4(ptr, val)
}
#[no_mangle]
pub extern "C" fn __sync_lock_test_and_set_8(ptr: *mut u64, val: u64) -> u64 {
global_rt().sync_lock_test_and_set_8(ptr, val)
}
#[no_mangle]
pub extern "C" fn __sync_lock_release_4(ptr: *mut u32) {
global_rt().sync_lock_release_4(ptr)
}
#[no_mangle]
pub extern "C" fn __sync_lock_release_8(ptr: *mut u64) {
global_rt().sync_lock_release_8(ptr)
}
#[no_mangle]
pub extern "C" fn __sync_synchronize() {
global_rt().sync_synchronize()
}
#[cfg(test)]
mod tests {
use super::*;
fn approx_eq_f32(a: f32, b: f32, epsilon: f32) -> bool {
(a - b).abs() < epsilon
}
fn approx_eq_f64(a: f64, b: f64, epsilon: f64) -> bool {
(a - b).abs() < epsilon
}
fn make_rt_x86_64() -> X86CompilerRT {
X86CompilerRT::default_x86_64()
}
fn make_rt_i386() -> X86CompilerRT {
X86CompilerRT::default_i386()
}
mod soft_float {
use super::*;
#[test]
fn test_addsf3_basic() {
let rt = make_rt_x86_64();
assert!(approx_eq_f32(rt.addsf3(1.0, 2.0), 3.0, 1e-6));
}
#[test]
fn test_addsf3_negative() {
let rt = make_rt_x86_64();
assert!(approx_eq_f32(rt.addsf3(-1.0, -2.0), -3.0, 1e-6));
}
#[test]
fn test_addsf3_zero() {
let rt = make_rt_x86_64();
assert_eq!(rt.addsf3(0.0, 0.0), 0.0);
assert_eq!(rt.addsf3(5.0, 0.0), 5.0);
assert_eq!(rt.addsf3(0.0, 5.0), 5.0);
}
#[test]
fn test_addsf3_infinity() {
let rt = make_rt_x86_64();
assert_eq!(rt.addsf3(f32::INFINITY, 1.0), f32::INFINITY);
assert_eq!(rt.addsf3(f32::NEG_INFINITY, -1.0), f32::NEG_INFINITY);
}
#[test]
fn test_addsf3_nan() {
let rt = make_rt_x86_64();
assert!(rt.addsf3(f32::NAN, 1.0).is_nan());
}
#[test]
fn test_adddf3_basic() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(rt.adddf3(1.0, 2.0), 3.0, 1e-12));
}
#[test]
fn test_adddf3_negative() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(rt.adddf3(-1.0, -2.0), -3.0, 1e-12));
}
#[test]
fn test_adddf3_large() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(rt.adddf3(1e300, 1e300), 2e300, 1e288));
}
#[test]
fn test_adddf3_nan() {
let rt = make_rt_x86_64();
assert!(rt.adddf3(f64::NAN, 1.0).is_nan());
}
#[test]
fn test_adddf3_infinity() {
let rt = make_rt_x86_64();
assert_eq!(rt.adddf3(f64::INFINITY, 1.0), f64::INFINITY);
}
#[test]
fn test_subsf3_basic() {
let rt = make_rt_x86_64();
assert!(approx_eq_f32(rt.subsf3(5.0, 2.0), 3.0, 1e-6));
}
#[test]
fn test_subsf3_negative() {
let rt = make_rt_x86_64();
assert!(approx_eq_f32(rt.subsf3(2.0, 5.0), -3.0, 1e-6));
}
#[test]
fn test_subdf3_basic() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(rt.subdf3(5.0, 2.0), 3.0, 1e-12));
}
#[test]
fn test_mulsf3_basic() {
let rt = make_rt_x86_64();
assert!(approx_eq_f32(rt.mulsf3(3.0, 4.0), 12.0, 1e-6));
}
#[test]
fn test_mulsf3_negative() {
let rt = make_rt_x86_64();
assert!(approx_eq_f32(rt.mulsf3(-3.0, 4.0), -12.0, 1e-6));
}
#[test]
fn test_mulsf3_zero() {
let rt = make_rt_x86_64();
assert_eq!(rt.mulsf3(0.0, 5.0), 0.0);
}
#[test]
fn test_muldf3_basic() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(rt.muldf3(3.0, 4.0), 12.0, 1e-12));
}
#[test]
fn test_muldf3_large() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(rt.muldf3(1e154, 1e154), 1e308, 1e296));
}
#[test]
fn test_muldf3_nan() {
let rt = make_rt_x86_64();
assert!(rt.muldf3(f64::NAN, 1.0).is_nan());
}
#[test]
fn test_divsf3_basic() {
let rt = make_rt_x86_64();
assert!(approx_eq_f32(rt.divsf3(10.0, 2.0), 5.0, 1e-6));
}
#[test]
fn test_divsf3_by_zero_nonzero() {
let rt = make_rt_x86_64();
assert_eq!(rt.divsf3(1.0, 0.0), f32::INFINITY);
}
#[test]
fn test_divsf3_by_zero_negative() {
let rt = make_rt_x86_64();
assert_eq!(rt.divsf3(-1.0, 0.0), f32::NEG_INFINITY);
}
#[test]
fn test_divsf3_zero_by_zero() {
let rt = make_rt_x86_64();
assert!(rt.divsf3(0.0, 0.0).is_nan());
}
#[test]
fn test_divdf3_basic() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(rt.divdf3(10.0, 2.0), 5.0, 1e-12));
}
#[test]
fn test_divdf3_by_zero() {
let rt = make_rt_x86_64();
assert_eq!(rt.divdf3(1.0, 0.0), f64::INFINITY);
}
#[test]
fn test_divdf3_zero_by_zero() {
let rt = make_rt_x86_64();
assert!(rt.divdf3(0.0, 0.0).is_nan());
}
#[test]
fn test_divdf3_infinity() {
let rt = make_rt_x86_64();
assert_eq!(rt.divdf3(f64::INFINITY, 1.0), f64::INFINITY);
}
#[test]
fn test_negsf2_positive() {
let rt = make_rt_x86_64();
assert_eq!(rt.negsf2(3.0), -3.0);
}
#[test]
fn test_negsf2_negative() {
let rt = make_rt_x86_64();
assert_eq!(rt.negsf2(-3.0), 3.0);
}
#[test]
fn test_negsf2_zero() {
let rt = make_rt_x86_64();
let result = rt.negsf2(0.0);
assert_eq!(result.to_bits(), (-0.0f32).to_bits());
}
#[test]
fn test_negsf2_nan() {
let rt = make_rt_x86_64();
assert!(rt.negsf2(f32::NAN).is_nan());
}
#[test]
fn test_negdf2_basic() {
let rt = make_rt_x86_64();
assert_eq!(rt.negdf2(3.0), -3.0);
}
#[test]
fn test_negdf2_nan() {
let rt = make_rt_x86_64();
assert!(rt.negdf2(f64::NAN).is_nan());
}
#[test]
fn test_negdf2_infinity() {
let rt = make_rt_x86_64();
assert_eq!(rt.negdf2(f64::NEG_INFINITY), f64::INFINITY);
}
#[test]
fn test_cmpsf2_less() {
let rt = make_rt_x86_64();
assert_eq!(rt.cmpsf2(1.0, 2.0), -1);
}
#[test]
fn test_cmpsf2_equal() {
let rt = make_rt_x86_64();
assert_eq!(rt.cmpsf2(2.0, 2.0), 0);
}
#[test]
fn test_cmpsf2_greater() {
let rt = make_rt_x86_64();
assert_eq!(rt.cmpsf2(3.0, 2.0), 1);
}
#[test]
fn test_cmpsf2_nan() {
let rt = make_rt_x86_64();
assert_eq!(rt.cmpsf2(f32::NAN, 1.0), -2);
assert_eq!(rt.cmpsf2(1.0, f32::NAN), -2);
assert_eq!(rt.cmpsf2(f32::NAN, f32::NAN), -2);
}
#[test]
fn test_cmpdf2_less() {
let rt = make_rt_x86_64();
assert_eq!(rt.cmpdf2(1.0, 2.0), -1);
}
#[test]
fn test_cmpdf2_equal() {
let rt = make_rt_x86_64();
assert_eq!(rt.cmpdf2(2.0, 2.0), 0);
}
#[test]
fn test_cmpdf2_greater() {
let rt = make_rt_x86_64();
assert_eq!(rt.cmpdf2(3.0, 2.0), 1);
}
#[test]
fn test_cmpdf2_nan() {
let rt = make_rt_x86_64();
assert_eq!(rt.cmpdf2(f64::NAN, 1.0), -2);
}
#[test]
fn test_eqsf2_equal() {
let rt = make_rt_x86_64();
assert_eq!(rt.eqsf2(3.0, 3.0), 0);
}
#[test]
fn test_eqsf2_not_equal() {
let rt = make_rt_x86_64();
assert_eq!(rt.eqsf2(3.0, 4.0), 1);
}
#[test]
fn test_eqsf2_nan() {
let rt = make_rt_x86_64();
assert_eq!(rt.eqsf2(f32::NAN, 3.0), 1);
}
#[test]
fn test_eqdf2_equal() {
let rt = make_rt_x86_64();
assert_eq!(rt.eqdf2(3.0, 3.0), 0);
}
#[test]
fn test_eqdf2_nan() {
let rt = make_rt_x86_64();
assert_eq!(rt.eqdf2(f64::NAN, 3.0), 1);
}
#[test]
fn test_nesf2_not_equal() {
let rt = make_rt_x86_64();
assert_eq!(rt.nesf2(3.0, 4.0), 0);
}
#[test]
fn test_nesf2_equal() {
let rt = make_rt_x86_64();
assert_eq!(rt.nesf2(3.0, 3.0), 1);
}
#[test]
fn test_nesf2_nan() {
let rt = make_rt_x86_64();
assert_eq!(rt.nesf2(f32::NAN, 3.0), 1);
}
#[test]
fn test_nedf2_not_equal() {
let rt = make_rt_x86_64();
assert_eq!(rt.nedf2(3.0, 4.0), 0);
}
#[test]
fn test_nedf2_equal() {
let rt = make_rt_x86_64();
assert_eq!(rt.nedf2(3.0, 3.0), 1);
}
#[test]
fn test_gesf2_greater() {
let rt = make_rt_x86_64();
assert_eq!(rt.gesf2(3.0, 2.0), 0);
}
#[test]
fn test_gesf2_equal() {
let rt = make_rt_x86_64();
assert_eq!(rt.gesf2(3.0, 3.0), 0);
}
#[test]
fn test_gesf2_less() {
let rt = make_rt_x86_64();
assert_eq!(rt.gesf2(2.0, 3.0), -1);
}
#[test]
fn test_gesf2_nan() {
let rt = make_rt_x86_64();
assert_eq!(rt.gesf2(f32::NAN, 2.0), -1);
}
#[test]
fn test_gedf2_greater() {
let rt = make_rt_x86_64();
assert_eq!(rt.gedf2(3.0, 2.0), 0);
}
#[test]
fn test_gedf2_less() {
let rt = make_rt_x86_64();
assert_eq!(rt.gedf2(2.0, 3.0), -1);
}
#[test]
fn test_gedf2_nan() {
let rt = make_rt_x86_64();
assert_eq!(rt.gedf2(f64::NAN, 2.0), -1);
}
#[test]
fn test_ltsf2_less() {
let rt = make_rt_x86_64();
assert_eq!(rt.ltsf2(2.0, 3.0), 0);
}
#[test]
fn test_ltsf2_greater() {
let rt = make_rt_x86_64();
assert_eq!(rt.ltsf2(3.0, 2.0), 1);
}
#[test]
fn test_ltsf2_equal() {
let rt = make_rt_x86_64();
assert_eq!(rt.ltsf2(3.0, 3.0), 1);
}
#[test]
fn test_ltsf2_nan() {
let rt = make_rt_x86_64();
assert_eq!(rt.ltsf2(f32::NAN, 2.0), 1);
}
#[test]
fn test_ltdf2_less() {
let rt = make_rt_x86_64();
assert_eq!(rt.ltdf2(2.0, 3.0), 0);
}
#[test]
fn test_ltdf2_greater() {
let rt = make_rt_x86_64();
assert_eq!(rt.ltdf2(3.0, 2.0), 1);
}
#[test]
fn test_ltdf2_nan() {
let rt = make_rt_x86_64();
assert_eq!(rt.ltdf2(f64::NAN, 2.0), 1);
}
#[test]
fn test_lesf2_less() {
let rt = make_rt_x86_64();
assert_eq!(rt.lesf2(2.0, 3.0), 0);
}
#[test]
fn test_lesf2_equal() {
let rt = make_rt_x86_64();
assert_eq!(rt.lesf2(3.0, 3.0), 0);
}
#[test]
fn test_lesf2_greater() {
let rt = make_rt_x86_64();
assert_eq!(rt.lesf2(3.0, 2.0), 1);
}
#[test]
fn test_lesf2_nan() {
let rt = make_rt_x86_64();
assert_eq!(rt.lesf2(f32::NAN, 2.0), 1);
}
#[test]
fn test_ledf2_less() {
let rt = make_rt_x86_64();
assert_eq!(rt.ledf2(2.0, 3.0), 0);
}
#[test]
fn test_ledf2_nan() {
let rt = make_rt_x86_64();
assert_eq!(rt.ledf2(f64::NAN, 2.0), 1);
}
#[test]
fn test_extendsfdf2_basic() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(rt.extendsfdf2(1.0f32), 1.0, 1e-12));
}
#[test]
fn test_extendsfdf2_negative() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(rt.extendsfdf2(-3.14f32), -3.14, 1e-4));
}
#[test]
fn test_extendsfdf2_nan() {
let rt = make_rt_x86_64();
assert!(rt.extendsfdf2(f32::NAN).is_nan());
}
#[test]
fn test_extendsfdf2_infinity() {
let rt = make_rt_x86_64();
assert_eq!(rt.extendsfdf2(f32::INFINITY), f64::INFINITY);
}
#[test]
fn test_truncdfsf2_basic() {
let rt = make_rt_x86_64();
assert!(approx_eq_f32(rt.truncdfsf2(1.0), 1.0, 1e-6));
}
#[test]
fn test_truncdfsf2_nan() {
let rt = make_rt_x86_64();
assert!(rt.truncdfsf2(f64::NAN).is_nan());
}
#[test]
fn test_truncdfsf2_infinity() {
let rt = make_rt_x86_64();
assert_eq!(rt.truncdfsf2(f64::INFINITY), f32::INFINITY);
}
#[test]
fn test_truncdfsf2_overflow() {
let rt = make_rt_x86_64();
assert_eq!(rt.truncdfsf2(1e300), f32::INFINITY);
}
#[test]
fn test_fixsfsi_positive() {
let rt = make_rt_x86_64();
assert_eq!(rt.fixsfsi(3.7f32), 3);
}
#[test]
fn test_fixsfsi_negative() {
let rt = make_rt_x86_64();
assert_eq!(rt.fixsfsi(-3.7f32), -3);
}
#[test]
fn test_fixsfsi_nan() {
let rt = make_rt_x86_64();
assert_eq!(rt.fixsfsi(f32::NAN), 0);
}
#[test]
fn test_fixsfsi_max() {
let rt = make_rt_x86_64();
assert!(rt.fixsfsi(f32::MAX) != 0);
}
#[test]
fn test_fixdfsi_basic() {
let rt = make_rt_x86_64();
assert_eq!(rt.fixdfsi(42.9), 42);
}
#[test]
fn test_fixdfsi_nan() {
let rt = make_rt_x86_64();
assert_eq!(rt.fixdfsi(f64::NAN), 0);
}
#[test]
fn test_fixsfdi_basic() {
let rt = make_rt_x86_64();
assert_eq!(rt.fixsfdi(5.0f32), 5i64);
}
#[test]
fn test_fixsfdi_negative() {
let rt = make_rt_x86_64();
assert_eq!(rt.fixsfdi(-10.0f32), -10i64);
}
#[test]
fn test_fixsfdi_nan() {
let rt = make_rt_x86_64();
assert_eq!(rt.fixsfdi(f32::NAN), 0i64);
}
#[test]
fn test_fixdfdi_basic() {
let rt = make_rt_x86_64();
assert_eq!(rt.fixdfdi(100.0), 100i64);
}
#[test]
fn test_fixdfdi_nan() {
let rt = make_rt_x86_64();
assert_eq!(rt.fixdfdi(f64::NAN), 0i64);
}
#[test]
fn test_floatsisf_basic() {
let rt = make_rt_x86_64();
assert!(approx_eq_f32(rt.floatsisf(42), 42.0, 1e-6));
}
#[test]
fn test_floatsisf_zero() {
let rt = make_rt_x86_64();
assert_eq!(rt.floatsisf(0), 0.0);
}
#[test]
fn test_floatsisf_negative() {
let rt = make_rt_x86_64();
assert!(approx_eq_f32(rt.floatsisf(-42), -42.0, 1e-6));
}
#[test]
fn test_floatsidf_basic() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(rt.floatsidf(42), 42.0, 1e-12));
}
#[test]
fn test_floatdisf_basic() {
let rt = make_rt_x86_64();
assert!(approx_eq_f32(
rt.floatdisf(1i64 << 30),
(1i64 << 30) as f32,
1.0
));
}
#[test]
fn test_floatdidf_basic() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(
rt.floatdidf(1i64 << 40),
(1i64 << 40) as f64,
1.0
));
}
#[test]
fn test_fixunssfsi_positive() {
let rt = make_rt_x86_64();
assert_eq!(rt.fixunssfsi(3.7f32), 3u32);
}
#[test]
fn test_fixunssfsi_negative() {
let rt = make_rt_x86_64();
assert_eq!(rt.fixunssfsi(-3.0f32), 0u32);
}
#[test]
fn test_fixunssfsi_nan() {
let rt = make_rt_x86_64();
assert_eq!(rt.fixunssfsi(f32::NAN), 0u32);
}
#[test]
fn test_fixunsdfsi_basic() {
let rt = make_rt_x86_64();
assert_eq!(rt.fixunsdfsi(42.9), 42u32);
}
#[test]
fn test_fixunsdfsi_negative() {
let rt = make_rt_x86_64();
assert_eq!(rt.fixunsdfsi(-3.0), 0u32);
}
#[test]
fn test_fixunssfdi_basic() {
let rt = make_rt_x86_64();
assert_eq!(rt.fixunssfdi(5.0f32), 5u64);
}
#[test]
fn test_fixunssfdi_nan() {
let rt = make_rt_x86_64();
assert_eq!(rt.fixunssfdi(f32::NAN), 0u64);
}
#[test]
fn test_fixunsdfdi_basic() {
let rt = make_rt_x86_64();
assert_eq!(rt.fixunsdfdi(100.0), 100u64);
}
#[test]
fn test_fixunsdfdi_nan() {
let rt = make_rt_x86_64();
assert_eq!(rt.fixunsdfdi(f64::NAN), 0u64);
}
#[test]
fn test_floatunsisf_basic() {
let rt = make_rt_x86_64();
assert!(approx_eq_f32(rt.floatunsisf(42u32), 42.0, 1e-6));
}
#[test]
fn test_floatunsisf_zero() {
let rt = make_rt_x86_64();
assert_eq!(rt.floatunsisf(0u32), 0.0);
}
#[test]
fn test_floatunsidf_basic() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(rt.floatunsidf(42u32), 42.0, 1e-12));
}
#[test]
fn test_floatundisf_basic() {
let rt = make_rt_x86_64();
assert!(rt.floatundisf(u64::MAX) > 0.0);
}
#[test]
fn test_floatundidf_basic() {
let rt = make_rt_x86_64();
assert!(rt.floatundidf(u64::MAX) > 0.0);
}
#[test]
fn test_powisf2_zero_exp() {
let rt = make_rt_x86_64();
assert_eq!(rt.powisf2(5.0, 0), 1.0);
}
#[test]
fn test_powisf2_positive_exp() {
let rt = make_rt_x86_64();
assert!(approx_eq_f32(rt.powisf2(2.0, 3), 8.0, 1e-6));
}
#[test]
fn test_powisf2_negative_exp() {
let rt = make_rt_x86_64();
assert!(approx_eq_f32(rt.powisf2(2.0, -1), 0.5, 1e-6));
}
#[test]
fn test_powisf2_base_zero() {
let rt = make_rt_x86_64();
assert_eq!(rt.powisf2(0.0, 5), 0.0);
}
#[test]
fn test_powisf2_base_zero_neg_exp() {
let rt = make_rt_x86_64();
assert_eq!(rt.powisf2(0.0, -1), f32::INFINITY);
}
#[test]
fn test_powidf2_zero_exp() {
let rt = make_rt_x86_64();
assert_eq!(rt.powidf2(5.0, 0), 1.0);
}
#[test]
fn test_powidf2_positive_exp() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(rt.powidf2(2.0, 10), 1024.0, 1e-10));
}
#[test]
fn test_powidf2_negative_exp() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(rt.powidf2(2.0, -1), 0.5, 1e-12));
}
#[test]
fn test_powidf2_base_zero_neg_exp() {
let rt = make_rt_x86_64();
assert_eq!(rt.powidf2(0.0, -1), f64::INFINITY);
}
#[test]
fn test_addtf3_basic() {
let rt = make_rt_x86_64();
let (hi, lo) = rt.addtf3(1.0, 0.0, 2.0, 0.0);
assert!(approx_eq_f64(hi, 3.0, 1e-12));
}
#[test]
fn test_addtf3_negative() {
let rt = make_rt_x86_64();
let (hi, lo) = rt.addtf3(-1.0, 0.0, -2.0, 0.0);
assert!(approx_eq_f64(hi, -3.0, 1e-12));
}
#[test]
fn test_subtf3_basic() {
let rt = make_rt_x86_64();
let (hi, _lo) = rt.subtf3(5.0, 0.0, 2.0, 0.0);
assert!(approx_eq_f64(hi, 3.0, 1e-12));
}
#[test]
fn test_multf3_basic() {
let rt = make_rt_x86_64();
let (hi, _lo) = rt.multf3(3.0, 0.0, 4.0, 0.0);
assert!(approx_eq_f64(hi, 12.0, 1e-12));
}
#[test]
fn test_divtf3_basic() {
let rt = make_rt_x86_64();
let (hi, _lo) = rt.divtf3(10.0, 0.0, 2.0, 0.0);
assert!(approx_eq_f64(hi, 5.0, 1e-12));
}
#[test]
fn test_negtf2_basic() {
let rt = make_rt_x86_64();
let (hi, lo) = rt.negtf2(3.0, 1e-16);
assert!(approx_eq_f64(hi, -3.0, 1e-12));
assert!(approx_eq_f64(lo, -1e-16, 1e-20));
}
}
mod integer_library {
use super::*;
#[test]
fn test_divdi3_basic() {
let rt = make_rt_x86_64();
assert_eq!(rt.divdi3(10, 3), 3);
}
#[test]
fn test_divdi3_negative() {
let rt = make_rt_x86_64();
assert_eq!(rt.divdi3(-10, 3), -3);
}
#[test]
fn test_divdi3_both_negative() {
let rt = make_rt_x86_64();
assert_eq!(rt.divdi3(-10, -3), 3);
}
#[test]
fn test_divdi3_by_zero() {
let rt = make_rt_x86_64();
assert_eq!(rt.divdi3(10, 0), i64::MIN);
}
#[test]
fn test_divdi3_min_by_neg_one() {
let rt = make_rt_x86_64();
assert_eq!(rt.divdi3(i64::MIN, -1), i64::MIN);
}
#[test]
fn test_divdi3_exact() {
let rt = make_rt_x86_64();
assert_eq!(rt.divdi3(100, 25), 4);
}
#[test]
fn test_moddi3_basic() {
let rt = make_rt_x86_64();
assert_eq!(rt.moddi3(10, 3), 1);
}
#[test]
fn test_moddi3_negative() {
let rt = make_rt_x86_64();
assert_eq!(rt.moddi3(-10, 3), -1);
}
#[test]
fn test_moddi3_by_zero() {
let rt = make_rt_x86_64();
assert_eq!(rt.moddi3(10, 0), 0);
}
#[test]
fn test_divmoddi4_basic() {
let rt = make_rt_x86_64();
let (q, r) = rt.divmoddi4(10, 3);
assert_eq!(q, 3);
assert_eq!(r, 1);
}
#[test]
fn test_divmoddi4_negative() {
let rt = make_rt_x86_64();
let (q, r) = rt.divmoddi4(-10, 3);
assert_eq!(q, -3);
assert_eq!(r, -1);
}
#[test]
fn test_udivdi3_basic() {
let rt = make_rt_x86_64();
assert_eq!(rt.udivdi3(10, 3), 3);
}
#[test]
fn test_udivdi3_by_zero() {
let rt = make_rt_x86_64();
assert_eq!(rt.udivdi3(10, 0), u64::MAX);
}
#[test]
fn test_udivdi3_large() {
let rt = make_rt_x86_64();
assert_eq!(rt.udivdi3(u64::MAX, 2), u64::MAX / 2);
}
#[test]
fn test_umoddi3_basic() {
let rt = make_rt_x86_64();
assert_eq!(rt.umoddi3(10, 3), 1);
}
#[test]
fn test_umoddi3_by_zero() {
let rt = make_rt_x86_64();
assert_eq!(rt.umoddi3(10, 0), 10);
}
#[test]
fn test_udivmoddi4_basic() {
let rt = make_rt_x86_64();
let (q, r) = rt.udivmoddi4(10, 3);
assert_eq!(q, 3);
assert_eq!(r, 1);
}
#[test]
fn test_divti3_basic() {
let rt = make_rt_x86_64();
let (q_hi, q_lo) = rt.divti3(0, 10, 0, 3);
assert_eq!(q_hi, 0);
assert_eq!(q_lo, 3);
}
#[test]
fn test_divti3_negative_dividend() {
let rt = make_rt_x86_64();
let (q_hi, q_lo) = rt.divti3(-1, (-10i64) as u64, 0, 3);
assert_eq!(q_hi, -1);
assert_eq!(q_lo, -3u64 as u64);
}
#[test]
fn test_divti3_by_zero() {
let rt = make_rt_x86_64();
let (q_hi, q_lo) = rt.divti3(0, 10, 0, 0);
assert_eq!(q_hi, 0);
assert_eq!(q_lo, 0);
}
#[test]
fn test_modti3_basic() {
let rt = make_rt_x86_64();
let (r_hi, r_lo) = rt.modti3(0, 10, 0, 3);
assert_eq!(r_hi, 0);
assert_eq!(r_lo, 1);
}
#[test]
fn test_udivti3_basic() {
let rt = make_rt_x86_64();
let (q_hi, q_lo) = rt.udivti3(0, 10, 0, 3);
assert_eq!(q_hi, 0);
assert_eq!(q_lo, 3);
}
#[test]
fn test_udivti3_by_zero() {
let rt = make_rt_x86_64();
let (q_hi, q_lo) = rt.udivti3(0, 10, 0, 0);
assert_eq!(q_hi, u64::MAX);
assert_eq!(q_lo, u64::MAX);
}
#[test]
fn test_umodti3_basic() {
let rt = make_rt_x86_64();
let (r_hi, r_lo) = rt.umodti3(0, 10, 0, 3);
assert_eq!(r_hi, 0);
assert_eq!(r_lo, 1);
}
#[test]
fn test_muldi3_basic() {
let rt = make_rt_x86_64();
assert_eq!(rt.muldi3(3, 4), 12);
}
#[test]
fn test_muldi3_negative() {
let rt = make_rt_x86_64();
assert_eq!(rt.muldi3(-3, 4), -12);
}
#[test]
fn test_muldi3_large() {
let rt = make_rt_x86_64();
assert_eq!(rt.muldi3(1i64 << 31, 2), 1i64 << 32);
}
#[test]
fn test_multi3_basic() {
let rt = make_rt_x86_64();
let (p_hi, p_lo) = rt.multi3(0, 3, 0, 4);
assert_eq!(p_hi, 0);
assert_eq!(p_lo, 12);
}
#[test]
fn test_multi3_negative() {
let rt = make_rt_x86_64();
let (p_hi, p_lo) = rt.multi3(-1, (-3u64) as u64, 0, 4);
assert_eq!(p_hi, -1);
assert_eq!(p_lo, -12u64 as u64);
}
#[test]
fn test_ashldi3_basic() {
let rt = make_rt_x86_64();
assert_eq!(rt.ashldi3(1, 3), 8);
}
#[test]
fn test_ashldi3_overshift() {
let rt = make_rt_x86_64();
assert_eq!(rt.ashldi3(1, 64), 0);
}
#[test]
fn test_ashldi3_negative() {
let rt = make_rt_x86_64();
assert_eq!(rt.ashldi3(1, -1), 1);
}
#[test]
fn test_ashldi3_zero() {
let rt = make_rt_x86_64();
assert_eq!(rt.ashldi3(1, 0), 1);
}
#[test]
fn test_ashrdi3_positive() {
let rt = make_rt_x86_64();
assert_eq!(rt.ashrdi3(8, 3), 1);
}
#[test]
fn test_ashrdi3_negative() {
let rt = make_rt_x86_64();
assert_eq!(rt.ashrdi3(-8, 3), -1);
}
#[test]
fn test_ashrdi3_overshift() {
let rt = make_rt_x86_64();
assert_eq!(rt.ashrdi3(-1, 100), -1);
}
#[test]
fn test_lshrdi3_basic() {
let rt = make_rt_x86_64();
assert_eq!(rt.lshrdi3(8, 3), 1);
}
#[test]
fn test_lshrdi3_overshift() {
let rt = make_rt_x86_64();
assert_eq!(rt.lshrdi3(u64::MAX, 100), 0);
}
#[test]
fn test_ashlti3_basic() {
let rt = make_rt_x86_64();
let (hi, lo) = rt.ashlti3(0, 1, 3);
assert_eq!(hi, 0);
assert_eq!(lo, 8);
}
#[test]
fn test_ashlti3_cross_boundary() {
let rt = make_rt_x86_64();
let (hi, lo) = rt.ashlti3(0, 1, 65);
assert_eq!(hi, 2);
assert_eq!(lo, 0);
}
#[test]
fn test_ashlti3_overshift() {
let rt = make_rt_x86_64();
let (hi, lo) = rt.ashlti3(0, 1, 200);
assert_eq!(hi, 0);
assert_eq!(lo, 0);
}
#[test]
fn test_ashrti3_positive() {
let rt = make_rt_x86_64();
let (hi, lo) = rt.ashrti3(0, 8, 3);
assert_eq!(hi, 0);
assert_eq!(lo, 1);
}
#[test]
fn test_ashrti3_negative() {
let rt = make_rt_x86_64();
let (hi, lo) = rt.ashrti3(u64::MAX, u64::MAX, 3);
assert_eq!(hi, u64::MAX);
assert_eq!(lo, u64::MAX);
}
#[test]
fn test_lshrti3_basic() {
let rt = make_rt_x86_64();
let (hi, lo) = rt.lshrti3(1, 0, 1);
assert_eq!(hi, 0);
assert_eq!(lo, 1u64 << 63);
}
#[test]
fn test_lshrti3_overshift() {
let rt = make_rt_x86_64();
let (hi, lo) = rt.lshrti3(u64::MAX, u64::MAX, 200);
assert_eq!(hi, 0);
assert_eq!(lo, 0);
}
#[test]
fn test_clzsi2_basic() {
let rt = make_rt_x86_64();
assert_eq!(rt.clzsi2(1), 31);
}
#[test]
fn test_clzsi2_zero() {
let rt = make_rt_x86_64();
assert_eq!(rt.clzsi2(0), 32);
}
#[test]
fn test_clzsi2_high_bit() {
let rt = make_rt_x86_64();
assert_eq!(rt.clzsi2(0x8000_0000), 0);
}
#[test]
fn test_clzdi2_basic() {
let rt = make_rt_x86_64();
assert_eq!(rt.clzdi2(1), 63);
}
#[test]
fn test_clzdi2_zero() {
let rt = make_rt_x86_64();
assert_eq!(rt.clzdi2(0), 64);
}
#[test]
fn test_clzdi2_high_bit() {
let rt = make_rt_x86_64();
assert_eq!(rt.clzdi2(0x8000_0000_0000_0000), 0);
}
#[test]
fn test_clzti2_hi_nonzero() {
let rt = make_rt_x86_64();
assert_eq!(rt.clzti2(1, 0), 63);
}
#[test]
fn test_clzti2_lo_nonzero() {
let rt = make_rt_x86_64();
assert_eq!(rt.clzti2(0, 1), 127);
}
#[test]
fn test_clzti2_zero() {
let rt = make_rt_x86_64();
assert_eq!(rt.clzti2(0, 0), 128);
}
#[test]
fn test_ctzsi2_basic() {
let rt = make_rt_x86_64();
assert_eq!(rt.ctzsi2(8), 3);
}
#[test]
fn test_ctzsi2_zero() {
let rt = make_rt_x86_64();
assert_eq!(rt.ctzsi2(0), 32);
}
#[test]
fn test_ctzsi2_odd() {
let rt = make_rt_x86_64();
assert_eq!(rt.ctzsi2(1), 0);
}
#[test]
fn test_ctzdi2_basic() {
let rt = make_rt_x86_64();
assert_eq!(rt.ctzdi2(8), 3);
}
#[test]
fn test_ctzdi2_zero() {
let rt = make_rt_x86_64();
assert_eq!(rt.ctzdi2(0), 64);
}
#[test]
fn test_ctzti2_lo_nonzero() {
let rt = make_rt_x86_64();
assert_eq!(rt.ctzti2(0, 8), 3);
}
#[test]
fn test_ctzti2_hi_nonzero() {
let rt = make_rt_x86_64();
assert_eq!(rt.ctzti2(1, 0), 64);
}
#[test]
fn test_ctzti2_zero() {
let rt = make_rt_x86_64();
assert_eq!(rt.ctzti2(0, 0), 128);
}
#[test]
fn test_popcountsi2_basic() {
let rt = make_rt_x86_64();
assert_eq!(rt.popcountsi2(0b1011), 3);
}
#[test]
fn test_popcountsi2_zero() {
let rt = make_rt_x86_64();
assert_eq!(rt.popcountsi2(0), 0);
}
#[test]
fn test_popcountsi2_all_ones() {
let rt = make_rt_x86_64();
assert_eq!(rt.popcountsi2(u32::MAX), 32);
}
#[test]
fn test_popcountdi2_basic() {
let rt = make_rt_x86_64();
assert_eq!(rt.popcountdi2(0b1011), 3);
}
#[test]
fn test_popcountdi2_all_ones() {
let rt = make_rt_x86_64();
assert_eq!(rt.popcountdi2(u64::MAX), 64);
}
#[test]
fn test_popcountti2_basic() {
let rt = make_rt_x86_64();
assert_eq!(rt.popcountti2(0, 0b1011), 3);
}
#[test]
fn test_popcountti2_split() {
let rt = make_rt_x86_64();
assert_eq!(rt.popcountti2(1, 2), 2); }
#[test]
fn test_paritysi2_even() {
let rt = make_rt_x86_64();
assert_eq!(rt.paritysi2(0b11), 0);
}
#[test]
fn test_paritysi2_odd() {
let rt = make_rt_x86_64();
assert_eq!(rt.paritysi2(0b1), 1);
}
#[test]
fn test_paritydi2_odd() {
let rt = make_rt_x86_64();
assert_eq!(rt.paritydi2(0b1), 1);
}
#[test]
fn test_paritydi2_even() {
let rt = make_rt_x86_64();
assert_eq!(rt.paritydi2(0), 0);
}
#[test]
fn test_parityti2_odd() {
let rt = make_rt_x86_64();
assert_eq!(rt.parityti2(0, 1), 1);
}
#[test]
fn test_bswapsi2_basic() {
let rt = make_rt_x86_64();
assert_eq!(rt.bswapsi2(0x12345678), 0x78563412);
}
#[test]
fn test_bswapsi2_zero() {
let rt = make_rt_x86_64();
assert_eq!(rt.bswapsi2(0), 0);
}
#[test]
fn test_bswapsi2_identity() {
let rt = make_rt_x86_64();
let x = 0xAABBCCDDu32;
assert_eq!(rt.bswapsi2(rt.bswapsi2(x)), x);
}
#[test]
fn test_bswapdi2_basic() {
let rt = make_rt_x86_64();
assert_eq!(rt.bswapdi2(0x0123456789ABCDEF), 0xEFCDAB8967452301);
}
#[test]
fn test_bswapdi2_identity() {
let rt = make_rt_x86_64();
let x = 0x0123456789ABCDEFu64;
assert_eq!(rt.bswapdi2(rt.bswapdi2(x)), x);
}
}
mod atomic_support {
use super::*;
#[test]
fn test_atomic_load_4() {
let rt = make_rt_x86_64();
let val: u32 = 42;
assert_eq!(rt.atomic_load_4(&val as *const u32 as *const u8), 42);
}
#[test]
fn test_atomic_load_8() {
let rt = make_rt_x86_64();
let val: u64 = 0xDEAD_BEEF_CAFE_BABE;
assert_eq!(rt.atomic_load_8(&val as *const u64 as *const u8), val);
}
#[test]
fn test_atomic_store_4() {
let rt = make_rt_x86_64();
let mut val: u32 = 0;
rt.atomic_store_4(&mut val as *mut u32 as *mut u8, 42);
assert_eq!(val, 42);
}
#[test]
fn test_atomic_store_8() {
let rt = make_rt_x86_64();
let mut val: u64 = 0;
rt.atomic_store_8(&mut val as *mut u64 as *mut u8, 0xCAFE);
assert_eq!(val, 0xCAFE);
}
#[test]
fn test_atomic_exchange_4() {
let rt = make_rt_x86_64();
let mut val: u32 = 10;
let old = rt.atomic_exchange_4(&mut val, 20);
assert_eq!(old, 10);
assert_eq!(val, 20);
}
#[test]
fn test_atomic_exchange_8() {
let rt = make_rt_x86_64();
let mut val: u64 = 100;
let old = rt.atomic_exchange_8(&mut val, 200);
assert_eq!(old, 100);
assert_eq!(val, 200);
}
#[test]
fn test_atomic_compare_exchange_4_success() {
let rt = make_rt_x86_64();
let mut val: u32 = 42;
let mut expected: u32 = 42;
let success = rt.atomic_compare_exchange_4(&mut val, &mut expected, 99, false);
assert!(success);
assert_eq!(val, 99);
}
#[test]
fn test_atomic_compare_exchange_4_failure() {
let rt = make_rt_x86_64();
let mut val: u32 = 42;
let mut expected: u32 = 100;
let success = rt.atomic_compare_exchange_4(&mut val, &mut expected, 99, false);
assert!(!success);
assert_eq!(expected, 42);
assert_eq!(val, 42);
}
#[test]
fn test_atomic_compare_exchange_8_success() {
let rt = make_rt_x86_64();
let mut val: u64 = 0xABCD;
let mut expected: u64 = 0xABCD;
let success = rt.atomic_compare_exchange_8(&mut val, &mut expected, 0xDEAD, false);
assert!(success);
assert_eq!(val, 0xDEAD);
}
#[test]
fn test_atomic_compare_exchange_8_failure() {
let rt = make_rt_x86_64();
let mut val: u64 = 0xABCD;
let mut expected: u64 = 0xBEEF;
let success = rt.atomic_compare_exchange_8(&mut val, &mut expected, 0xDEAD, false);
assert!(!success);
assert_eq!(expected, 0xABCD);
}
#[test]
fn test_atomic_fetch_add_4() {
let rt = make_rt_x86_64();
let mut val: u32 = 10;
let old = rt.atomic_fetch_add_4(&mut val, 5);
assert_eq!(old, 10);
assert_eq!(val, 15);
}
#[test]
fn test_atomic_fetch_add_8() {
let rt = make_rt_x86_64();
let mut val: u64 = 100;
let old = rt.atomic_fetch_add_8(&mut val, 50);
assert_eq!(old, 100);
assert_eq!(val, 150);
}
#[test]
fn test_atomic_fetch_sub_4() {
let rt = make_rt_x86_64();
let mut val: u32 = 10;
let old = rt.atomic_fetch_sub_4(&mut val, 3);
assert_eq!(old, 10);
assert_eq!(val, 7);
}
#[test]
fn test_atomic_fetch_sub_8() {
let rt = make_rt_x86_64();
let mut val: u64 = 100;
let old = rt.atomic_fetch_sub_8(&mut val, 30);
assert_eq!(old, 100);
assert_eq!(val, 70);
}
#[test]
fn test_atomic_fetch_and_4() {
let rt = make_rt_x86_64();
let mut val: u32 = 0xFF;
let old = rt.atomic_fetch_and_4(&mut val, 0x0F);
assert_eq!(old, 0xFF);
assert_eq!(val, 0x0F);
}
#[test]
fn test_atomic_fetch_and_8() {
let rt = make_rt_x86_64();
let mut val: u64 = 0xFFFF;
let old = rt.atomic_fetch_and_8(&mut val, 0x00FF);
assert_eq!(old, 0xFFFF);
assert_eq!(val, 0x00FF);
}
#[test]
fn test_atomic_fetch_or_4() {
let rt = make_rt_x86_64();
let mut val: u32 = 0x0F;
let old = rt.atomic_fetch_or_4(&mut val, 0xF0);
assert_eq!(old, 0x0F);
assert_eq!(val, 0xFF);
}
#[test]
fn test_atomic_fetch_or_8() {
let rt = make_rt_x86_64();
let mut val: u64 = 0x00FF;
let old = rt.atomic_fetch_or_8(&mut val, 0xFF00);
assert_eq!(old, 0x00FF);
assert_eq!(val, 0xFFFF);
}
#[test]
fn test_atomic_fetch_xor_4() {
let rt = make_rt_x86_64();
let mut val: u32 = 0xFF;
let old = rt.atomic_fetch_xor_4(&mut val, 0x0F);
assert_eq!(old, 0xFF);
assert_eq!(val, 0xF0);
}
#[test]
fn test_atomic_fetch_xor_8() {
let rt = make_rt_x86_64();
let mut val: u64 = 0xFFFF;
let old = rt.atomic_fetch_xor_8(&mut val, 0x00FF);
assert_eq!(old, 0xFFFF);
assert_eq!(val, 0xFF00);
}
#[test]
fn test_atomic_fetch_nand_4() {
let rt = make_rt_x86_64();
let mut val: u32 = 0xFF;
let old = rt.atomic_fetch_nand_4(&mut val, 0x0F);
assert_eq!(old, 0xFF);
assert_eq!(val, !(0xFF & 0x0F));
}
#[test]
fn test_atomic_fetch_nand_8() {
let rt = make_rt_x86_64();
let mut val: u64 = 0xFFFF;
let old = rt.atomic_fetch_nand_8(&mut val, 0x00FF);
assert_eq!(old, 0xFFFF);
assert_eq!(val, !(0xFFFF & 0x00FF));
}
#[test]
fn test_atomic_test_and_set() {
let rt = make_rt_x86_64();
let mut byte: u8 = 0;
let old = rt.atomic_test_and_set(&mut byte);
assert_eq!(old, 0);
assert_eq!(byte, 1);
}
#[test]
fn test_atomic_test_and_set_already_set() {
let rt = make_rt_x86_64();
let mut byte: u8 = 1;
let old = rt.atomic_test_and_set(&mut byte);
assert_eq!(old, 1);
assert_eq!(byte, 1);
}
#[test]
fn test_atomic_clear() {
let rt = make_rt_x86_64();
let mut byte: u8 = 1;
rt.atomic_clear(&mut byte);
assert_eq!(byte, 0);
}
#[test]
fn test_atomic_clear_already_clear() {
let rt = make_rt_x86_64();
let mut byte: u8 = 0;
rt.atomic_clear(&mut byte);
assert_eq!(byte, 0);
}
#[test]
fn test_sync_fetch_and_add_4() {
let rt = make_rt_x86_64();
let mut val: u32 = 10;
let old = rt.sync_fetch_and_add_4(&mut val, 5);
assert_eq!(old, 10);
assert_eq!(val, 15);
}
#[test]
fn test_sync_val_compare_and_swap_4_success() {
let rt = make_rt_x86_64();
let mut val: u32 = 42;
let old = rt.sync_val_compare_and_swap_4(&mut val, 42, 99);
assert_eq!(old, 42);
assert_eq!(val, 99);
}
#[test]
fn test_sync_val_compare_and_swap_4_failure() {
let rt = make_rt_x86_64();
let mut val: u32 = 42;
let old = rt.sync_val_compare_and_swap_4(&mut val, 100, 99);
assert_eq!(old, 42);
assert_eq!(val, 42);
}
#[test]
fn test_sync_bool_compare_and_swap_4_success() {
let rt = make_rt_x86_64();
let mut val: u32 = 42;
assert!(rt.sync_bool_compare_and_swap_4(&mut val, 42, 99));
assert_eq!(val, 99);
}
#[test]
fn test_sync_bool_compare_and_swap_4_failure() {
let rt = make_rt_x86_64();
let mut val: u32 = 42;
assert!(!rt.sync_bool_compare_and_swap_4(&mut val, 100, 99));
assert_eq!(val, 42);
}
#[test]
fn test_sync_lock_test_and_set_4() {
let rt = make_rt_x86_64();
let mut val: u32 = 0;
let old = rt.sync_lock_test_and_set_4(&mut val, 1);
assert_eq!(old, 0);
assert_eq!(val, 1);
}
#[test]
fn test_sync_lock_release_4() {
let rt = make_rt_x86_64();
let mut val: u32 = 1;
rt.sync_lock_release_4(&mut val);
assert_eq!(val, 0);
}
#[test]
fn test_sync_synchronize() {
let rt = make_rt_x86_64();
rt.sync_synchronize();
}
#[test]
fn test_atomic_load_store_roundtrip() {
let rt = make_rt_x86_64();
let mut val: u32 = 0;
rt.atomic_store_4(&mut val as *mut u32 as *mut u8, 12345);
let loaded = rt.atomic_load_4(&val as *const u32 as *const u8);
assert_eq!(loaded, 12345);
}
}
mod checked_arithmetic {
use super::*;
#[test]
fn test_addvsi3_no_overflow() {
let rt = make_rt_x86_64();
assert_eq!(rt.addvsi3(10, 20), 30);
}
#[test]
fn test_addvsi3_negative() {
let rt = make_rt_x86_64();
assert_eq!(rt.addvsi3(-10, -20), -30);
}
#[test]
#[should_panic]
fn test_addvsi3_overflow_positive() {
let rt = make_rt_x86_64();
rt.addvsi3(i32::MAX, 1);
}
#[test]
#[should_panic]
fn test_addvsi3_overflow_negative() {
let rt = make_rt_x86_64();
rt.addvsi3(i32::MIN, -1);
}
#[test]
fn test_addvdi3_no_overflow() {
let rt = make_rt_x86_64();
assert_eq!(rt.addvdi3(100, 200), 300);
}
#[test]
#[should_panic]
fn test_addvdi3_overflow() {
let rt = make_rt_x86_64();
rt.addvdi3(i64::MAX, 1);
}
#[test]
fn test_subvsi3_no_overflow() {
let rt = make_rt_x86_64();
assert_eq!(rt.subvsi3(30, 10), 20);
}
#[test]
#[should_panic]
fn test_subvsi3_overflow() {
let rt = make_rt_x86_64();
rt.subvsi3(i32::MIN, 1);
}
#[test]
fn test_subvdi3_no_overflow() {
let rt = make_rt_x86_64();
assert_eq!(rt.subvdi3(300, 100), 200);
}
#[test]
#[should_panic]
fn test_subvdi3_overflow() {
let rt = make_rt_x86_64();
rt.subvdi3(i64::MIN, 1);
}
#[test]
fn test_mulvsi3_no_overflow() {
let rt = make_rt_x86_64();
assert_eq!(rt.mulvsi3(3, 4), 12);
}
#[test]
#[should_panic]
fn test_mulvsi3_overflow() {
let rt = make_rt_x86_64();
rt.mulvsi3(i32::MAX, 2);
}
#[test]
fn test_mulvdi3_no_overflow() {
let rt = make_rt_x86_64();
assert_eq!(rt.mulvdi3(3, 4), 12);
}
#[test]
#[should_panic]
fn test_mulvdi3_overflow() {
let rt = make_rt_x86_64();
rt.mulvdi3(i64::MAX, 2);
}
#[test]
fn test_negvsi2_no_overflow() {
let rt = make_rt_x86_64();
assert_eq!(rt.negvsi2(42), -42);
}
#[test]
fn test_negvsi2_zero() {
let rt = make_rt_x86_64();
assert_eq!(rt.negvsi2(0), 0);
}
#[test]
#[should_panic]
fn test_negvsi2_overflow() {
let rt = make_rt_x86_64();
rt.negvsi2(i32::MIN);
}
#[test]
fn test_negvdi2_no_overflow() {
let rt = make_rt_x86_64();
assert_eq!(rt.negvdi2(42), -42);
}
#[test]
#[should_panic]
fn test_negvdi2_overflow() {
let rt = make_rt_x86_64();
rt.negvdi2(i64::MIN);
}
}
mod x87_math {
use super::*;
#[test]
fn test_sin_f64_zero() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(rt.sin_f64(0.0), 0.0, 1e-10));
}
#[test]
fn test_sin_f64_pi_half() {
let rt = make_rt_x86_64();
let pi_half = std::f64::consts::FRAC_PI_2;
assert!(approx_eq_f64(rt.sin_f64(pi_half), 1.0, 1e-6));
}
#[test]
fn test_sin_f64_pi() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(rt.sin_f64(std::f64::consts::PI), 0.0, 1e-6));
}
#[test]
fn test_sin_f64_nan() {
let rt = make_rt_x86_64();
assert!(rt.sin_f64(f64::NAN).is_nan());
}
#[test]
fn test_sin_f64_infinity() {
let rt = make_rt_x86_64();
assert!(rt.sin_f64(f64::INFINITY).is_nan());
}
#[test]
fn test_cos_f64_zero() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(rt.cos_f64(0.0), 1.0, 1e-10));
}
#[test]
fn test_cos_f64_pi() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(rt.cos_f64(std::f64::consts::PI), -1.0, 1e-6));
}
#[test]
fn test_cos_f64_nan() {
let rt = make_rt_x86_64();
assert!(rt.cos_f64(f64::NAN).is_nan());
}
#[test]
fn test_tan_f64_zero() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(rt.tan_f64(0.0), 0.0, 1e-10));
}
#[test]
fn test_tan_f64_pi_quarter() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(
rt.tan_f64(std::f64::consts::FRAC_PI_4),
1.0,
1e-6
));
}
#[test]
fn test_tan_f64_nan() {
let rt = make_rt_x86_64();
assert!(rt.tan_f64(f64::NAN).is_nan());
}
#[test]
fn test_asin_f64_zero() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(rt.asin_f64(0.0), 0.0, 1e-10));
}
#[test]
fn test_asin_f64_one() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(
rt.asin_f64(1.0),
std::f64::consts::FRAC_PI_2,
1e-5
));
}
#[test]
fn test_asin_f64_neg_one() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(
rt.asin_f64(-1.0),
-std::f64::consts::FRAC_PI_2,
1e-5
));
}
#[test]
fn test_asin_f64_out_of_range() {
let rt = make_rt_x86_64();
assert!(rt.asin_f64(2.0).is_nan());
}
#[test]
fn test_acos_f64_zero() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(
rt.acos_f64(0.0),
std::f64::consts::FRAC_PI_2,
1e-5
));
}
#[test]
fn test_acos_f64_one() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(rt.acos_f64(1.0), 0.0, 1e-5));
}
#[test]
fn test_atan_f64_zero() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(rt.atan_f64(0.0), 0.0, 1e-10));
}
#[test]
fn test_atan_f64_one() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(
rt.atan_f64(1.0),
std::f64::consts::FRAC_PI_4,
1e-5
));
}
#[test]
fn test_atan_f64_infinity() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(
rt.atan_f64(f64::INFINITY),
std::f64::consts::FRAC_PI_2,
1e-10
));
}
#[test]
fn test_atan_f64_neg_infinity() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(
rt.atan_f64(f64::NEG_INFINITY),
-std::f64::consts::FRAC_PI_2,
1e-10
));
}
#[test]
fn test_atan2_f64_quadrant1() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(
rt.atan2_f64(1.0, 1.0),
std::f64::consts::FRAC_PI_4,
1e-5
));
}
#[test]
fn test_atan2_f64_quadrant2() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(
rt.atan2_f64(1.0, -1.0),
3.0 * std::f64::consts::FRAC_PI_4,
1e-5
));
}
#[test]
fn test_atan2_f64_zero() {
let rt = make_rt_x86_64();
assert_eq!(rt.atan2_f64(0.0, 0.0), 0.0);
}
#[test]
fn test_sinh_f64_zero() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(rt.sinh_f64(0.0), 0.0, 1e-10));
}
#[test]
fn test_sinh_f64_nan() {
let rt = make_rt_x86_64();
assert!(rt.sinh_f64(f64::NAN).is_nan());
}
#[test]
fn test_cosh_f64_zero() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(rt.cosh_f64(0.0), 1.0, 1e-10));
}
#[test]
fn test_cosh_f64_nan() {
let rt = make_rt_x86_64();
assert!(rt.cosh_f64(f64::NAN).is_nan());
}
#[test]
fn test_tanh_f64_zero() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(rt.tanh_f64(0.0), 0.0, 1e-10));
}
#[test]
fn test_tanh_f64_infinity() {
let rt = make_rt_x86_64();
assert_eq!(rt.tanh_f64(f64::INFINITY), 1.0);
assert_eq!(rt.tanh_f64(f64::NEG_INFINITY), -1.0);
}
#[test]
fn test_exp_f64_zero() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(rt.exp_f64(0.0), 1.0, 1e-12));
}
#[test]
fn test_exp_f64_one() {
let rt = make_rt_x86_64();
let e = std::f64::consts::E;
assert!(approx_eq_f64(rt.exp_f64(1.0), e, 1e-6));
}
#[test]
fn test_exp_f64_neg_infinity() {
let rt = make_rt_x86_64();
assert_eq!(rt.exp_f64(f64::NEG_INFINITY), 0.0);
}
#[test]
fn test_exp_f64_infinity() {
let rt = make_rt_x86_64();
assert_eq!(rt.exp_f64(f64::INFINITY), f64::INFINITY);
}
#[test]
fn test_exp2_f64_zero() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(rt.exp2_f64(0.0), 1.0, 1e-12));
}
#[test]
fn test_exp2_f64_three() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(rt.exp2_f64(3.0), 8.0, 1e-10));
}
#[test]
fn test_exp2_f64_negative() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(rt.exp2_f64(-1.0), 0.5, 1e-12));
}
#[test]
fn test_log_f64_one() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(rt.log_f64(1.0), 0.0, 1e-12));
}
#[test]
fn test_log_f64_e() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(rt.log_f64(std::f64::consts::E), 1.0, 1e-6));
}
#[test]
fn test_log_f64_zero() {
let rt = make_rt_x86_64();
assert_eq!(rt.log_f64(0.0), f64::NEG_INFINITY);
}
#[test]
fn test_log_f64_negative() {
let rt = make_rt_x86_64();
assert!(rt.log_f64(-1.0).is_nan());
}
#[test]
fn test_log_f64_nan() {
let rt = make_rt_x86_64();
assert!(rt.log_f64(f64::NAN).is_nan());
}
#[test]
fn test_log2_f64_one() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(rt.log2_f64(1.0), 0.0, 1e-12));
}
#[test]
fn test_log2_f64_eight() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(rt.log2_f64(8.0), 3.0, 1e-10));
}
#[test]
fn test_log10_f64_one() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(rt.log10_f64(1.0), 0.0, 1e-12));
}
#[test]
fn test_log10_f64_hundred() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(rt.log10_f64(100.0), 2.0, 1e-10));
}
#[test]
fn test_pow_f64_square() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(rt.pow_f64(3.0, 2.0), 9.0, 1e-10));
}
#[test]
fn test_pow_f64_zero_to_zero() {
let rt = make_rt_x86_64();
assert_eq!(rt.pow_f64(0.0, 0.0), 1.0);
}
#[test]
fn test_pow_f64_base_zero_neg_exp() {
let rt = make_rt_x86_64();
assert_eq!(rt.pow_f64(0.0, -1.0), f64::INFINITY);
}
#[test]
fn test_sqrt_f64_positive() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(rt.sqrt_f64(4.0), 2.0, 1e-12));
}
#[test]
fn test_sqrt_f64_zero() {
let rt = make_rt_x86_64();
assert_eq!(rt.sqrt_f64(0.0), 0.0);
}
#[test]
fn test_sqrt_f64_negative() {
let rt = make_rt_x86_64();
assert!(rt.sqrt_f64(-1.0).is_nan());
}
#[test]
fn test_fmod_f64_basic() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(rt.fmod_f64(10.0, 3.0), 1.0, 1e-12));
}
#[test]
fn test_fmod_f64_by_zero() {
let rt = make_rt_x86_64();
assert!(rt.fmod_f64(10.0, 0.0).is_nan());
}
#[test]
fn test_remainder_f64_exact() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(rt.remainder_f64(10.0, 3.0), 1.0, 1e-12));
}
#[test]
fn test_remainder_f64_by_zero() {
let rt = make_rt_x86_64();
assert!(rt.remainder_f64(10.0, 0.0).is_nan());
}
#[test]
fn test_ceil_f64_positive() {
let rt = make_rt_x86_64();
assert_eq!(rt.ceil_f64(3.2), 4.0);
}
#[test]
fn test_ceil_f64_negative() {
let rt = make_rt_x86_64();
assert_eq!(rt.ceil_f64(-3.2), -3.0);
}
#[test]
fn test_ceil_f64_integer() {
let rt = make_rt_x86_64();
assert_eq!(rt.ceil_f64(5.0), 5.0);
}
#[test]
fn test_floor_f64_positive() {
let rt = make_rt_x86_64();
assert_eq!(rt.floor_f64(3.8), 3.0);
}
#[test]
fn test_floor_f64_negative() {
let rt = make_rt_x86_64();
assert_eq!(rt.floor_f64(-3.2), -4.0);
}
#[test]
fn test_floor_f64_integer() {
let rt = make_rt_x86_64();
assert_eq!(rt.floor_f64(5.0), 5.0);
}
#[test]
fn test_trunc_f64_positive() {
let rt = make_rt_x86_64();
assert_eq!(rt.trunc_f64(3.9), 3.0);
}
#[test]
fn test_trunc_f64_negative() {
let rt = make_rt_x86_64();
assert_eq!(rt.trunc_f64(-3.9), -3.0);
}
#[test]
fn test_round_f64_up() {
let rt = make_rt_x86_64();
assert_eq!(rt.round_f64(3.6), 4.0);
}
#[test]
fn test_round_f64_down() {
let rt = make_rt_x86_64();
assert_eq!(rt.round_f64(3.4), 3.0);
}
#[test]
fn test_round_f64_half_up() {
let rt = make_rt_x86_64();
assert_eq!(rt.round_f64(3.5), 4.0);
}
#[test]
fn test_rint_f64_integer() {
let rt = make_rt_x86_64();
assert_eq!(rt.rint_f64(5.0), 5.0);
}
#[test]
fn test_rint_f64_half() {
let rt = make_rt_x86_64();
assert_eq!(rt.rint_f64(2.5), 2.0);
}
#[test]
fn test_fabs_f64_positive() {
let rt = make_rt_x86_64();
assert_eq!(rt.fabs_f64(3.0), 3.0);
}
#[test]
fn test_fabs_f64_negative() {
let rt = make_rt_x86_64();
assert_eq!(rt.fabs_f64(-3.0), 3.0);
}
#[test]
fn test_fabs_f64_zero() {
let rt = make_rt_x86_64();
assert_eq!(rt.fabs_f64(-0.0).to_bits(), 0.0f64.to_bits());
}
#[test]
fn test_fmin_f64_basic() {
let rt = make_rt_x86_64();
assert_eq!(rt.fmin_f64(2.0, 5.0), 2.0);
}
#[test]
fn test_fmin_f64_nan() {
let rt = make_rt_x86_64();
assert_eq!(rt.fmin_f64(f64::NAN, 5.0), 5.0);
assert_eq!(rt.fmin_f64(5.0, f64::NAN), 5.0);
}
#[test]
fn test_fmax_f64_basic() {
let rt = make_rt_x86_64();
assert_eq!(rt.fmax_f64(2.0, 5.0), 5.0);
}
#[test]
fn test_fmax_f64_nan() {
let rt = make_rt_x86_64();
assert_eq!(rt.fmax_f64(f64::NAN, 5.0), 5.0);
}
#[test]
fn test_copysign_f64_positive_to_negative() {
let rt = make_rt_x86_64();
assert_eq!(rt.copysign_f64(1.0, -2.0), -1.0);
}
#[test]
fn test_copysign_f64_negative_to_positive() {
let rt = make_rt_x86_64();
assert_eq!(rt.copysign_f64(-1.0, 2.0), 1.0);
}
#[test]
fn test_ldexp_f64_basic() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(rt.ldexp_f64(1.5, 3), 12.0, 1e-12));
}
#[test]
fn test_ldexp_f64_negative_exp() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(rt.ldexp_f64(1.0, -1), 0.5, 1e-12));
}
#[test]
fn test_ldexp_f64_zero() {
let rt = make_rt_x86_64();
assert_eq!(rt.ldexp_f64(0.0, 5), 0.0);
}
#[test]
fn test_frexp_f64_basic() {
let rt = make_rt_x86_64();
let (frac, exp) = rt.frexp_f64(12.0);
assert!(approx_eq_f64(frac, 0.75, 1e-10));
assert_eq!(exp, 4);
}
#[test]
fn test_frexp_f64_one() {
let rt = make_rt_x86_64();
let (frac, exp) = rt.frexp_f64(1.0);
assert!(approx_eq_f64(frac, 0.5, 1e-10));
assert_eq!(exp, 1);
}
#[test]
fn test_frexp_f64_zero() {
let rt = make_rt_x86_64();
let (frac, exp) = rt.frexp_f64(0.0);
assert_eq!(frac, 0.0);
assert_eq!(exp, 0);
}
#[test]
fn test_modf_f64_positive() {
let rt = make_rt_x86_64();
let (frac, int) = rt.modf_f64(3.14);
assert!(approx_eq_f64(int, 3.0, 1e-12));
assert!(approx_eq_f64(frac, 0.14, 1e-2));
}
#[test]
fn test_modf_f64_negative() {
let rt = make_rt_x86_64();
let (frac, int) = rt.modf_f64(-3.14);
assert!(approx_eq_f64(int, -3.0, 1e-12));
assert!(approx_eq_f64(frac, -0.14, 1e-2));
}
#[test]
fn test_scalbn_f64_basic() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(rt.scalbn_f64(1.5, 3), 12.0, 1e-12));
}
}
mod exception_handling {
use super::*;
#[test]
fn test_cxa_allocate_exception() {
let rt = make_rt_x86_64();
let ptr = rt.cxa_allocate_exception(64);
assert!(!ptr.is_null());
}
#[test]
fn test_cxa_allocate_exception_small() {
let rt = make_rt_x86_64();
let ptr = rt.cxa_allocate_exception(1);
assert!(!ptr.is_null());
}
#[test]
fn test_cxa_allocate_exception_large() {
let rt = make_rt_x86_64();
let ptr = rt.cxa_allocate_exception(4096);
assert!(!ptr.is_null());
}
#[test]
fn test_cxa_begin_catch() {
let rt = make_rt_x86_64();
let exc = rt.cxa_allocate_exception(16);
let result = rt.cxa_begin_catch(exc);
assert!(!result.is_null());
}
#[test]
fn test_cxa_end_catch() {
let rt = make_rt_x86_64();
rt.cxa_end_catch();
}
#[test]
fn test_cxa_guard_acquire_first_time() {
let rt = make_rt_x86_64();
let mut guard: u64 = 0;
let result = rt.cxa_guard_acquire(&mut guard);
assert_eq!(result, 1);
}
#[test]
fn test_cxa_guard_acquire_after_release() {
let rt = make_rt_x86_64();
let mut guard: u64 = 0;
rt.cxa_guard_acquire(&mut guard);
rt.cxa_guard_release(&mut guard);
let result = rt.cxa_guard_acquire(&mut guard);
assert_eq!(result, 0);
}
#[test]
fn test_cxa_guard_abort() {
let rt = make_rt_x86_64();
let mut guard: u64 = 0;
rt.cxa_guard_acquire(&mut guard);
rt.cxa_guard_abort(&mut guard);
let guard_byte = unsafe { &*(&guard as *const u64 as *const u8) };
assert_eq!(*guard_byte, 0);
}
#[test]
#[should_panic]
fn test_cxa_pure_virtual() {
let rt = make_rt_x86_64();
rt.cxa_pure_virtual();
}
#[test]
#[should_panic]
fn test_cxa_deleted_virtual() {
let rt = make_rt_x86_64();
rt.cxa_deleted_virtual();
}
#[test]
fn test_unwind_raise_exception() {
let rt = make_rt_x86_64();
let exc = rt.cxa_allocate_exception(16);
let result = rt.unwind_raise_exception(exc);
assert_eq!(result, 5); }
#[test]
fn test_gxx_personality_v0_search_phase() {
let rt = make_rt_x86_64();
let mut ctx: u64 = 0;
let result = rt.gxx_personality_v0(
1,
1,
0,
std::ptr::null_mut(),
&mut ctx as *mut u64 as *mut u8,
);
assert_eq!(result, 8); }
#[test]
fn test_gxx_personality_v0_cleanup_phase() {
let rt = make_rt_x86_64();
let mut ctx: u64 = 0;
let result = rt.gxx_personality_v0(
1,
2,
0,
std::ptr::null_mut(),
&mut ctx as *mut u64 as *mut u8,
);
assert_eq!(result, 8);
}
#[test]
fn test_gxx_personality_v0_bad_version() {
let rt = make_rt_x86_64();
let mut ctx: u64 = 0;
let result = rt.gxx_personality_v0(
0,
0,
0,
std::ptr::null_mut(),
&mut ctx as *mut u64 as *mut u8,
);
assert_eq!(result, 9); }
#[test]
fn test_gcc_personality_v0() {
let rt = make_rt_x86_64();
let mut ctx: u64 = 0;
let result = rt.gcc_personality_v0(
1,
1,
0,
std::ptr::null_mut(),
&mut ctx as *mut u64 as *mut u8,
);
assert_eq!(result, 8);
}
#[test]
fn test_cxa_demangle_simple() {
let rt = make_rt_x86_64();
let name = b"_Z3foov\0";
let mut status: i32 = -1;
let result = rt.cxa_demangle(
name.as_ptr(),
std::ptr::null_mut(),
std::ptr::null_mut(),
&mut status,
);
if !result.is_null() {
assert_eq!(status, 0);
}
}
#[test]
fn test_cxa_demangle_null() {
let rt = make_rt_x86_64();
let mut status: i32 = 0;
let result = rt.cxa_demangle(
std::ptr::null(),
std::ptr::null_mut(),
std::ptr::null_mut(),
&mut status,
);
assert!(result.is_null());
assert_eq!(status, -2);
}
#[test]
fn test_cxa_demangle_empty() {
let rt = make_rt_x86_64();
let name = b"\0";
let mut status: i32 = 0;
let result = rt.cxa_demangle(
name.as_ptr(),
std::ptr::null_mut(),
std::ptr::null_mut(),
&mut status,
);
assert!(result.is_null());
assert_eq!(status, -2);
}
#[test]
fn test_cxa_demangle_not_itanium() {
let rt = make_rt_x86_64();
let name = b"foobar\0";
let mut status: i32 = 0;
let result = rt.cxa_demangle(
name.as_ptr(),
std::ptr::null_mut(),
std::ptr::null_mut(),
&mut status,
);
assert!(result.is_null());
assert_eq!(status, -2);
}
#[test]
fn test_unwind_get_set_gr() {
let rt = make_rt_x86_64();
let mut ctx = [0u64; 32];
rt.unwind_set_gr(ctx.as_mut_ptr() as *mut u8, 0, 42);
let val = rt.unwind_get_gr(ctx.as_ptr() as *const u8, 0);
assert_eq!(val, 42);
}
#[test]
fn test_unwind_get_set_ip() {
let rt = make_rt_x86_64();
let mut ctx = [0u8; 256];
rt.unwind_set_ip(ctx.as_mut_ptr(), 0xDEAD_BEEF);
let val = rt.unwind_get_ip(ctx.as_ptr());
assert_eq!(val, 0xDEAD_BEEF);
}
}
mod security_hardening {
use super::*;
#[test]
fn test_stack_chk_guard() {
let rt = make_rt_x86_64();
let guard = rt.stack_chk_guard();
assert!(guard != 0);
}
#[test]
fn test_memcpy_chk_basic() {
let rt = make_rt_x86_64();
let src = [1u8, 2, 3, 4];
let mut dest = [0u8; 4];
let result = rt.memcpy_chk(dest.as_mut_ptr(), src.as_ptr(), 4, 4);
assert_eq!(result, dest.as_mut_ptr());
assert_eq!(dest, src);
}
#[test]
#[should_panic]
fn test_memcpy_chk_overflow() {
let rt = make_rt_x86_64();
let src = [1u8, 2, 3, 4, 5];
let mut dest = [0u8; 4];
rt.memcpy_chk(dest.as_mut_ptr(), src.as_ptr(), 5, 4);
}
#[test]
fn test_memmove_chk_basic() {
let rt = make_rt_x86_64();
let src = [1u8, 2, 3, 4];
let mut dest = [0u8; 4];
rt.memmove_chk(dest.as_mut_ptr(), src.as_ptr(), 4, 4);
assert_eq!(dest, src);
}
#[test]
#[should_panic]
fn test_memmove_chk_overflow() {
let rt = make_rt_x86_64();
let src = [1u8, 2, 3, 4, 5];
let mut dest = [0u8; 4];
rt.memmove_chk(dest.as_mut_ptr(), src.as_ptr(), 5, 4);
}
#[test]
fn test_memset_chk_basic() {
let rt = make_rt_x86_64();
let mut dest = [0u8; 4];
rt.memset_chk(dest.as_mut_ptr(), 0xFF, 4, 4);
assert_eq!(dest, [0xFFu8; 4]);
}
#[test]
#[should_panic]
fn test_memset_chk_overflow() {
let rt = make_rt_x86_64();
let mut dest = [0u8; 4];
rt.memset_chk(dest.as_mut_ptr(), 0, 5, 4);
}
#[test]
fn test_strcpy_chk_basic() {
let rt = make_rt_x86_64();
let src = b"hello\0";
let mut dest = [0u8; 16];
let result = rt.strcpy_chk(dest.as_mut_ptr(), src.as_ptr(), 16);
assert_eq!(result, dest.as_mut_ptr());
assert_eq!(&dest[..6], b"hello\0");
}
#[test]
#[should_panic]
fn test_strcpy_chk_overflow() {
let rt = make_rt_x86_64();
let src = b"hello world\0";
let mut dest = [0u8; 5];
rt.strcpy_chk(dest.as_mut_ptr(), src.as_ptr(), 5);
}
#[test]
fn test_strncpy_chk_basic() {
let rt = make_rt_x86_64();
let src = b"hi\0";
let mut dest = [0u8; 10];
rt.strncpy_chk(dest.as_mut_ptr(), src.as_ptr(), 5, 10);
assert_eq!(&dest[..3], b"hi\0");
}
#[test]
#[should_panic]
fn test_strncpy_chk_overflow() {
let rt = make_rt_x86_64();
let src = b"hi\0";
let mut dest = [0u8; 3];
rt.strncpy_chk(dest.as_mut_ptr(), src.as_ptr(), 5, 3);
}
#[test]
fn test_strcat_chk_basic() {
let rt = make_rt_x86_64();
let mut dest = [0u8; 20];
dest[0] = b'h';
dest[1] = b'i';
dest[2] = 0;
let src = b" there\0";
rt.strcat_chk(dest.as_mut_ptr(), src.as_ptr(), 20);
assert_eq!(dest[0], b'h');
assert_eq!(dest[1], b'i');
}
#[test]
#[should_panic]
fn test_strcat_chk_overflow() {
let rt = make_rt_x86_64();
let mut dest = [0u8; 5];
dest[0] = b'h';
dest[1] = b'i';
dest[2] = 0;
let src = b"longstring\0";
rt.strcat_chk(dest.as_mut_ptr(), src.as_ptr(), 5);
}
#[test]
fn test_strncat_chk_basic() {
let rt = make_rt_x86_64();
let mut dest = [0u8; 20];
dest[0] = b'a';
dest[1] = 0;
let src = b"bc\0";
rt.strncat_chk(dest.as_mut_ptr(), src.as_ptr(), 3, 20);
assert_eq!(dest[0], b'a');
assert_eq!(dest[1], b'b');
assert_eq!(dest[2], b'c');
assert_eq!(dest[3], 0);
}
#[test]
#[should_panic]
fn test_strncat_chk_overflow() {
let rt = make_rt_x86_64();
let mut dest = [0u8; 3];
dest[0] = b'a';
dest[1] = 0;
let src = b"bc\0";
rt.strncat_chk(dest.as_mut_ptr(), src.as_ptr(), 5, 3);
}
#[test]
fn test_sprintf_chk_null_dest() {
let rt = make_rt_x86_64();
let fmt = b"hello\0";
let result = rt.sprintf_chk(std::ptr::null_mut(), 0, 10, fmt.as_ptr());
assert_eq!(result, -1);
}
#[test]
fn test_snprintf_chk_basic() {
let rt = make_rt_x86_64();
let mut dest = [0u8; 10];
let fmt = b"test\0";
let result = rt.snprintf_chk(dest.as_mut_ptr(), 5, 0, 10, fmt.as_ptr());
assert_eq!(result, 0);
}
#[test]
#[should_panic]
fn test_snprintf_chk_overflow() {
let rt = make_rt_x86_64();
let mut dest = [0u8; 5];
let fmt = b"test\0";
rt.snprintf_chk(dest.as_mut_ptr(), 10, 0, 5, fmt.as_ptr());
}
#[test]
fn test_vsprintf_chk_null() {
let rt = make_rt_x86_64();
let fmt = b"hello\0";
let result = rt.vsprintf_chk(
std::ptr::null_mut(),
0,
10,
fmt.as_ptr(),
std::ptr::null_mut(),
);
assert_eq!(result, -1);
}
#[test]
fn test_vsnprintf_chk_basic() {
let rt = make_rt_x86_64();
let mut dest = [0u8; 10];
let fmt = b"test\0";
let result = rt.vsnprintf_chk(
dest.as_mut_ptr(),
5,
0,
10,
fmt.as_ptr(),
std::ptr::null_mut(),
);
assert_eq!(result, 0);
}
#[test]
#[should_panic]
fn test_vsnprintf_chk_overflow() {
let rt = make_rt_x86_64();
let mut dest = [0u8; 5];
let fmt = b"test\0";
rt.vsnprintf_chk(
dest.as_mut_ptr(),
10,
0,
5,
fmt.as_ptr(),
std::ptr::null_mut(),
);
}
}
mod struct_tests {
use super::*;
#[test]
fn test_new_x86_64() {
let rt = X86CompilerRT::default_x86_64();
assert!(rt.is_64bit);
assert_eq!(rt.pointer_size(), 8);
}
#[test]
fn test_new_i386() {
let rt = X86CompilerRT::default_i386();
assert!(!rt.is_64bit);
assert_eq!(rt.pointer_size(), 4);
}
#[test]
fn test_has_sse_default() {
let rt = X86CompilerRT::default_x86_64();
assert!(rt.has_sse());
}
#[test]
fn test_has_sse2_default() {
let rt = X86CompilerRT::default_x86_64();
assert!(rt.has_sse2());
}
#[test]
fn test_pointer_size_64() {
let rt = make_rt_x86_64();
assert_eq!(rt.pointer_size(), 8);
}
#[test]
fn test_pointer_size_32() {
let rt = make_rt_i386();
assert_eq!(rt.pointer_size(), 4);
}
#[test]
fn test_stack_alignment_64() {
let rt = make_rt_x86_64();
assert_eq!(rt.stack_alignment(), 16);
}
#[test]
fn test_stack_alignment_32() {
let rt = make_rt_i386();
assert_eq!(rt.stack_alignment(), 16);
}
#[test]
fn test_cpu_mask() {
let rt = X86CompilerRT::default_x86_64();
let mask = rt.cpu_mask();
assert!(mask & (1 << 0) != 0); assert!(mask & (1 << 1) != 0); }
#[test]
fn test_has_fpu_flag() {
let rt = make_rt_x86_64();
assert!(rt.has_fpu);
}
#[test]
fn test_personality_fn_default() {
let rt = make_rt_x86_64();
assert_eq!(rt.personality_fn, "__gxx_personality_v0");
}
#[test]
fn test_stack_guard_default() {
let rt = make_rt_x86_64();
assert!(rt.stack_guard != 0);
}
#[test]
fn test_debug_clone() {
let rt = make_rt_x86_64();
let rt2 = rt.clone();
assert_eq!(rt.is_64bit, rt2.is_64bit);
assert_eq!(rt.stack_guard, rt2.stack_guard);
}
}
mod atomic_ordering {
use super::*;
#[test]
fn test_atomic_ordering_from_u32() {
assert_eq!(AtomicOrdering::from(0), AtomicOrdering::Relaxed);
assert_eq!(AtomicOrdering::from(1), AtomicOrdering::Consume);
assert_eq!(AtomicOrdering::from(2), AtomicOrdering::Acquire);
assert_eq!(AtomicOrdering::from(3), AtomicOrdering::Release);
assert_eq!(AtomicOrdering::from(4), AtomicOrdering::AcqRel);
assert_eq!(AtomicOrdering::from(5), AtomicOrdering::SeqCst);
}
#[test]
fn test_atomic_ordering_from_invalid() {
assert_eq!(AtomicOrdering::from(99), AtomicOrdering::SeqCst);
}
#[test]
fn test_atomic_ordering_debug() {
let o = AtomicOrdering::SeqCst;
assert_eq!(format!("{:?}", o), "SeqCst");
}
#[test]
fn test_atomic_ordering_clone() {
let o = AtomicOrdering::Acquire;
let o2 = o;
assert_eq!(o, o2);
}
}
mod ieee_helpers {
use super::*;
#[test]
fn test_decompose_f32_normal() {
let (sign, exp, mant) = decompose_f32(1.0);
assert!(!sign);
assert_eq!(exp, 127);
assert_eq!(mant, 0);
}
#[test]
fn test_decompose_f32_negative() {
let (sign, exp, mant) = decompose_f32(-1.0);
assert!(sign);
assert_eq!(exp, 127);
assert_eq!(mant, 0);
}
#[test]
fn test_decompose_f64_normal() {
let (sign, exp, mant) = decompose_f64(1.0);
assert!(!sign);
assert_eq!(exp, 1023);
assert_eq!(mant, 0);
}
#[test]
fn test_decompose_f64_negative() {
let (sign, exp, mant) = decompose_f64(-1.0);
assert!(sign);
assert_eq!(exp, 1023);
assert_eq!(mant, 0);
}
#[test]
fn test_compose_f32_roundtrip() {
let original = 3.14f32;
let (s, e, m) = decompose_f32(original);
let reconstructed = compose_f32(s, e, m);
assert_eq!(original.to_bits(), reconstructed.to_bits());
}
#[test]
fn test_compose_f64_roundtrip() {
let original = 3.141592653589793;
let (s, e, m) = decompose_f64(original);
let reconstructed = compose_f64(s, e, m);
assert_eq!(original.to_bits(), reconstructed.to_bits());
}
#[test]
fn test_f32_is_nan() {
assert!(f32_is_nan(f32::NAN));
assert!(!f32_is_nan(1.0));
assert!(!f32_is_nan(f32::INFINITY));
}
#[test]
fn test_f64_is_nan() {
assert!(f64_is_nan(f64::NAN));
assert!(!f64_is_nan(1.0));
assert!(!f64_is_nan(f64::INFINITY));
}
#[test]
fn test_f32_is_inf() {
assert!(f32_is_inf(f32::INFINITY));
assert!(f32_is_inf(f32::NEG_INFINITY));
assert!(!f32_is_inf(1.0));
assert!(!f32_is_inf(f32::NAN));
}
#[test]
fn test_f64_is_inf() {
assert!(f64_is_inf(f64::INFINITY));
assert!(f64_is_inf(f64::NEG_INFINITY));
assert!(!f64_is_inf(1.0));
assert!(!f64_is_inf(f64::NAN));
}
#[test]
fn test_f32_is_zero() {
assert!(f32_is_zero(0.0));
assert!(f32_is_zero(-0.0));
assert!(!f32_is_zero(1.0));
}
#[test]
fn test_f64_is_zero() {
assert!(f64_is_zero(0.0));
assert!(f64_is_zero(-0.0));
assert!(!f64_is_zero(1.0));
}
#[test]
fn test_addtf3_impl_basic() {
let (hi, _lo) = addtf3_impl(1.0, 1e-16, 2.0, 2e-16);
assert!(approx_eq_f64(hi, 3.0, 1e-12));
}
}
mod soft_float_internal {
use super::*;
#[test]
fn test_soft_add_f32_basic() {
let rt = make_rt_x86_64();
let result = rt.soft_add_f32(1.0, 2.0);
assert!(approx_eq_f32(result, 3.0, 1e-6));
}
#[test]
fn test_soft_add_f32_negative() {
let rt = make_rt_x86_64();
let result = rt.soft_add_f32(-1.0, -2.0);
assert!(approx_eq_f32(result, -3.0, 1e-6));
}
#[test]
fn test_soft_add_f32_cancellation() {
let rt = make_rt_x86_64();
let result = rt.soft_add_f32(5.0, -5.0);
assert_eq!(result, 0.0);
}
#[test]
fn test_soft_add_f32_nan() {
let rt = make_rt_x86_64();
assert!(rt.soft_add_f32(f32::NAN, 1.0).is_nan());
assert!(rt.soft_add_f32(1.0, f32::NAN).is_nan());
}
#[test]
fn test_soft_add_f32_infinity() {
let rt = make_rt_x86_64();
assert_eq!(rt.soft_add_f32(f32::INFINITY, 1.0), f32::INFINITY);
}
#[test]
fn test_soft_add_f32_opposite_infinity() {
let rt = make_rt_x86_64();
assert!(rt.soft_add_f32(f32::INFINITY, f32::NEG_INFINITY).is_nan());
}
#[test]
fn test_soft_add_f32_zero() {
let rt = make_rt_x86_64();
assert_eq!(rt.soft_add_f32(0.0, 0.0), 0.0);
assert_eq!(rt.soft_add_f32(5.0, 0.0), 5.0);
}
#[test]
fn test_soft_add_f32_negative_zero() {
let rt = make_rt_x86_64();
let result = rt.soft_add_f32(-0.0, -0.0);
assert_eq!(result.to_bits(), (-0.0f32).to_bits());
}
#[test]
fn test_soft_add_f64_basic() {
let rt = make_rt_x86_64();
let result = rt.soft_add_f64(1.0, 2.0);
assert!(approx_eq_f64(result, 3.0, 1e-12));
}
#[test]
fn test_soft_add_f64_cancellation() {
let rt = make_rt_x86_64();
let result = rt.soft_add_f64(5.0, -5.0);
assert_eq!(result, 0.0);
}
#[test]
fn test_soft_add_f64_nan() {
let rt = make_rt_x86_64();
assert!(rt.soft_add_f64(f64::NAN, 1.0).is_nan());
}
#[test]
fn test_soft_add_f64_infinity() {
let rt = make_rt_x86_64();
assert_eq!(rt.soft_add_f64(f64::INFINITY, 1.0), f64::INFINITY);
assert!(rt.soft_add_f64(f64::INFINITY, f64::NEG_INFINITY).is_nan());
}
#[test]
fn test_soft_mul_f32_basic() {
let rt = make_rt_x86_64();
let result = rt.soft_mul_f32(3.0, 4.0);
assert!(approx_eq_f32(result, 12.0, 1e-6));
}
#[test]
fn test_soft_mul_f32_zero() {
let rt = make_rt_x86_64();
assert_eq!(rt.soft_mul_f32(0.0, 5.0), 0.0);
assert_eq!(rt.soft_mul_f32(5.0, 0.0), 0.0);
}
#[test]
fn test_soft_mul_f32_negative() {
let rt = make_rt_x86_64();
let result = rt.soft_mul_f32(-3.0, 4.0);
assert!(approx_eq_f32(result, -12.0, 1e-6));
}
#[test]
fn test_soft_mul_f32_nan() {
let rt = make_rt_x86_64();
assert!(rt.soft_mul_f32(f32::NAN, 1.0).is_nan());
}
#[test]
fn test_soft_mul_f32_inf_zero() {
let rt = make_rt_x86_64();
assert!(rt.soft_mul_f32(f32::INFINITY, 0.0).is_nan());
}
#[test]
fn test_soft_mul_f64_basic() {
let rt = make_rt_x86_64();
let result = rt.soft_mul_f64(3.0, 4.0);
assert!(approx_eq_f64(result, 12.0, 1e-12));
}
#[test]
fn test_soft_mul_f64_negative() {
let rt = make_rt_x86_64();
let result = rt.soft_mul_f64(-3.0, 4.0);
assert!(approx_eq_f64(result, -12.0, 1e-12));
}
#[test]
fn test_soft_mul_f64_nan() {
let rt = make_rt_x86_64();
assert!(rt.soft_mul_f64(f64::NAN, 1.0).is_nan());
}
#[test]
fn test_soft_mul_f64_zero() {
let rt = make_rt_x86_64();
assert_eq!(rt.soft_mul_f64(0.0, 5.0), 0.0);
}
#[test]
fn test_soft_div_f32_basic() {
let rt = make_rt_x86_64();
let result = rt.soft_div_f32(10.0, 2.0);
assert!(approx_eq_f32(result, 5.0, 1e-6));
}
#[test]
fn test_soft_div_f32_by_zero() {
let rt = make_rt_x86_64();
assert_eq!(rt.soft_div_f32(1.0, 0.0), f32::INFINITY);
assert_eq!(rt.soft_div_f32(-1.0, 0.0), f32::NEG_INFINITY);
}
#[test]
fn test_soft_div_f32_nan() {
let rt = make_rt_x86_64();
assert!(rt.soft_div_f32(f32::NAN, 1.0).is_nan());
assert!(rt.soft_div_f32(1.0, f32::NAN).is_nan());
}
#[test]
fn test_soft_div_f32_inf_by_inf() {
let rt = make_rt_x86_64();
assert!(rt.soft_div_f32(f32::INFINITY, f32::INFINITY).is_nan());
}
#[test]
fn test_soft_div_f64_basic() {
let rt = make_rt_x86_64();
let result = rt.soft_div_f64(10.0, 2.0);
assert!(approx_eq_f64(result, 5.0, 1e-12));
}
#[test]
fn test_soft_div_f64_by_zero() {
let rt = make_rt_x86_64();
assert_eq!(rt.soft_div_f64(1.0, 0.0), f64::INFINITY);
}
#[test]
fn test_soft_div_f64_nan() {
let rt = make_rt_x86_64();
assert!(rt.soft_div_f64(f64::NAN, 1.0).is_nan());
}
}
mod demangler {
use super::*;
#[test]
fn test_demangle_simple_function() {
let rt = make_rt_x86_64();
let result = rt.x86_demangle_itanium("_Z3foov");
assert!(result.is_some());
assert_eq!(result.unwrap(), "foo");
}
#[test]
fn test_demangle_invalid_prefix() {
let rt = make_rt_x86_64();
assert!(rt.x86_demangle_itanium("foobar").is_none());
}
#[test]
fn test_demangle_empty() {
let rt = make_rt_x86_64();
assert!(rt.x86_demangle_itanium("").is_none());
}
#[test]
fn test_demangle_with_int() {
let rt = make_rt_x86_64();
let result = rt.x86_demangle_itanium("_Z3fooiv");
assert!(result.is_some());
let s = result.unwrap();
assert!(s.contains("foo"));
}
#[test]
fn test_demangle_with_float() {
let rt = make_rt_x86_64();
let result = rt.x86_demangle_itanium("_Z3barfv");
assert!(result.is_some());
let s = result.unwrap();
assert!(s.contains("bar"));
}
#[test]
fn test_demangle_with_double() {
let rt = make_rt_x86_64();
let result = rt.x86_demangle_itanium("_Z3bazdv");
assert!(result.is_some());
let s = result.unwrap();
assert!(s.contains("baz"));
}
}
mod conversion_edge_cases {
use super::*;
#[test]
fn test_fixsfsi_edge_max() {
let rt = make_rt_x86_64();
let result = rt.fixsfsi(2.14748365e9); assert!(!result.is_negative() || result == i32::MIN);
}
#[test]
fn test_fixsfsi_edge_min() {
let rt = make_rt_x86_64();
let result = rt.fixsfsi(-2.14748365e9); assert!(result.is_negative() || result == 0);
}
#[test]
fn test_fixdfsi_near_max() {
let rt = make_rt_x86_64();
let result = rt.fixdfsi(i32::MAX as f64);
assert_eq!(result, i32::MAX);
}
#[test]
fn test_fixdfsi_near_min() {
let rt = make_rt_x86_64();
let result = rt.fixdfsi(i32::MIN as f64);
assert_eq!(result, i32::MIN);
}
#[test]
fn test_fixdfdi_near_max() {
let rt = make_rt_x86_64();
let result = rt.fixdfdi(i64::MAX as f64);
assert!(result >= i64::MAX / 2);
}
#[test]
fn test_floatdisf_large_integer() {
let rt = make_rt_x86_64();
let result = rt.floatdisf(i64::MAX);
assert!(result > 0.0);
assert!(result.is_finite());
}
#[test]
fn test_floatdidf_large_integer() {
let rt = make_rt_x86_64();
let result = rt.floatdidf(i64::MAX);
assert!(result > 0.0);
assert!(result.is_finite());
}
#[test]
fn test_floatundisf_large() {
let rt = make_rt_x86_64();
let result = rt.floatundisf(u64::MAX);
assert!(result > 0.0);
assert!(result.is_finite() || result.is_infinite());
}
#[test]
fn test_floatundidf_large() {
let rt = make_rt_x86_64();
let result = rt.floatundidf(u64::MAX);
assert!(result > 0.0);
assert!(result.is_finite());
}
#[test]
fn test_truncdfsf2_very_small() {
let rt = make_rt_x86_64();
let result = rt.truncdfsf2(1e-50);
assert_eq!(result, 0.0);
}
#[test]
fn test_truncdfsf2_normal() {
let rt = make_rt_x86_64();
let result = rt.truncdfsf2(3.141592653589793);
assert!(approx_eq_f32(result, 3.1415927, 1e-6));
}
#[test]
fn test_fixunssfsi_overflow() {
let rt = make_rt_x86_64();
let result = rt.fixunssfsi(u32::MAX as f32);
assert_eq!(result, u32::MAX);
}
#[test]
fn test_fixunsdfsi_near_max() {
let rt = make_rt_x86_64();
let result = rt.fixunsdfsi(u32::MAX as f64);
assert!(result == u32::MAX || result > u32::MAX / 2);
}
#[test]
fn test_fixunssfdi_large() {
let rt = make_rt_x86_64();
let result = rt.fixunssfdi(3.0e9f32);
assert!(result > 1000);
}
#[test]
fn test_fixunsdfdi_large() {
let rt = make_rt_x86_64();
let result = rt.fixunsdfdi(1e18);
assert!(result > 1000);
}
}
mod integration {
use super::*;
#[test]
fn test_soft_float_workflow() {
let rt = make_rt_i386(); let add = rt.addsf3(1.5, 2.5);
let mul = rt.mulsf3(add, 2.0);
let sub = rt.subsf3(mul, 1.0);
let div = rt.divsf3(sub, 7.0);
assert!(approx_eq_f32(div, 1.0, 1e-6));
}
#[test]
fn test_soft_float_double_workflow() {
let rt = make_rt_i386();
let a = 3.14;
let b = 2.72;
let sum = rt.adddf3(a, b);
let diff = rt.subdf3(a, b);
let prod = rt.muldf3(sum, diff);
assert!(prod > 0.0);
}
#[test]
fn test_conversion_roundtrip_f32() {
let rt = make_rt_x86_64();
for &v in &[0i32, 1, -1, 42, -42, 1000, -1000] {
let f = rt.floatsisf(v);
let i = rt.fixsfsi(f);
assert_eq!(i, v, "roundtrip failed for {}", v);
}
}
#[test]
fn test_conversion_roundtrip_f64() {
let rt = make_rt_x86_64();
for &v in &[0i32, 1, -1, 42, -42, 1000, -1000] {
let f = rt.floatsidf(v);
let i = rt.fixdfsi(f);
assert_eq!(i, v, "roundtrip failed for {}", v);
}
}
#[test]
fn test_conversion_unsigned_roundtrip_f32() {
let rt = make_rt_x86_64();
for &v in &[0u32, 1, 42, 1000] {
let f = rt.floatunsisf(v);
let i = rt.fixunssfsi(f);
assert_eq!(i, v, "unsigned roundtrip failed for {}", v);
}
}
#[test]
fn test_conversion_unsigned_roundtrip_f64() {
let rt = make_rt_x86_64();
for &v in &[0u32, 1, 42, 1000] {
let f = rt.floatunsidf(v);
let i = rt.fixunsdfsi(f);
assert_eq!(i, v, "unsigned roundtrip failed for {}", v);
}
}
#[test]
fn test_i128_roundtrip_signed() {
let rt = make_rt_x86_64();
let (m_hi, m_lo) = rt.multi3(0, 100, 0, 3);
let (d_hi, d_lo) = rt.divti3(m_hi, m_lo, 0, 3);
assert_eq!(d_hi, 0);
assert_eq!(d_lo, 100);
}
#[test]
fn test_i128_roundtrip_unsigned() {
let rt = make_rt_x86_64();
let (q_hi, q_lo) = rt.udivti3(0, 300, 0, 3);
assert_eq!(q_hi, 0);
assert_eq!(q_lo, 100);
}
#[test]
fn test_multi3_with_carry() {
let rt = make_rt_x86_64();
let (p_hi, p_lo) = rt.multi3(0, 1u64 << 63, 0, 2);
assert_eq!(p_hi, 1);
assert_eq!(p_lo, 0);
}
#[test]
fn test_umulti3_with_carry() {
let rt = make_rt_x86_64();
let (p_hi, p_lo) = rt.umulti3(0, 1u64 << 63, 0, 2);
assert_eq!(p_hi, 1);
assert_eq!(p_lo, 0);
}
#[test]
fn test_shift_pattern_ashlti3() {
let rt = make_rt_x86_64();
let (hi, lo) = rt.ashlti3(0, 1, 100);
assert_eq!(hi, 1u64 << 36);
assert_eq!(lo, 0);
}
#[test]
fn test_shift_pattern_lshrti3() {
let rt = make_rt_x86_64();
let (hi, lo) = rt.lshrti3(1u64 << 36, 0, 100);
assert_eq!(hi, 0);
assert_eq!(lo, 1);
}
#[test]
fn test_atomic_sequence() {
let rt = make_rt_x86_64();
let mut val: u32 = 0;
rt.atomic_store_4(&mut val as *mut u32 as *mut u8, 10);
rt.atomic_fetch_add_4(&mut val, 5);
rt.atomic_fetch_sub_4(&mut val, 3);
let final_val = rt.atomic_load_4(&val as *const u32 as *const u8);
assert_eq!(final_val, 12);
}
#[test]
fn test_atomic_bit_operations_sequence() {
let rt = make_rt_x86_64();
let mut val: u32 = 0xFF;
rt.atomic_fetch_and_4(&mut val, 0x0F);
rt.atomic_fetch_or_4(&mut val, 0xF0);
let final_val = rt.atomic_load_4(&val as *const u32 as *const u8);
assert_eq!(final_val, 0xFF);
}
#[test]
fn test_sync_vs_atomic_consistency() {
let rt = make_rt_x86_64();
let mut a: u32 = 100;
let mut b: u32 = 100;
let sync_old = rt.sync_fetch_and_add_4(&mut a, 5);
let atomic_old = rt.atomic_fetch_add_4(&mut b, 5);
assert_eq!(sync_old, atomic_old);
assert_eq!(a, b);
}
#[test]
fn test_guard_lifecycle() {
let rt = make_rt_x86_64();
let mut guard: u64 = 0;
assert_eq!(rt.cxa_guard_acquire(&mut guard), 1);
rt.cxa_guard_release(&mut guard);
assert_eq!(rt.cxa_guard_acquire(&mut guard), 0);
}
#[test]
fn test_guard_abort_lifecycle() {
let rt = make_rt_x86_64();
let mut guard: u64 = 0;
assert_eq!(rt.cxa_guard_acquire(&mut guard), 1);
rt.cxa_guard_abort(&mut guard);
let guard_byte = unsafe { &*(&guard as *const u64 as *const u8) };
assert_eq!(*guard_byte, 0);
}
#[test]
fn test_security_operations_chain() {
let rt = make_rt_x86_64();
let mut buf = [0u8; 64];
let src = [42u8; 16];
rt.memcpy_chk(buf.as_mut_ptr(), src.as_ptr(), 16, 64);
assert_eq!(&buf[..16], &src[..]);
rt.memset_chk(buf.as_mut_ptr(), 0xAB, 32, 64);
assert_eq!(buf[0], 0xAB);
assert_eq!(buf[31], 0xAB);
let hello = b"hello\0";
rt.strcpy_chk(buf.as_mut_ptr(), hello.as_ptr(), 64);
assert_eq!(&buf[..6], b"hello\0");
}
#[test]
fn test_x87_trig_identity() {
let rt = make_rt_x86_64();
let x = 0.5;
let s = rt.sin_f64(x);
let c = rt.cos_f64(x);
let sum_sq = s * s + c * c;
assert!(approx_eq_f64(sum_sq, 1.0, 1e-4));
}
#[test]
fn test_x87_exp_log_inverse() {
let rt = make_rt_x86_64();
let x = 2.0;
let e = rt.exp_f64(x);
let l = rt.log_f64(e);
assert!(approx_eq_f64(l, x, 1e-6));
}
#[test]
fn test_pow_sqrt_relationship() {
let rt = make_rt_x86_64();
let x = 4.0;
let sqrt_val = rt.sqrt_f64(x);
let pow_val = rt.pow_f64(x, 0.5);
assert!(approx_eq_f64(sqrt_val, pow_val, 1e-5));
}
#[test]
fn test_full_fp_workflow() {
let rt = make_rt_x86_64();
let x = 1.0;
let s = rt.sin_f64(x);
let c = rt.cos_f64(x);
let s2 = rt.muldf3(s, s);
let c2 = rt.muldf3(c, c);
let result = rt.adddf3(s2, c2);
assert!(approx_eq_f64(result, 1.0, 1e-4));
}
#[test]
fn test_neg_abs_relationship() {
let rt = make_rt_x86_64();
let x = -3.14;
let neg = rt.negdf2(x);
let abs_neg = rt.fabs_f64(neg);
assert!(approx_eq_f64(abs_neg, 3.14, 1e-12));
}
#[test]
fn test_ldexp_frexp_roundtrip() {
let rt = make_rt_x86_64();
let original = 42.0;
let (frac, exp) = rt.frexp_f64(original);
let reconstructed = rt.ldexp_f64(frac, exp);
assert!(approx_eq_f64(reconstructed, original, 1e-10));
}
}
mod stress {
use super::*;
#[test]
fn test_many_addsf3() {
let rt = make_rt_x86_64();
let mut sum = 0.0f32;
for i in 0..1000 {
sum = rt.addsf3(sum, i as f32);
}
assert!(sum > 0.0);
}
#[test]
fn test_many_adddf3() {
let rt = make_rt_x86_64();
let mut sum = 0.0f64;
for i in 0..1000 {
sum = rt.adddf3(sum, i as f64);
}
assert!(sum > 0.0);
}
#[test]
fn test_many_muldf3() {
let rt = make_rt_x86_64();
let mut prod = 1.0f64;
for _ in 0..100 {
prod = rt.muldf3(prod, 1.001);
}
assert!(prod > 1.0);
}
#[test]
fn test_many_divisions() {
let rt = make_rt_x86_64();
let mut val = 1000.0f64;
for _ in 0..100 {
val = rt.divdf3(val, 1.001);
}
assert!(val < 1000.0);
}
#[test]
fn test_many_ashldi3() {
let rt = make_rt_x86_64();
assert_eq!(rt.ashldi3(1, 0), 1);
assert_eq!(rt.ashldi3(1, 1), 2);
assert_eq!(rt.ashldi3(1, 2), 4);
assert_eq!(rt.ashldi3(1, 10), 1024);
assert_eq!(rt.ashldi3(1, 63), 1i64 << 63);
}
#[test]
fn test_many_ashrdi3() {
let rt = make_rt_x86_64();
let val = 1i64 << 62;
assert_eq!(rt.ashrdi3(val, 62), 1);
assert_eq!(rt.ashrdi3(val, 63), 0);
}
#[test]
fn test_many_clz() {
let rt = make_rt_x86_64();
for i in 0..32 {
let val = 1u32 << i;
let expected = 31 - i;
assert_eq!(
rt.clzsi2(val),
expected,
"clz(1 << {}) expected {}",
i,
expected
);
}
}
#[test]
fn test_many_ctz() {
let rt = make_rt_x86_64();
for i in 0..32 {
let val = 1u32 << i;
assert_eq!(rt.ctzsi2(val), i, "ctz(1 << {}) expected {}", i, i);
}
}
#[test]
fn test_many_popcount() {
let rt = make_rt_x86_64();
assert_eq!(rt.popcountsi2(0x0000_0001), 1);
assert_eq!(rt.popcountsi2(0x0000_0003), 2);
assert_eq!(rt.popcountsi2(0x0000_0007), 3);
assert_eq!(rt.popcountsi2(0x0000_000F), 4);
assert_eq!(rt.popcountsi2(0xFFFF_FFFF), 32);
}
#[test]
fn test_many_bswapsi2() {
let rt = make_rt_x86_64();
let original = [0x12345678u32, 0xAABBCCDDu32, 0x00000001u32, 0x80000000u32];
for &val in &original {
let swapped = rt.bswapsi2(val);
let back = rt.bswapsi2(swapped);
assert_eq!(back, val, "bswap roundtrip failed for 0x{:08X}", val);
}
}
#[test]
fn test_many_fp_compares() {
let rt = make_rt_x86_64();
let values: [f64; 5] = [-1.0, 0.0, 0.5, 1.0, f64::NAN];
for &a in &values {
for &b in &values {
let result = rt.cmpdf2(a, b);
assert!(result >= -2 && result <= 1);
}
}
}
#[test]
fn test_i128_division_many_sizes() {
let rt = make_rt_x86_64();
let a_hi = 100u64;
let a_lo = 0u64;
let b_hi = 10u64;
let b_lo = 0u64;
let (q_hi, q_lo) = rt.udivti3(a_hi, a_lo, b_hi, b_lo);
assert_eq!(q_hi, 10);
assert_eq!(q_lo, 0);
}
#[test]
fn test_i128_division_with_remainder() {
let rt = make_rt_x86_64();
let (q_hi, q_lo) = rt.udivti3(0, 10, 0, 3);
let (r_hi, r_lo) = rt.umodti3(0, 10, 0, 3);
assert_eq!(q_hi, 0);
assert_eq!(q_lo, 3);
assert_eq!(r_hi, 0);
assert_eq!(r_lo, 1);
}
}
mod quad_precision {
use super::*;
#[test]
fn test_multf3_identity() {
let rt = make_rt_x86_64();
let (hi, _lo) = rt.multf3(1.0, 0.0, 5.0, 0.0);
assert!(approx_eq_f64(hi, 5.0, 1e-12));
}
#[test]
fn test_multf3_half() {
let rt = make_rt_x86_64();
let (hi, _lo) = rt.multf3(0.5, 0.0, 0.5, 0.0);
assert!(approx_eq_f64(hi, 0.25, 1e-12));
}
#[test]
fn test_divtf3_exact() {
let rt = make_rt_x86_64();
let (hi, _lo) = rt.divtf3(6.0, 0.0, 3.0, 0.0);
assert!(approx_eq_f64(hi, 2.0, 1e-12));
}
#[test]
fn test_divtf3_inexact() {
let rt = make_rt_x86_64();
let (hi, _lo) = rt.divtf3(1.0, 0.0, 3.0, 0.0);
assert!(approx_eq_f64(hi, 1.0 / 3.0, 1e-12));
}
#[test]
fn test_addtf3_negative() {
let (hi, _lo) = addtf3_impl(-1.0, 0.0, -2.0, 0.0);
assert!(approx_eq_f64(hi, -3.0, 1e-12));
}
#[test]
fn test_addtf3_cancellation() {
let (hi, _lo) = addtf3_impl(5.0, 0.0, -5.0, 0.0);
assert!(approx_eq_f64(hi, 0.0, 1e-12));
}
}
mod x87_math_extended {
use super::*;
#[test]
fn test_sin_f64_negative() {
let rt = make_rt_x86_64();
let s_pos = rt.sin_f64(std::f64::consts::FRAC_PI_2);
let s_neg = rt.sin_f64(-std::f64::consts::FRAC_PI_2);
assert!(approx_eq_f64(s_pos, -s_neg, 1e-6));
}
#[test]
fn test_cos_f64_even() {
let rt = make_rt_x86_64();
let c_pos = rt.cos_f64(std::f64::consts::FRAC_PI_2);
let c_neg = rt.cos_f64(-std::f64::consts::FRAC_PI_2);
assert!(approx_eq_f64(c_pos, c_neg, 1e-6));
}
#[test]
fn test_tan_f64_negative() {
let rt = make_rt_x86_64();
let t_pos = rt.tan_f64(std::f64::consts::FRAC_PI_4);
let t_neg = rt.tan_f64(-std::f64::consts::FRAC_PI_4);
assert!(approx_eq_f64(t_pos, -t_neg, 1e-6));
}
#[test]
fn test_asin_f64_symmetry() {
let rt = make_rt_x86_64();
let v = 0.5;
assert!(approx_eq_f64(rt.asin_f64(v), -rt.asin_f64(-v), 1e-5));
}
#[test]
fn test_atan_f64_symmetry() {
let rt = make_rt_x86_64();
let v = 1.0;
assert!(approx_eq_f64(rt.atan_f64(v), -rt.atan_f64(-v), 1e-5));
}
#[test]
fn test_atan2_f64_axes() {
let rt = make_rt_x86_64();
assert!(approx_eq_f64(rt.atan2_f64(0.0, 1.0), 0.0, 1e-10));
assert!(approx_eq_f64(
rt.atan2_f64(1.0, 0.0),
std::f64::consts::FRAC_PI_2,
1e-6
));
assert!(approx_eq_f64(
rt.atan2_f64(0.0, -1.0),
std::f64::consts::PI,
1e-6
));
assert!(approx_eq_f64(
rt.atan2_f64(-1.0, 0.0),
-std::f64::consts::FRAC_PI_2,
1e-6
));
}
#[test]
fn test_sinh_f64_odd() {
let rt = make_rt_x86_64();
let x = 1.0;
assert!(approx_eq_f64(rt.sinh_f64(x), -rt.sinh_f64(-x), 1e-6));
}
#[test]
fn test_cosh_f64_even() {
let rt = make_rt_x86_64();
let x = 1.0;
assert!(approx_eq_f64(rt.cosh_f64(x), rt.cosh_f64(-x), 1e-6));
}
#[test]
fn test_tanh_f64_range() {
let rt = make_rt_x86_64();
for x in &[-5.0, -1.0, 0.0, 1.0, 5.0] {
let t = rt.tanh_f64(*x);
assert!(t >= -1.0 && t <= 1.0, "tanh({}) = {} out of range", x, t);
}
}
#[test]
fn test_exp2_f64_powers() {
let rt = make_rt_x86_64();
for i in -5..=5 {
let expected = 2.0f64.powi(i);
let actual = rt.exp2_f64(i as f64);
assert!(
approx_eq_f64(actual, expected, 1e-8),
"exp2({}) = {}, expected {}",
i,
actual,
expected
);
}
}
#[test]
fn test_log2_f64_powers() {
let rt = make_rt_x86_64();
for i in 1..=10 {
let val = 2.0f64.powi(i);
let actual = rt.log2_f64(val);
assert!(
approx_eq_f64(actual, i as f64, 1e-10),
"log2(2^{}) = {}, expected {}",
i,
actual,
i
);
}
}
#[test]
fn test_log10_f64_powers() {
let rt = make_rt_x86_64();
for i in 0..=5 {
let val = 10.0f64.powi(i);
let actual = rt.log10_f64(val);
assert!(
approx_eq_f64(actual, i as f64, 1e-10),
"log10(10^{}) = {}, expected {}",
i,
actual,
i
);
}
}
#[test]
fn test_ldexp_f64_powers() {
let rt = make_rt_x86_64();
for i in 0..=10 {
let expected = 1.0f64 * (1u64 << i) as f64;
let actual = rt.ldexp_f64(1.0, i);
assert!(
approx_eq_f64(actual, expected, 1e-12),
"ldexp(1, {}) = {}, expected {}",
i,
actual,
expected
);
}
}
#[test]
fn test_scalbn_f64_powers() {
let rt = make_rt_x86_64();
for i in -5..=5 {
let expected = 1.0f64 * 2.0f64.powi(i);
let actual = rt.scalbn_f64(1.0, i);
assert!(approx_eq_f64(actual, expected, 1e-12));
}
}
#[test]
fn test_fmin_f64_negative_zero() {
let rt = make_rt_x86_64();
let result = rt.fmin_f64(-0.0, 0.0);
assert!(result.to_bits() == (-0.0f64).to_bits());
}
#[test]
fn test_fmax_f64_negative_zero() {
let rt = make_rt_x86_64();
let result = rt.fmax_f64(-0.0, 0.0);
assert!(result.to_bits() == 0.0f64.to_bits());
}
#[test]
fn test_copysign_f64_zero() {
let rt = make_rt_x86_64();
let result = rt.copysign_f64(1.0, -0.0);
assert_eq!(result, -1.0);
}
}
mod exception_handling_extended {
use super::*;
#[test]
fn test_unwind_forced_unwind() {
let rt = make_rt_x86_64();
let exc = rt.cxa_allocate_exception(16);
let result = rt.unwind_forced_unwind(exc, None, std::ptr::null_mut());
assert_eq!(result, 5); }
#[test]
fn test_unwind_get_language_specific_data() {
let rt = make_rt_x86_64();
let result = rt.unwind_get_language_specific_data(0x400000);
assert!(result.is_null());
}
#[test]
fn test_unwind_get_region_start() {
let rt = make_rt_x86_64();
let result = rt.unwind_get_region_start(0x400000);
assert_eq!(result, 0);
}
#[test]
fn test_gxx_personality_v0_handler_frame() {
let rt = make_rt_x86_64();
let mut ctx: u64 = 0;
let result = rt.gxx_personality_v0(
1,
6,
0,
std::ptr::null_mut(),
&mut ctx as *mut u64 as *mut u8,
);
assert_eq!(result, 7); }
#[test]
fn test_gcc_personality_bad_version() {
let rt = make_rt_x86_64();
let mut ctx: u64 = 0;
let result = rt.gcc_personality_v0(
0,
0,
0,
std::ptr::null_mut(),
&mut ctx as *mut u64 as *mut u8,
);
assert_eq!(result, 9);
}
#[test]
fn test_unwind_delete_exception() {
let rt = make_rt_x86_64();
let exc = rt.cxa_allocate_exception(16);
rt.unwind_delete_exception(exc);
}
#[test]
fn test_unwind_get_set_cfa() {
let rt = make_rt_x86_64();
let mut ctx = [0u8; 256];
let cfa = rt.unwind_get_cfa(ctx.as_ptr());
assert_eq!(cfa, 0);
}
}
mod security_hardening_extended {
use super::*;
#[test]
fn test_memcpy_chk_zero_length() {
let rt = make_rt_x86_64();
let src = [1u8, 2, 3, 4];
let mut dest = [0u8; 4];
let result = rt.memcpy_chk(dest.as_mut_ptr(), src.as_ptr(), 0, 4);
assert_eq!(result, dest.as_mut_ptr());
assert_eq!(dest, [0u8; 4]);
}
#[test]
fn test_memmove_chk_overlapping() {
let rt = make_rt_x86_64();
let mut buf = [1u8, 2, 3, 4, 5, 6, 7, 8];
rt.memmove_chk(unsafe { buf.as_mut_ptr().add(2) }, buf.as_ptr(), 4, 6);
assert_eq!(buf[2], 1);
assert_eq!(buf[3], 2);
assert_eq!(buf[4], 3);
assert_eq!(buf[5], 4);
}
#[test]
fn test_memset_chk_pattern() {
let rt = make_rt_x86_64();
let mut buf = [0u8; 32];
rt.memset_chk(buf.as_mut_ptr(), 0xDE, 16, 32);
for i in 0..16 {
assert_eq!(buf[i], 0xDE);
}
for i in 16..32 {
assert_eq!(buf[i], 0);
}
}
#[test]
fn test_strncpy_chk_exact() {
let rt = make_rt_x86_64();
let src = b"exact\0";
let mut dest = [0u8; 10];
rt.strncpy_chk(dest.as_mut_ptr(), src.as_ptr(), 6, 10);
assert_eq!(&dest[..6], b"exact\0");
}
#[test]
fn test_strncpy_chk_padding() {
let rt = make_rt_x86_64();
let src = b"hi\0";
let mut dest = [0xFFu8; 10];
rt.strncpy_chk(dest.as_mut_ptr(), src.as_ptr(), 5, 10);
assert_eq!(dest[0], b'h');
assert_eq!(dest[1], b'i');
assert_eq!(dest[2], 0);
assert_eq!(dest[3], 0);
assert_eq!(dest[4], 0);
}
#[test]
fn test_strncat_chk_limit() {
let rt = make_rt_x86_64();
let mut dest = [0u8; 20];
dest[0] = b'X';
dest[1] = 0;
let src = b"YZ\0";
rt.strncat_chk(dest.as_mut_ptr(), src.as_ptr(), 1, 20);
assert_eq!(dest[0], b'X');
assert_eq!(dest[1], b'Y');
assert_eq!(dest[2], 0);
}
#[test]
fn test_sprintf_chk_minimum() {
let rt = make_rt_x86_64();
let mut dest = [0u8; 1];
let fmt = b"%s\0";
let result = rt.sprintf_chk(dest.as_mut_ptr(), 0, 1, fmt.as_ptr());
assert_eq!(result, 0);
}
#[test]
#[should_panic]
fn test_sprintf_chk_zero_length() {
let rt = make_rt_x86_64();
let fmt = b"hello\0";
rt.sprintf_chk(std::ptr::null_mut(), 0, 0, fmt.as_ptr());
}
#[test]
fn test_vsprintf_chk_null_ap() {
let rt = make_rt_x86_64();
let mut dest = [0u8; 4];
let fmt = b"hi\0";
let result =
rt.vsprintf_chk(dest.as_mut_ptr(), 0, 4, fmt.as_ptr(), std::ptr::null_mut());
assert_eq!(result, 0);
}
#[test]
fn test_vsnprintf_chk_null_dest() {
let rt = make_rt_x86_64();
let fmt = b"hi\0";
let result = rt.vsnprintf_chk(
std::ptr::null_mut(),
0,
0,
10,
fmt.as_ptr(),
std::ptr::null_mut(),
);
assert_eq!(result, -1);
}
}
mod extern_c_entry_points {
use super::*;
#[test]
fn test_extern_addsf3() {
init_x86_compiler_rt(true);
let result = __addsf3(1.0, 2.0);
assert!(approx_eq_f32(result, 3.0, 1e-6));
}
#[test]
fn test_extern_adddf3() {
init_x86_compiler_rt(true);
let result = __adddf3(1.0, 2.0);
assert!(approx_eq_f64(result, 3.0, 1e-12));
}
#[test]
fn test_extern_subsf3() {
init_x86_compiler_rt(true);
let result = __subsf3(5.0, 2.0);
assert!(approx_eq_f32(result, 3.0, 1e-6));
}
#[test]
fn test_extern_subdf3() {
init_x86_compiler_rt(true);
let result = __subdf3(5.0, 2.0);
assert!(approx_eq_f64(result, 3.0, 1e-12));
}
#[test]
fn test_extern_mulsf3() {
init_x86_compiler_rt(true);
let result = __mulsf3(3.0, 4.0);
assert!(approx_eq_f32(result, 12.0, 1e-6));
}
#[test]
fn test_extern_muldf3() {
init_x86_compiler_rt(true);
let result = __muldf3(3.0, 4.0);
assert!(approx_eq_f64(result, 12.0, 1e-12));
}
#[test]
fn test_extern_divsf3() {
init_x86_compiler_rt(true);
let result = __divsf3(10.0, 2.0);
assert!(approx_eq_f32(result, 5.0, 1e-6));
}
#[test]
fn test_extern_divdf3() {
init_x86_compiler_rt(true);
let result = __divdf3(10.0, 2.0);
assert!(approx_eq_f64(result, 5.0, 1e-12));
}
#[test]
fn test_extern_negsf2() {
init_x86_compiler_rt(true);
assert_eq!(__negsf2(3.0), -3.0);
}
#[test]
fn test_extern_negdf2() {
init_x86_compiler_rt(true);
assert_eq!(__negdf2(3.0), -3.0);
}
#[test]
fn test_extern_eqsf2() {
init_x86_compiler_rt(true);
assert_eq!(__eqsf2(3.0, 3.0), 0);
assert_eq!(__eqsf2(3.0, 4.0), 1);
}
#[test]
fn test_extern_eqdf2() {
init_x86_compiler_rt(true);
assert_eq!(__eqdf2(3.0, 3.0), 0);
assert_eq!(__eqdf2(3.0, 4.0), 1);
}
#[test]
fn test_extern_cmpsf2() {
init_x86_compiler_rt(true);
assert_eq!(__cmpsf2(1.0, 2.0), -1);
assert_eq!(__cmpsf2(3.0, 2.0), 1);
assert_eq!(__cmpsf2(2.0, 2.0), 0);
}
#[test]
fn test_extern_cmpdf2() {
init_x86_compiler_rt(true);
assert_eq!(__cmpdf2(1.0, 2.0), -1);
assert_eq!(__cmpdf2(3.0, 2.0), 1);
assert_eq!(__cmpdf2(2.0, 2.0), 0);
}
#[test]
fn test_extern_extendsfdf2() {
init_x86_compiler_rt(true);
let result = __extendsfdf2(1.0f32);
assert!(approx_eq_f64(result, 1.0, 1e-12));
}
#[test]
fn test_extern_truncdfsf2() {
init_x86_compiler_rt(true);
let result = __truncdfsf2(1.0);
assert!(approx_eq_f32(result, 1.0, 1e-6));
}
#[test]
fn test_extern_fixsfsi() {
init_x86_compiler_rt(true);
assert_eq!(__fixsfsi(3.7f32), 3);
}
#[test]
fn test_extern_fixdfsi() {
init_x86_compiler_rt(true);
assert_eq!(__fixdfsi(3.7), 3);
}
#[test]
fn test_extern_fixsfdi() {
init_x86_compiler_rt(true);
assert_eq!(__fixsfdi(5.0f32), 5i64);
}
#[test]
fn test_extern_fixdfdi() {
init_x86_compiler_rt(true);
assert_eq!(__fixdfdi(5.0), 5i64);
}
#[test]
fn test_extern_floatsisf() {
init_x86_compiler_rt(true);
assert!(approx_eq_f32(__floatsisf(42), 42.0, 1e-6));
}
#[test]
fn test_extern_floatsidf() {
init_x86_compiler_rt(true);
assert!(approx_eq_f64(__floatsidf(42), 42.0, 1e-12));
}
#[test]
fn test_extern_floatdisf() {
init_x86_compiler_rt(true);
let result = __floatdisf(100i64);
assert!(approx_eq_f32(result, 100.0, 1.0));
}
#[test]
fn test_extern_floatdidf() {
init_x86_compiler_rt(true);
assert!(approx_eq_f64(__floatdidf(100i64), 100.0, 1e-12));
}
#[test]
fn test_extern_fixunssfsi() {
init_x86_compiler_rt(true);
assert_eq!(__fixunssfsi(3.7f32), 3u32);
}
#[test]
fn test_extern_fixunsdfsi() {
init_x86_compiler_rt(true);
assert_eq!(__fixunsdfsi(3.7), 3u32);
}
#[test]
fn test_extern_fixunssfdi() {
init_x86_compiler_rt(true);
assert_eq!(__fixunssfdi(5.0f32), 5u64);
}
#[test]
fn test_extern_fixunsdfdi() {
init_x86_compiler_rt(true);
assert_eq!(__fixunsdfdi(5.0), 5u64);
}
#[test]
fn test_extern_floatunsisf() {
init_x86_compiler_rt(true);
assert!(approx_eq_f32(__floatunsisf(42u32), 42.0, 1e-6));
}
#[test]
fn test_extern_floatunsidf() {
init_x86_compiler_rt(true);
assert!(approx_eq_f64(__floatunsidf(42u32), 42.0, 1e-12));
}
#[test]
fn test_extern_floatundisf() {
init_x86_compiler_rt(true);
assert!(__floatundisf(100u64) > 0.0);
}
#[test]
fn test_extern_floatundidf() {
init_x86_compiler_rt(true);
assert!(__floatundidf(100u64) > 0.0);
}
#[test]
fn test_extern_powisf2() {
init_x86_compiler_rt(true);
assert!(approx_eq_f32(__powisf2(2.0, 3), 8.0, 1e-6));
}
#[test]
fn test_extern_powidf2() {
init_x86_compiler_rt(true);
assert!(approx_eq_f64(__powidf2(2.0, 10), 1024.0, 1e-10));
}
#[test]
fn test_extern_divdi3() {
init_x86_compiler_rt(true);
assert_eq!(__divdi3(10, 3), 3);
}
#[test]
fn test_extern_moddi3() {
init_x86_compiler_rt(true);
assert_eq!(__moddi3(10, 3), 1);
}
#[test]
fn test_extern_udivdi3() {
init_x86_compiler_rt(true);
assert_eq!(__udivdi3(10, 3), 3);
}
#[test]
fn test_extern_umoddi3() {
init_x86_compiler_rt(true);
assert_eq!(__umoddi3(10, 3), 1);
}
#[test]
fn test_extern_muldi3() {
init_x86_compiler_rt(true);
assert_eq!(__muldi3(3, 4), 12);
}
#[test]
fn test_extern_ashldi3() {
init_x86_compiler_rt(true);
assert_eq!(__ashldi3(1, 3), 8);
}
#[test]
fn test_extern_ashrdi3() {
init_x86_compiler_rt(true);
assert_eq!(__ashrdi3(8, 3), 1);
}
#[test]
fn test_extern_lshrdi3() {
init_x86_compiler_rt(true);
assert_eq!(__lshrdi3(8, 3), 1);
}
#[test]
fn test_extern_clzsi2() {
init_x86_compiler_rt(true);
assert_eq!(__clzsi2(1), 31);
}
#[test]
fn test_extern_clzdi2() {
init_x86_compiler_rt(true);
assert_eq!(__clzdi2(1), 63);
}
#[test]
fn test_extern_ctzsi2() {
init_x86_compiler_rt(true);
assert_eq!(__ctzsi2(8), 3);
}
#[test]
fn test_extern_ctzdi2() {
init_x86_compiler_rt(true);
assert_eq!(__ctzdi2(8), 3);
}
#[test]
fn test_extern_popcountsi2() {
init_x86_compiler_rt(true);
assert_eq!(__popcountsi2(0b1011), 3);
}
#[test]
fn test_extern_popcountdi2() {
init_x86_compiler_rt(true);
assert_eq!(__popcountdi2(0b1011), 3);
}
#[test]
fn test_extern_paritysi2() {
init_x86_compiler_rt(true);
assert_eq!(__paritysi2(0b1), 1);
}
#[test]
fn test_extern_paritydi2() {
init_x86_compiler_rt(true);
assert_eq!(__paritydi2(0b1), 1);
}
#[test]
fn test_extern_bswapsi2() {
init_x86_compiler_rt(true);
assert_eq!(__bswapsi2(0x12345678), 0x78563412);
}
#[test]
fn test_extern_bswapdi2() {
init_x86_compiler_rt(true);
assert_eq!(__bswapdi2(0x0123456789ABCDEF), 0xEFCDAB8967452301);
}
#[test]
fn test_extern_addvsi3() {
init_x86_compiler_rt(true);
assert_eq!(__addvsi3(10, 20), 30);
}
#[test]
fn test_extern_subvsi3() {
init_x86_compiler_rt(true);
assert_eq!(__subvsi3(30, 10), 20);
}
#[test]
fn test_extern_mulvsi3() {
init_x86_compiler_rt(true);
assert_eq!(__mulvsi3(3, 4), 12);
}
#[test]
fn test_extern_negvsi2() {
init_x86_compiler_rt(true);
assert_eq!(__negvsi2(42), -42);
}
#[test]
fn test_extern_addvdi3() {
init_x86_compiler_rt(true);
assert_eq!(__addvdi3(100, 200), 300);
}
#[test]
fn test_extern_subvdi3() {
init_x86_compiler_rt(true);
assert_eq!(__subvdi3(300, 100), 200);
}
#[test]
fn test_extern_mulvdi3() {
init_x86_compiler_rt(true);
assert_eq!(__mulvdi3(3, 4), 12);
}
#[test]
fn test_extern_negvdi2() {
init_x86_compiler_rt(true);
assert_eq!(__negvdi2(42), -42);
}
#[test]
fn test_extern_cxa_allocate_exception() {
init_x86_compiler_rt(true);
let exc = __cxa_allocate_exception(64);
assert!(!exc.is_null());
}
#[test]
fn test_extern_cxa_begin_catch() {
init_x86_compiler_rt(true);
let exc = __cxa_allocate_exception(16);
let result = __cxa_begin_catch(exc);
assert!(!result.is_null());
}
#[test]
fn test_extern_cxa_end_catch() {
init_x86_compiler_rt(true);
__cxa_end_catch();
}
#[test]
fn test_extern_cxa_guard_acquire_release() {
init_x86_compiler_rt(true);
let mut guard: u64 = 0;
assert_eq!(__cxa_guard_acquire(&mut guard), 1);
__cxa_guard_release(&mut guard);
assert_eq!(__cxa_guard_acquire(&mut guard), 0);
}
#[test]
fn test_extern_cxa_guard_abort() {
init_x86_compiler_rt(true);
let mut guard: u64 = 0;
__cxa_guard_acquire(&mut guard);
__cxa_guard_abort(&mut guard);
}
#[test]
fn test_extern_cxa_demangle() {
init_x86_compiler_rt(true);
let name = b"_Z3foov\0";
let mut status: i32 = -1;
let result = __cxa_demangle(
name.as_ptr(),
std::ptr::null_mut(),
std::ptr::null_mut(),
&mut status,
);
if !result.is_null() {
assert_eq!(status, 0);
}
}
#[test]
fn test_extern_gxx_personality_v0() {
init_x86_compiler_rt(true);
let mut ctx: u64 = 0;
let result = __gxx_personality_v0(
1,
1,
0,
std::ptr::null_mut(),
&mut ctx as *mut u64 as *mut u8,
);
assert_eq!(result, 8);
}
#[test]
fn test_extern_gcc_personality_v0() {
init_x86_compiler_rt(true);
let mut ctx: u64 = 0;
let result = __gcc_personality_v0(
1,
1,
0,
std::ptr::null_mut(),
&mut ctx as *mut u64 as *mut u8,
);
assert_eq!(result, 8);
}
#[test]
fn test_extern_unwind_raise_exception() {
init_x86_compiler_rt(true);
let exc = __cxa_allocate_exception(16);
let result = _Unwind_RaiseException(exc);
assert_eq!(result, 5);
}
#[test]
fn test_extern_unwind_forced_unwind() {
init_x86_compiler_rt(true);
let exc = __cxa_allocate_exception(16);
let result = _Unwind_ForcedUnwind(exc, None, std::ptr::null_mut());
assert_eq!(result, 5);
}
#[test]
fn test_extern_unwind_get_set_gr() {
init_x86_compiler_rt(true);
let mut ctx = [0u64; 32];
_Unwind_SetGR(ctx.as_mut_ptr() as *mut u8, 0, 42);
let val = _Unwind_GetGR(ctx.as_ptr() as *const u8, 0);
assert_eq!(val, 42);
}
#[test]
fn test_extern_unwind_get_set_ip() {
init_x86_compiler_rt(true);
let mut ctx = [0u8; 256];
_Unwind_SetIP(ctx.as_mut_ptr(), 0xCAFE_BABE);
let val = _Unwind_GetIP(ctx.as_ptr());
assert_eq!(val, 0xCAFE_BABE);
}
#[test]
fn test_extern_memcpy_chk() {
init_x86_compiler_rt(true);
let src = [1u8, 2, 3, 4];
let mut dest = [0u8; 4];
__memcpy_chk(dest.as_mut_ptr(), src.as_ptr(), 4, 4);
assert_eq!(dest, src);
}
#[test]
fn test_extern_memset_chk() {
init_x86_compiler_rt(true);
let mut dest = [0u8; 4];
__memset_chk(dest.as_mut_ptr(), 0xFF, 4, 4);
assert_eq!(dest, [0xFFu8; 4]);
}
#[test]
fn test_extern_strcpy_chk() {
init_x86_compiler_rt(true);
let src = b"hi\0";
let mut dest = [0u8; 10];
__strcpy_chk(dest.as_mut_ptr(), src.as_ptr(), 10);
assert_eq!(dest[0], b'h');
assert_eq!(dest[1], b'i');
assert_eq!(dest[2], 0);
}
#[test]
fn test_extern_strncpy_chk() {
init_x86_compiler_rt(true);
let src = b"hi\0";
let mut dest = [0u8; 10];
__strncpy_chk(dest.as_mut_ptr(), src.as_ptr(), 5, 10);
assert_eq!(dest[0], b'h');
assert_eq!(dest[1], b'i');
}
#[test]
fn test_extern_strcat_chk() {
init_x86_compiler_rt(true);
let mut dest = [0u8; 20];
dest[0] = b'a';
dest[1] = 0;
let src = b"b\0";
__strcat_chk(dest.as_mut_ptr(), src.as_ptr(), 20);
assert_eq!(dest[0], b'a');
assert_eq!(dest[1], b'b');
assert_eq!(dest[2], 0);
}
}
mod fuzz_style {
use super::*;
#[test]
fn test_add_vs_sub_consistency_f32() {
let rt = make_rt_x86_64();
let a = 3.14f32;
let b = 1.41f32;
let sum = rt.addsf3(a, b);
let diff = rt.subsf3(sum, b);
assert!(approx_eq_f32(diff, a, 1e-4));
}
#[test]
fn test_add_vs_sub_consistency_f64() {
let rt = make_rt_x86_64();
let a = 3.141592653589793;
let b = 1.4142135623730951;
let sum = rt.adddf3(a, b);
let diff = rt.subdf3(sum, b);
assert!(approx_eq_f64(diff, a, 1e-10));
}
#[test]
fn test_mul_vs_div_consistency_f32() {
let rt = make_rt_x86_64();
let a = 5.0f32;
let b = 2.0f32;
let prod = rt.mulsf3(a, b);
let quot = rt.divsf3(prod, b);
assert!(approx_eq_f32(quot, a, 1e-4));
}
#[test]
fn test_mul_vs_div_consistency_f64() {
let rt = make_rt_x86_64();
let a = 5.0;
let b = 2.0;
let prod = rt.muldf3(a, b);
let quot = rt.divdf3(prod, b);
assert!(approx_eq_f64(quot, a, 1e-10));
}
#[test]
fn test_div_vs_mul_identity_f32() {
let rt = make_rt_x86_64();
let x = 3.0f32;
let inv = rt.divsf3(1.0, x);
let prod = rt.mulsf3(x, inv);
assert!(approx_eq_f32(prod, 1.0, 1e-4));
}
#[test]
fn test_div_vs_mul_identity_f64() {
let rt = make_rt_x86_64();
let x = 3.0;
let inv = rt.divdf3(1.0, x);
let prod = rt.muldf3(x, inv);
assert!(approx_eq_f64(prod, 1.0, 1e-10));
}
#[test]
fn test_neg_distributive_f32() {
let rt = make_rt_x86_64();
let a = 3.0f32;
let b = 2.0f32;
let sum = rt.addsf3(a, b);
let neg_sum = rt.negsf2(sum);
let neg_a = rt.negsf2(a);
let neg_b = rt.negsf2(b);
let sum_neg = rt.addsf3(neg_a, neg_b);
assert!(approx_eq_f32(neg_sum, sum_neg, 1e-6));
}
#[test]
fn test_neg_distributive_f64() {
let rt = make_rt_x86_64();
let a = 3.0;
let b = 2.0;
let sum = rt.adddf3(a, b);
let neg_sum = rt.negdf2(sum);
let neg_a = rt.negdf2(a);
let neg_b = rt.negdf2(b);
let sum_neg = rt.adddf3(neg_a, neg_b);
assert!(approx_eq_f64(neg_sum, sum_neg, 1e-12));
}
#[test]
fn test_shift_add_relationship() {
let rt = make_rt_x86_64();
let x = 3i64;
let shifted = rt.ashldi3(x, 1);
let multiplied = rt.muldi3(x, 2);
assert_eq!(shifted, multiplied);
}
#[test]
fn test_shift_sub_relationship() {
let rt = make_rt_x86_64();
let x = 16i64;
let shifted = rt.ashrdi3(x, 1);
let divided = rt.divdi3(x, 2);
assert_eq!(shifted, divided);
}
#[test]
fn test_bswap_clz_relationship() {
let rt = make_rt_x86_64();
let val = 0x0000_1000u32;
let swapped = rt.bswapsi2(val);
let clz_orig = rt.clzsi2(val);
let clz_swapped = rt.clzsi2(swapped);
assert!(clz_orig >= 0 && clz_orig <= 32);
assert!(clz_swapped >= 0 && clz_swapped <= 32);
}
#[test]
fn test_fabs_f64_squared() {
let rt = make_rt_x86_64();
let x = -3.0;
let abs_x = rt.fabs_f64(x);
let square = rt.muldf3(abs_x, abs_x);
assert!(approx_eq_f64(square, 9.0, 1e-12));
}
#[test]
fn test_copysign_identity() {
let rt = make_rt_x86_64();
let x = 5.0;
let neg = -5.0;
assert_eq!(rt.copysign_f64(x, x), x);
assert_eq!(rt.copysign_f64(x, neg), neg);
assert_eq!(rt.copysign_f64(neg, x), x);
assert_eq!(rt.copysign_f64(neg, neg), neg);
}
#[test]
fn test_floor_ceil_relationship() {
let rt = make_rt_x86_64();
let x = 3.7;
let ceil_neg = rt.ceil_f64(-x);
let neg_ceil = rt.negdf2(ceil_neg);
let floor = rt.floor_f64(x);
assert!(approx_eq_f64(neg_ceil, floor, 1e-12));
}
#[test]
fn test_fmin_fmax_relationship() {
let rt = make_rt_x86_64();
let a = 1.0;
let b = 9.0;
let min = rt.fmin_f64(a, b);
let max = rt.fmax_f64(a, b);
assert_eq!(min, 1.0);
assert_eq!(max, 9.0);
let sum_mm = rt.adddf3(min, max);
let sum_ab = rt.adddf3(a, b);
assert!(approx_eq_f64(sum_mm, sum_ab, 1e-12));
}
#[test]
fn test_exp_log_roundtrip_range() {
let rt = make_rt_x86_64();
for &x in &[0.1, 0.5, 1.0, 2.0, 5.0, 10.0] {
let e = rt.exp_f64(x);
let l = rt.log_f64(e);
assert!(
approx_eq_f64(l, x, 1e-6),
"exp-log roundtrip failed for {}",
x
);
}
}
#[test]
fn test_asin_sin_roundtrip() {
let rt = make_rt_x86_64();
for &x in &[0.0, 0.2, 0.5, 0.8] {
let s = rt.sin_f64(x);
let a = rt.asin_f64(s);
assert!(
approx_eq_f64(a, x, 1e-4),
"sin-asin roundtrip failed for {}",
x
);
}
}
#[test]
fn test_multiple_int_div_and_mod_consistency() {
let rt = make_rt_x86_64();
for (a, b) in &[(10, 3), (100, 7), (64, 8), (i64::MAX, 2), (i64::MIN, 1)] {
let q = rt.divdi3(*a, *b);
let r = rt.moddi3(*a, *b);
let qb = rt.muldi3(q, *b);
let recomputed = qb.wrapping_add(r);
assert!(
recomputed == *a || *a == i64::MIN && *b == -1,
"div/mod consistency: {} / {} gave q={} r={}, q*b+r={}",
a,
b,
q,
r,
recomputed
);
}
}
}
mod global_init {
use super::*;
#[test]
fn test_init_x86_64_creates_global() {
init_x86_compiler_rt(true);
let rt = global_rt();
assert!(rt.is_64bit);
}
#[test]
fn test_init_i386_creates_global() {
init_x86_compiler_rt(false);
let rt = global_rt();
assert!(!rt.is_64bit);
}
#[test]
fn test_init_twice_does_not_panic() {
init_x86_compiler_rt(true);
init_x86_compiler_rt(true);
}
}
mod edge_cases {
use super::*;
#[test]
fn test_neg_i128_zero() {
let rt = make_rt_x86_64();
let (hi, lo) = rt.neg_i128(0, 0);
assert_eq!(hi, 0);
assert_eq!(lo, 0);
}
#[test]
fn test_neg_i128_max() {
let rt = make_rt_x86_64();
let (hi, lo) = rt.neg_i128(u64::MAX, u64::MAX);
assert_eq!(hi, 0);
assert_eq!(lo, 1);
}
#[test]
fn test_neg_i128_one() {
let rt = make_rt_x86_64();
let (hi, lo) = rt.neg_i128(0, 1);
assert_eq!(hi, -1i64 as u64 as i64);
assert_eq!(lo, u64::MAX);
}
#[test]
fn test_neg_i128_power_of_two() {
let rt = make_rt_x86_64();
let (hi, lo) = rt.neg_i128(0, 1u64 << 63);
assert_eq!(hi, -1i64 as u64 as i64);
assert_eq!(lo, 1u64 << 63);
}
#[test]
fn test_umulti3_zero() {
let rt = make_rt_x86_64();
let (p_hi, p_lo) = rt.umulti3(0, 0, 0, 0);
assert_eq!(p_hi, 0);
assert_eq!(p_lo, 0);
}
#[test]
fn test_umulti3_identity() {
let rt = make_rt_x86_64();
let (p_hi, p_lo) = rt.umulti3(0, 42, 0, 1);
assert_eq!(p_hi, 0);
assert_eq!(p_lo, 42);
}
#[test]
fn test_ashrti3_negative_full() {
let rt = make_rt_x86_64();
let (hi, lo) = rt.ashrti3(u64::MAX, u64::MAX, 10);
assert_eq!(hi, u64::MAX);
assert_eq!(lo, u64::MAX);
}
#[test]
fn test_ashrti3_positive_to_zero() {
let rt = make_rt_x86_64();
let (hi, lo) = rt.ashrti3(0, 1, 2);
assert_eq!(hi, 0);
assert_eq!(lo, 0);
}
#[test]
fn test_ashlti3_overflow_to_zero() {
let rt = make_rt_x86_64();
let (hi, lo) = rt.ashlti3(1, 0, 128);
assert_eq!(hi, 0);
assert_eq!(lo, 0);
}
#[test]
fn test_lshrti3_all_ones_to_zero() {
let rt = make_rt_x86_64();
let (hi, lo) = rt.lshrti3(u64::MAX, u64::MAX, 128);
assert_eq!(hi, 0);
assert_eq!(lo, 0);
}
#[test]
fn test_lshrti3_mid_shift() {
let rt = make_rt_x86_64();
let (hi, lo) = rt.lshrti3(1, 0, 64);
assert_eq!(hi, 0);
assert_eq!(lo, 1);
}
#[test]
fn test_udivmodti4_raw_self_division() {
let rt = make_rt_x86_64();
let (q_hi, q_lo, r_hi, r_lo) = rt.udivmodti4_raw(0, 10, 0, 10);
assert_eq!(q_hi, 0);
assert_eq!(q_lo, 1);
assert_eq!(r_hi, 0);
assert_eq!(r_lo, 0);
}
#[test]
fn test_udivmodti4_raw_larger_dividend() {
let rt = make_rt_x86_64();
let (q_hi, q_lo, r_hi, r_lo) = rt.udivmodti4_raw(0, 100, 0, 30);
assert_eq!(q_hi, 0);
assert_eq!(q_lo, 3);
assert_eq!(r_hi, 0);
assert_eq!(r_lo, 10);
}
#[test]
fn test_udivmodti4_raw_zero_divisor() {
let rt = make_rt_x86_64();
let (q_hi, q_lo, r_hi, r_lo) = rt.udivmodti4_raw(0, 10, 0, 0);
assert_eq!(q_hi, 0);
assert_eq!(q_lo, 0);
assert_eq!(r_hi, 0);
assert_eq!(r_lo, 0);
}
#[test]
fn test_divti3_both_negative() {
let rt = make_rt_x86_64();
let (q_hi, q_lo) = rt.divti3(-1, (-10i64) as u64, -1, (-3i64) as u64);
assert_eq!(q_hi, 0);
assert_eq!(q_lo, 3);
}
#[test]
fn test_f32_is_nan_edge_cases() {
assert!(f32_is_nan(f32::NAN));
assert!(!f32_is_nan(0.0));
assert!(!f32_is_nan(f32::MAX));
assert!(!f32_is_nan(f32::MIN));
assert!(!f32_is_nan(f32::NEG_INFINITY));
assert!(!f32_is_nan(f32::INFINITY));
}
#[test]
fn test_f64_is_nan_edge_cases() {
assert!(f64_is_nan(f64::NAN));
assert!(!f64_is_nan(0.0));
assert!(!f64_is_nan(f64::MAX));
assert!(!f64_is_nan(f64::MIN));
assert!(!f64_is_nan(f64::NEG_INFINITY));
assert!(!f64_is_nan(f64::INFINITY));
}
#[test]
fn test_f32_is_nan_or_inf() {
assert!(f32_is_nan_or_inf(f32::NAN));
assert!(f32_is_nan_or_inf(f32::INFINITY));
assert!(f32_is_nan_or_inf(f32::NEG_INFINITY));
assert!(!f32_is_nan_or_inf(0.0));
assert!(!f32_is_nan_or_inf(1.0));
}
#[test]
fn test_f64_is_nan_or_inf() {
assert!(f64_is_nan_or_inf(f64::NAN));
assert!(f64_is_nan_or_inf(f64::INFINITY));
assert!(f64_is_nan_or_inf(f64::NEG_INFINITY));
assert!(!f64_is_nan_or_inf(0.0));
assert!(!f64_is_nan_or_inf(1.0));
}
}
mod property_based {
use super::*;
#[test]
fn test_divdi3_recover_with_mul_and_mod() {
let rt = make_rt_x86_64();
let test_cases = [
(10i64, 3i64),
(100i64, 7i64),
(-10i64, 3i64),
(10i64, -3i64),
(-100i64, -7i64),
(0i64, 5i64),
(i64::MAX, 1i64),
(i64::MIN, 2i64),
];
for (a, b) in &test_cases {
if *b == 0 {
continue;
}
let q = rt.divdi3(*a, *b);
let r = rt.moddi3(*a, *b);
let recomputed = q.wrapping_mul(*b).wrapping_add(r);
if *a == i64::MIN && *b == -1 {
continue;
}
assert_eq!(
recomputed, *a,
"div/mod: a={}, b={}, q={}, r={}",
a, b, q, r
);
}
}
#[test]
fn test_bswap_twice_is_identity_si2() {
let rt = make_rt_x86_64();
for val in &[0u32, 1, 0x8000_0000, 0x1234_5678, 0xFFFF_FFFF] {
assert_eq!(rt.bswapsi2(rt.bswapsi2(*val)), *val);
}
}
#[test]
fn test_bswap_twice_is_identity_di2() {
let rt = make_rt_x86_64();
for val in &[
0u64,
1,
0x8000_0000_0000_0000,
0x0123_4567_89AB_CDEF,
u64::MAX,
] {
assert_eq!(rt.bswapdi2(rt.bswapdi2(*val)), *val);
}
}
#[test]
fn test_clz_plus_ctz_less_or_equal_bits() {
let rt = make_rt_x86_64();
for shift in 0..32 {
let val = 1u32 << shift;
let clz = rt.clzsi2(val) as u32;
let ctz = rt.ctzsi2(val) as u32;
assert_eq!(ctz, shift);
assert!(clz + ctz <= 31);
}
}
#[test]
fn test_popcount_parity_relationship() {
let rt = make_rt_x86_64();
for val in &[0u32, 1, 2, 3, 0xFFFF, 0x12345678, u32::MAX] {
let pc = rt.popcountsi2(*val);
let parity = rt.paritysi2(*val);
assert_eq!(parity, pc & 1);
}
}
#[test]
fn test_fabs_then_copysign_restores_sign() {
let rt = make_rt_x86_64();
for x in &[-3.0, -1.0, -0.0, 0.0, 1.0, 3.0] {
let abs = rt.fabs_f64(*x);
let restored = rt.copysign_f64(abs, *x);
assert_eq!(restored.to_bits(), x.to_bits());
}
}
#[test]
fn test_floor_always_le_value() {
let rt = make_rt_x86_64();
for x in &[-3.7, -1.0, -0.5, 0.0, 0.5, 1.0, 3.7] {
let floor = rt.floor_f64(*x);
assert!(floor <= *x, "floor({}) = {} > {}", x, floor, x);
}
}
#[test]
fn test_ceil_always_ge_value() {
let rt = make_rt_x86_64();
for x in &[-3.7, -1.0, -0.5, 0.0, 0.5, 1.0, 3.7] {
let ceil = rt.ceil_f64(*x);
assert!(ceil >= *x, "ceil({}) = {} < {}", x, ceil, x);
}
}
#[test]
fn test_trunc_preserves_sign() {
let rt = make_rt_x86_64();
for x in &[-3.7, -0.5, 0.5, 3.7] {
let t = rt.trunc_f64(*x);
assert_eq!(t.is_sign_positive(), x.is_sign_positive());
}
}
#[test]
fn test_roundis_symmetric() {
let rt = make_rt_x86_64();
for x in &[-3.6, -3.4, 3.4, 3.6] {
let r = rt.round_f64(*x);
let nr = rt.round_f64(-*x);
assert!(approx_eq_f64(r, -nr, 1e-12), "round asymmetry for {}", x);
}
}
}
}