Everything in C

This commit is contained in:
Renar Narubin
2021-12-16 17:43:52 -08:00
parent d6c6477977
commit 63647ad28d
8 changed files with 365 additions and 288 deletions

View File

@@ -18,28 +18,19 @@ name = "math"
harness = false
[features]
default = [
"num-traits",
"finite-math-only",
"associative-math",
"reciprocal-math",
"no-signed-zeros",
"no-trapping-math",
"fp-contract-fast",
"approx-func",
]
default = ["num-traits"]
# default fast-math features
finite-math-only = []
associative-math = []
reciprocal-math = []
no-signed-zeros = []
no-trapping-math = []
fp-contract-fast = []
approx-func = []
# disable-able fast-math features
no-finite-math-only = []
no-associative-math = []
no-reciprocal-math = []
signed-zeros = []
trapping-math = []
fp-contract-on = []
no-approx-func = []
math-errno = []
# non-default fast-math-like features
denormal-fp-math-preserve-sign = []
# TODO denormal-fp-math? can have cpu-wide consequences
# optional trait implementations
nalgebra-v021 = ["num-traits", "nalgebra_v021", "simba_v01", "approx_v03"]

View File

@@ -6,6 +6,7 @@ fn main() {
builder.compiler("clang");
}
builder.warnings_into_errors(true);
builder.flag("-flto=thin");
build_ll(builder.clone());
@@ -21,32 +22,49 @@ fn build_ll(mut builder: cc::Build) {
}
fn build_c(mut builder: cc::Build) {
builder.flag("-O3");
builder.opt_level(3);
#[cfg(feature = "finite-math-only")]
builder.flag("-ffinite-math-only");
#[cfg(feature = "associative-math")]
#[cfg(not(feature = "no-associative-math"))]
builder.flag("-fassociative-math");
#[cfg(feature = "reciprocal-math")]
#[cfg(not(feature = "no-reciprocal-math"))]
builder.flag("-freciprocal-math");
#[cfg(feature = "no-signed-zeros")]
#[cfg(not(feature = "signed-zeros"))]
builder.flag("-fno-signed-zeros");
#[cfg(feature = "no-trapping-math")]
#[cfg(not(feature = "trapping-math"))]
builder.flag("-fno-trapping-math");
#[cfg(feature = "fp-contract-fast")]
#[cfg(not(feature = "fp-contract-on"))]
builder.flag("-ffp-contract=fast");
// TODO figure out if this works
//#[cfg(feature = "approx-func")]
//builder.flag("-Xclang -fapprox-func");
// -fapprox-func isn't currently available in the driver, but it is in clang itself
// https://reviews.llvm.org/D106191
#[cfg(not(feature = "no-approx-func"))]
builder.flag("-Xclang").flag("-fapprox-func");
#[cfg(feature = "denormal-fp-math-preserve-sign")]
builder.flag("-fdenormal-fp-math=preserve-sign");
#[cfg(not(feature = "math-errno"))]
builder.flag("-fno-math-errno");
builder.file("src/math/math.c").compile("math")
// poison_unsafe must be compiled without finite-math-only
// see its docs for details
poison_unsafe(builder.clone());
#[cfg(not(feature = "no-finite-math-only"))]
builder.flag("-ffinite-math-only");
poison_safe(builder);
}
fn poison_unsafe(mut builder: cc::Build) {
builder
.file("src/math/poison_unsafe.c")
.compile("poison_unsafe")
}
fn poison_safe(mut builder: cc::Build) {
builder
.file("src/math/poison_safe.c")
.compile("poison_safe")
}

View File

@@ -64,29 +64,19 @@ impl std::error::Error for InvalidValueError {}
// values have a relatively consistent behavior (stuff like transitivity), defined cases for UB,
// and importantly can be limited in scope by freezing to a fixed value.
//
// This library handles poison by limiting its reach to only the pure arithmetic operations on the
// wrapper float types. Any arbitrary FF32 is considered possibly invalid (containing +-inf or NaN)
// because it's not feasible to track validity (without running all operations in parallel with
// unfast-math and thus negating any possible improvement). Float add/sub/mul/div/rem are permitted
// on the possibly poison values (as documented by LLVM), producing transitively poison results,
// then wrapped in FF32. Any other operations require the value to be not-poison in order to be
// not-UB: anything like comparison/printing/conversion/casting/etc is done on frozen copies of
// the data. Originating values that were valid will pass through the arithmetic and freezing
// exactly as they are; invalid values will become poison through the arithmetic and then be frozen
// to some unspecified value. The user may encounter garbage in such a case, but not in a way that
// triggers UB.
// FIXME more docs
//
// Prior art and references
//
// https://github.com/rust-lang/rust/issues/21690
// Task for general purpose fast-math in rust lang. Discussions about the right approach
// and generalizability, including whether it should be type-based or annotation based. fast_fp
// uses types wrapping intrinsics because it's the only option available in user space, and gets
// good optimizations useful in practice
// uses types because it's the only option available in user space, and gets good optimizations
// useful in practice
//
// https://docs.rs/fast-floats/0.2.0/fast_floats/index.html
// Another crate that wraps fast intrinsics in types. They didn't address poison propagation,
// leaving constructors unsafe
// A crate that wraps fast intrinsics in types. Intrinsics only apply to basic ops, and they didn't
// address poison propagation, leaving constructors unsafe
//
// https://llvm.org/docs/LangRef.html#fast-math-flags
// LLVM's documentation on fast-math
@@ -227,8 +217,17 @@ macro_rules! impls {
const ONE: $fast_ty = <$fast_ty>::new(1.0);
const ZERO: $fast_ty = <$fast_ty>::new(0.0);
/// The smallest finite value
pub const MIN: $fast_ty = <$fast_ty>::new($base_ty::MIN);
/// The smallest positive value
pub const MIN_POSITIVE: $fast_ty = <$fast_ty>::new($base_ty::MIN_POSITIVE);
/// The largest finite value
pub const MAX: $fast_ty = <$fast_ty>::new($base_ty::MAX);
#[doc = "Create a new `"]
#[doc= stringify!($fast_ty)]
#[doc = stringify!($fast_ty)]
#[doc = "` instance from the given float value."]
///
/// The given value **MUST NOT** be infinite or NaN, and any operations involving this value must
@@ -239,7 +238,7 @@ macro_rules! impls {
}
#[doc = "Create a new `"]
#[doc= stringify!($fast_ty)]
#[doc = stringify!($fast_ty)]
#[doc = "` instance from the given float value, returning an error if the value is infinite or NaN."]
///
/// Note that this check is **not sufficient** to avoid all unspecified outputs, because an
@@ -261,57 +260,12 @@ macro_rules! impls {
self.0.freeze()
}
// TODO migrate these to native implementations to freeze less and fast-math more
forward_freeze_self! {
$fast_ty, $base_ty
pub fn acos(self) -> Self;
pub fn acosh(self) -> Self;
pub fn asin(self) -> Self;
pub fn asinh(self) -> Self;
pub fn atan(self) -> Self;
pub fn atan2(self, other: Self) -> Self;
pub fn atanh(self) -> Self;
pub fn cbrt(self) -> Self;
pub fn ceil(self) -> Self;
pub fn clamp(self, min: Self, max: Self) -> Self;
pub fn cos(self) -> Self;
pub fn cosh(self) -> Self;
pub fn div_euclid(self, rhs: Self) -> Self;
pub fn exp(self) -> Self;
pub fn exp2(self) -> Self;
pub fn exp_m1(self) -> Self;
pub fn floor(self) -> Self;
pub fn fract(self) -> Self;
pub fn ln(self) -> Self;
pub fn ln_1p(self) -> Self;
pub fn log(self, base: Self) -> Self;
pub fn log10(self) -> Self;
pub fn log2(self) -> Self;
//pub fn max(self, other: Self) -> Self;
//pub fn min(self, other: Self) -> Self;
pub fn mul_add(self, a: Self, b: Self) -> Self;
pub fn powf(self, n: Self) -> Self;
pub fn rem_euclid(self, rhs: Self) -> Self;
pub fn round(self) -> Self;
pub fn sin(self) -> Self;
pub fn sinh(self) -> Self;
//pub fn sqrt(self) -> Self;
pub fn tan(self) -> Self;
pub fn tanh(self) -> Self;
pub fn to_degrees(self) -> Self;
pub fn to_radians(self) -> Self;
pub fn trunc(self) -> Self;
}
#[inline]
pub fn powi(self, n: i32) -> Self {
<$fast_ty>::new(self.freeze_raw().powi(n))
}
#[inline]
pub fn sin_cos(self) -> (Self, Self) {
let (sin, cos) = self.freeze_raw().sin_cos();
(<$fast_ty>::new(sin), <$fast_ty>::new(cos))
}
#[inline]
@@ -347,14 +301,40 @@ macro_rules! impls {
self.classify() == FpCategory::Subnormal
}
/// The smallest finite value
pub const MIN: $fast_ty = <$fast_ty>::new($base_ty::MIN);
#[inline]
pub fn hypot(self, other: Self) -> Self {
(self * self + other * other).sqrt()
}
/// The smallest positive value
pub const MIN_POSITIVE: $fast_ty = <$fast_ty>::new($base_ty::MIN_POSITIVE);
#[inline]
pub fn signum(self) -> Self {
Self::ONE.copysign(self)
}
/// The largest finite value
pub const MAX: $fast_ty = <$fast_ty>::new($base_ty::MAX);
#[inline]
pub fn recip(self) -> Self {
Self::ONE / self
}
#[inline]
pub fn fract(self) -> Self {
self - self.trunc()
}
#[inline]
pub fn log(self, base: Self) -> Self {
self.ln() / base.ln()
}
#[inline]
pub fn mul_add(self, mul: Self, add: Self) -> Self {
self * mul + add
}
#[inline]
pub fn sin_cos(self) -> (Self, Self) {
(self.sin(), self.cos())
}
}
impl_fmt! {
@@ -417,17 +397,12 @@ macro_rules! impls {
}
}
impl Eq for $fast_ty {}
impl PartialOrd<$fast_ty> for $fast_ty {
#[inline(always)]
fn partial_cmp(&self, other: &$fast_ty) -> Option<cmp::Ordering> {
Some(self.cmp(other))
<$base_ty>::partial_cmp(&self.freeze_raw(), &other.freeze_raw())
}
// TODO specialize a MaybePoison<bool> with `x & 0b1`?
// then comparisons can freeze only once on output instead of twice on input
#[inline(always)]
fn lt(&self, other: &$fast_ty) -> bool {
self.freeze_raw() < other.freeze_raw()
@@ -449,6 +424,9 @@ macro_rules! impls {
}
}
// FIXME feature conditional Eq/Ord
impl Eq for $fast_ty {}
impl Ord for $fast_ty {
#[inline(always)]
fn cmp(&self, other: &$fast_ty) -> cmp::Ordering {

View File

@@ -1,81 +1,7 @@
use crate::{poison::MaybePoison, FF32, FF64};
use crate::{FF32, FF64};
use core::ops::{Add, Div, Mul, Neg, Rem, Sub};
use paste::paste;
impl FF32 {
const SIGN_BIT: u32 = 0x8000_0000;
const UNSIGNED_MASK: u32 = 0x7fff_ffff;
}
impl FF64 {
const SIGN_BIT: u64 = 0x8000_0000_0000_0000;
const UNSIGNED_MASK: u64 = 0x7fff_ffff_ffff_ffff;
}
macro_rules! impl_generic_math {
($fast_ty:ident, $base_ty:ident, $base_int:ident) => {
impl $fast_ty {
#[inline]
fn to_bits(self) -> MaybePoison<$base_int> {
// Safety:
//
// - `to_bits` should be valid for any input bits
// - poison propagation is controlled with MaybePoison
MaybePoison::new(unsafe { <$base_ty>::to_bits(self.0.maybe_poison()) })
}
#[inline]
fn from_bits(bits: MaybePoison<$base_int>) -> Self {
// Safety:
//
// - `from_bits` should be valid for any input bits
// - poison propagation is controlled with MaybePoison
Self(MaybePoison::new(unsafe {
<$base_ty>::from_bits(bits.maybe_poison())
}))
}
#[inline]
pub fn abs(self) -> Self {
let bits = self.to_bits();
<$fast_ty>::from_bits(MaybePoison::new(unsafe {
bits.maybe_poison() & Self::UNSIGNED_MASK
}))
}
#[inline]
pub fn copysign(self, other: Self) -> Self {
let this = self.to_bits();
let that = other.to_bits();
// Safety:
//
// - & of poison is safe because & does not produce UB for any input values
// - poison propagation is handled by wrapping in maybe poison
<$fast_ty>::from_bits(MaybePoison::new(unsafe {
(this.maybe_poison() & Self::UNSIGNED_MASK)
| (that.maybe_poison() & Self::SIGN_BIT)
}))
}
#[inline]
pub fn hypot(self, other: Self) -> Self {
(self * self + other * other).sqrt()
}
#[inline]
pub fn signum(self) -> Self {
Self::ONE.copysign(self)
}
#[inline]
pub fn recip(self) -> Self {
Self::ONE / self
}
}
};
}
macro_rules! impl_binary_refs {
($lhs:ident, $rhs:ident, $op_trait:ident, $op_fn:ident) => {
impl $op_trait<$rhs> for &$lhs {
@@ -142,22 +68,124 @@ macro_rules! impl_fast_ops {
};
}
macro_rules! poison_safe_fns {
($fast_ty:ident, $base_ty:ident:
$(fn $fn:ident(self $(, $arg:ident : Self)*) -> Self;)*) => {
paste! {
$(
#[link(name = "poison_safe")]
extern "C" {
// functions in the poison_safe lib can accept poison args.
// because the fast types are (transitively) repr(transparent) over the
// primitive type, we can pass them directly over FFI
fn [<$fn _ $base_ty>](a: $fast_ty $(, $arg: $fast_ty)*) -> $fast_ty;
}
impl $fast_ty {
#[inline]
pub fn $fn(self $(, $arg: Self)*) -> Self {
unsafe { [<$fn _ $base_ty>](self $(, $arg)*) }
}
}
)*
}
}
}
macro_rules! poison_unsafe_fns {
($fast_ty:ident, $base_ty:ident:
$(fn $fn:ident(self $(, $arg:ident : Self)*) -> Self;)*) => {
paste! {
$(
#[link(name = "poison_unsafe")]
extern "C" {
// functions in the poison_unsafe lib must have their arguments frozen, which
// is best expressed as accepting the base type instead of the fast type
fn [<$fn _ $base_ty>](a: $base_ty $(, $arg: $base_ty)*) -> $fast_ty;
}
impl $fast_ty {
#[inline]
pub fn $fn(self $(, $arg: Self)*) -> Self {
unsafe { [<$fn _ $base_ty>](self.freeze_raw() $(, $arg.freeze_raw())*) }
}
}
)*
}
}
}
macro_rules! impl_extern_math {
($fast_ty:ident, $base_ty:ident) => {
poison_safe_fns! {
$fast_ty, $base_ty:
fn abs(self) -> Self;
fn copysign(self, other: Self) -> Self;
fn max(self, other: Self) -> Self;
fn min(self, other: Self) -> Self;
}
poison_unsafe_fns! {
$fast_ty, $base_ty:
fn acos(self) -> Self;
fn acosh(self) -> Self;
fn asin(self) -> Self;
fn asinh(self) -> Self;
fn atan(self) -> Self;
fn atan2(self, other: Self) -> Self;
fn atanh(self) -> Self;
fn cbrt(self) -> Self;
fn ceil(self) -> Self;
fn cos(self) -> Self;
fn cosh(self) -> Self;
fn exp(self) -> Self;
fn exp2(self) -> Self;
fn floor(self) -> Self;
fn exp_m1(self) -> Self;
fn ln(self) -> Self;
fn ln_1p(self) -> Self;
fn log2(self) -> Self;
fn log10(self) -> Self;
fn powf(self, n: Self) -> Self;
fn round(self) -> Self;
fn sin(self) -> Self;
fn sinh(self) -> Self;
fn sqrt(self) -> Self;
fn tan(self) -> Self;
fn tanh(self) -> Self;
fn trunc(self) -> Self;
}
paste! {
#[link(name = "poison_safe")]
extern "C" {
fn [<add_ $base_ty>](a: $fast_ty, b: $fast_ty) -> $fast_ty;
fn [<sub_ $base_ty>](a: $fast_ty, b: $fast_ty) -> $fast_ty;
fn [<mul_ $base_ty>](a: $fast_ty, b: $fast_ty) -> $fast_ty;
fn [<div_ $base_ty>](a: $fast_ty, b: $fast_ty) -> $fast_ty;
fn [<rem_ $base_ty>](a: $fast_ty, b: $fast_ty) -> $fast_ty;
fn [<neg_ $base_ty>](a: $fast_ty) -> $fast_ty;
fn [<min_ $base_ty>](a: $fast_ty, b: $fast_ty) -> $fast_ty;
fn [<max_ $base_ty>](a: $fast_ty, b: $fast_ty) -> $fast_ty;
fn [<clamp_ $base_ty>](a: $fast_ty, min: $fast_ty, max: $fast_ty) -> $fast_ty;
fn [<powi_ $base_ty>](a: $fast_ty, b: i32) -> $fast_ty;
}
fn [<sqrt_ $base_ty>](a: $fast_ty) -> $fast_ty;
#[link(name = "poison_unsafe")]
extern "C" {
fn [<rem_ $base_ty>](a: $base_ty, b: $base_ty) -> $fast_ty;
}
// a few functions are special cases and aren't defined in submacros
impl $fast_ty {
#[inline]
pub fn clamp(self, min: Self, max: Self) -> Self {
assert!(min <= max);
unsafe { [<clamp_ $base_ty>](self, min, max) }
}
#[inline]
pub fn powi(self, n: i32) -> Self {
unsafe { [<powi_ $base_ty>](self, n) }
}
}
impl_fast_ops! {
@@ -166,7 +194,6 @@ macro_rules! impl_extern_math {
Sub, sub, [<sub_ $base_ty>],
Mul, mul, [<mul_ $base_ty>],
Div, div, [<div_ $base_ty>],
Rem, rem, [<rem_ $base_ty>],
}
impl Neg for $fast_ty {
@@ -187,28 +214,39 @@ macro_rules! impl_extern_math {
}
}
impl $fast_ty {
#[inline]
pub fn max(self, other: Self) -> Self {
unsafe { [<max_ $base_ty>](self, other) }
}
impl Rem <$fast_ty> for $fast_ty {
type Output = $fast_ty;
#[inline]
pub fn min(self, other: Self) -> Self {
unsafe { [<min_ $base_ty>](self, other) }
}
#[inline]
pub fn sqrt(self) -> Self {
unsafe { [<sqrt_ $base_ty>](self) }
#[inline(always)]
fn rem(self, other: $fast_ty) -> Self::Output {
unsafe { [<rem_ $base_ty>](self.freeze_raw(), other.freeze_raw()) }
}
}
impl Rem <$base_ty> for $fast_ty {
type Output = $fast_ty;
#[inline(always)]
fn rem(self, other: $base_ty) -> Self::Output {
unsafe { [<rem_ $base_ty>](self.freeze_raw(), other) }
}
}
impl Rem <$fast_ty> for $base_ty {
type Output = $fast_ty;
#[inline(always)]
fn rem(self, other: $fast_ty) -> Self::Output {
unsafe { [<rem_ $base_ty>](self, other.freeze_raw()) }
}
}
impl_binary_refs! { $fast_ty, $fast_ty, Rem, rem }
impl_binary_refs! { $fast_ty, $base_ty, Rem, rem }
impl_binary_refs! { $base_ty, $fast_ty, Rem, rem }
}
};
}
impl_generic_math! { FF32, f32, u32 }
impl_generic_math! { FF64, f64, u64 }
impl_extern_math! { FF32, f32 }
impl_extern_math! { FF64, f64 }

View File

@@ -1,4 +1,12 @@
#include <stdbool.h>
/*
* The functions in this file are ones which can safely accept poison values in
* their input arguments without triggering any UB[1]. Because they can accept
* poison values, any fast-math optimizations are valid for this file, and rust
* code can still safely call it without precautions like freezing.
*
* [1]: https://llvm.org/docs/LangRef.html#poison-values
*/
#include <math.h>
#define IMPL_OPERATIONS(C_TYPE, RUST_TYPE) \
@@ -25,31 +33,6 @@
__attribute__((always_inline)) \
C_TYPE neg_ ## RUST_TYPE(C_TYPE a) { \
return -a; \
} \
\
__attribute__((always_inline)) \
bool eq_ ## RUST_TYPE(C_TYPE a, C_TYPE b) { \
return a == b; \
} \
\
__attribute__((always_inline)) \
bool lt_ ## RUST_TYPE(C_TYPE a, C_TYPE b) { \
return a < b; \
} \
\
__attribute__((always_inline)) \
bool le_ ## RUST_TYPE(C_TYPE a, C_TYPE b) { \
return a <= b; \
} \
\
__attribute__((always_inline)) \
bool gt_ ## RUST_TYPE(C_TYPE a, C_TYPE b) { \
return a > b; \
} \
\
__attribute__((always_inline)) \
bool ge_ ## RUST_TYPE(C_TYPE a, C_TYPE b) { \
return a >= b; \
} \
#define IMPL_UNARY_FUNCTION(C_TYPE, RUST_TYPE, FN_NAME, FN_IMPL) \
@@ -67,16 +50,49 @@
IMPL_OPERATIONS(float, f32)
IMPL_OPERATIONS(double, f64)
// FIXME sqrt is not poison safe on some targets
IMPL_UNARY_FUNCTION(float, f32, sqrt, sqrtf)
IMPL_UNARY_FUNCTION(double, f64, sqrt, sqrt)
IMPL_UNARY_FUNCTION(float, f32, abs, fabsf)
IMPL_UNARY_FUNCTION(double, f64, abs, fabs)
// FIXME mod is not poison safe, though LLVM frem is
IMPL_BINARY_FUNCTION(float, f32, rem, fmodf)
IMPL_BINARY_FUNCTION(double, f64, rem, fmod)
IMPL_BINARY_FUNCTION(float, f32, copysign, copysignf)
IMPL_BINARY_FUNCTION(double, f64, copysign, copysign)
IMPL_BINARY_FUNCTION(float, f32, max, fmaxf)
IMPL_BINARY_FUNCTION(double, f64, max, fmax)
IMPL_BINARY_FUNCTION(float, f32, min, fminf)
IMPL_BINARY_FUNCTION(double, f64, min, fmin)
__attribute__((always_inline))
float powi_f32(float a, int b) {
return __builtin_powif(a, b);
}
__attribute__((always_inline))
double powi_f64(double a, int b) {
return __builtin_powi(a, b);
}
__attribute__((always_inline))
float clamp_f32(float a, float min, float max) {
// under -O3 these comparisons are compiled to selects which, unlike
// branches, propagate poison without UB
if(a < min) {
a = min;
}
if(a > max) {
a = max;
}
return a;
}
__attribute__((always_inline))
double clamp_f64(double a, double min, double max) {
if(a < min) {
a = min;
}
if(a > max) {
a = max;
}
return a;
}

85
src/math/poison_unsafe.c Normal file
View File

@@ -0,0 +1,85 @@
/*
* The functions in this file are ones which *cannot* safely accept poison
* values in their input arguments without potentially triggering UB[1]. To
* make them safe to call from rust, two steps must be taken:
*
* 1. Arguments passed to these functions must be frozen before the call, so
* that poison values do not enter the function.
*
* 2. fast-math flags which potentially produce poison values must be disabled
* when compiling this file, so that poison values cannot be generated within
* them. Currently, this applies only to `finite-math-only`: NaN and +/-inf
* must be honored, because the `finite-math-only` flag is the only fast-math
* flag that can produce LLVM poison as of this writing.
*
* These two constraints potentially prevent some optimizations. However this
* seems like the best compromise between safety and performance, to allow an
* ergonomic rust interface without labelling many methods `unsafe` and
* requiring the users to police their input values.
*
* [1]: https://llvm.org/docs/LangRef.html#poison-values
*/
#include <math.h>
#define IMPL_UNARY_FUNCTION(C_TYPE, RUST_TYPE, FN_NAME, FN_IMPL) \
__attribute__((always_inline)) \
C_TYPE FN_NAME ## _ ## RUST_TYPE(C_TYPE a) { \
return FN_IMPL(a); \
} \
#define IMPL_UNARY(DOUBLE_FN) \
IMPL_UNARY_FUNCTION(double, f64, DOUBLE_FN, DOUBLE_FN) \
IMPL_UNARY_FUNCTION(float, f32, DOUBLE_FN, DOUBLE_FN ## f) \
#define IMPL_BINARY_FUNCTION(C_TYPE, RUST_TYPE, FN_NAME, FN_IMPL) \
__attribute__((always_inline)) \
C_TYPE FN_NAME ## _ ## RUST_TYPE(C_TYPE a, C_TYPE b) { \
return FN_IMPL(a, b); \
} \
#define IMPL_BINARY(DOUBLE_FN) \
IMPL_BINARY_FUNCTION(double, f64, DOUBLE_FN, DOUBLE_FN) \
IMPL_BINARY_FUNCTION(float, f32, DOUBLE_FN, DOUBLE_FN ## f) \
IMPL_UNARY(acos)
IMPL_UNARY(acosh)
IMPL_UNARY(asin)
IMPL_UNARY(asinh)
IMPL_UNARY(atan)
IMPL_BINARY(atan2)
IMPL_UNARY(atanh)
IMPL_UNARY(cbrt)
IMPL_UNARY(ceil)
IMPL_UNARY(cos)
IMPL_UNARY(cosh)
IMPL_UNARY(exp)
IMPL_UNARY(exp2)
IMPL_UNARY(floor)
IMPL_UNARY_FUNCTION(double, f64, exp_m1, expm1)
IMPL_UNARY_FUNCTION(float, f32, exp_m1, expm1f)
IMPL_BINARY_FUNCTION(double, f64, rem, fmod)
IMPL_BINARY_FUNCTION(float, f32, rem, fmodf)
IMPL_UNARY_FUNCTION(double, f64, ln, log)
IMPL_UNARY_FUNCTION(float, f32, ln, logf)
IMPL_UNARY_FUNCTION(double, f64, ln_1p, log1p)
IMPL_UNARY_FUNCTION(float, f32, ln_1p, log1pf)
IMPL_UNARY(log2)
IMPL_UNARY(log10)
IMPL_BINARY_FUNCTION(double, f64, powf, pow)
IMPL_BINARY_FUNCTION(float, f32, powf, powf)
IMPL_UNARY(round)
IMPL_UNARY(sin)
IMPL_UNARY(sinh)
IMPL_UNARY(sqrt)
IMPL_UNARY(tan)
IMPL_UNARY(tanh)
IMPL_UNARY(trunc)

View File

@@ -8,19 +8,4 @@ define double @freeze_f64(double %a) unnamed_addr #0 {
ret double %b
}
define i1 @freeze_i1(i1 %a) unnamed_addr #0 {
%b = freeze i1 %a
ret i1 %b
}
define i32 @freeze_i32(i32 %a) unnamed_addr #0 {
%b = freeze i32 %a
ret i32 %b
}
define i64 @freeze_i64(i64 %a) unnamed_addr #0 {
%b = freeze i64 %a
ret i64 %b
}
attributes #0 = { alwaysinline nofree norecurse willreturn nosync nounwind readnone }

View File

@@ -20,40 +20,10 @@ impl<T> MaybePoison<T> {
}
}
/// A macro to implement poison handling *only* on types which are valid for every bit pattern
macro_rules! impl_maybe_poison {
($($raw_ty:ty),*) => {
$(
impl MaybePoison<$raw_ty> {
/// Get the (possibly poison) value from this instance.
///
/// The compiler may relax poison values to undefined values. That means, among other
/// consequences, that calls to this function from copies of the same value could manifest
/// different return values. Poison values are also transitive: an instruction that depends on
/// a poison value, produces a poison value itself.
///
/// Propogation of poison values can be stopped using [`freeze`](MaybePoison::freeze)
///
/// # Safety
///
/// It is UB to use a poison value as an operand to an instruction where _any_ of the operand's
/// values trigger UB. This includes, for example, use as the divisor in integer division, or
/// as the condition of a branch.
///
/// See more examples and explanations in the [LLVM
/// documentation](https://llvm.org/docs/LangRef.html#poisonvalues)
#[inline(always)]
pub(crate) unsafe fn maybe_poison(self) -> $raw_ty {
*self.0.as_ptr()
}
}
)*
}
}
macro_rules! impl_freeze {
($($raw_ty:ty, $fn_name:ident;)*) => {
$(
#[link(name = "freeze")]
extern "C" {
fn $fn_name(val: MaybePoison<$raw_ty>) -> $raw_ty;
}
@@ -68,11 +38,7 @@ macro_rules! impl_freeze {
}
}
impl_maybe_poison! { f32, f64, u32, u64 }
impl_freeze! {
f32, freeze_f32;
f64, freeze_f64;
//u32, freeze_i32;
//u64, freeze_i64;
//bool, freeze_i1;
}