Skip to content

Commit

Permalink
Move unary_op_* functions from shims::x86::sse module to shims::x86
Browse files Browse the repository at this point in the history
  • Loading branch information
eduardosm committed Dec 7, 2023
1 parent f7af14b commit c37f4d6
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 125 deletions.
124 changes: 124 additions & 0 deletions src/tools/miri/src/shims/x86/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
use rand::Rng as _;

use rustc_apfloat::{ieee::Single, Float as _};
use rustc_middle::{mir, ty};
use rustc_span::Symbol;
use rustc_target::abi::Size;
Expand Down Expand Up @@ -331,6 +334,127 @@ fn bin_op_simd_float_all<'tcx, F: rustc_apfloat::Float>(
Ok(())
}

#[derive(Copy, Clone)]
enum FloatUnaryOp {
/// sqrt(x)
///
/// <https://www.felixcloutier.com/x86/sqrtss>
/// <https://www.felixcloutier.com/x86/sqrtps>
Sqrt,
/// Approximation of 1/x
///
/// <https://www.felixcloutier.com/x86/rcpss>
/// <https://www.felixcloutier.com/x86/rcpps>
Rcp,
/// Approximation of 1/sqrt(x)
///
/// <https://www.felixcloutier.com/x86/rsqrtss>
/// <https://www.felixcloutier.com/x86/rsqrtps>
Rsqrt,
}

/// Performs `which` scalar operation on `op` and returns the result.
#[allow(clippy::arithmetic_side_effects)] // floating point operations without side effects
fn unary_op_f32<'tcx>(
this: &mut crate::MiriInterpCx<'_, 'tcx>,
which: FloatUnaryOp,
op: &ImmTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Scalar<Provenance>> {
match which {
FloatUnaryOp::Sqrt => {
let op = op.to_scalar();
// FIXME using host floats
Ok(Scalar::from_u32(f32::from_bits(op.to_u32()?).sqrt().to_bits()))
}
FloatUnaryOp::Rcp => {
let op = op.to_scalar().to_f32()?;
let div = (Single::from_u128(1).value / op).value;
// Apply a relative error with a magnitude on the order of 2^-12 to simulate the
// inaccuracy of RCP.
let res = apply_random_float_error(this, div, -12);
Ok(Scalar::from_f32(res))
}
FloatUnaryOp::Rsqrt => {
let op = op.to_scalar().to_u32()?;
// FIXME using host floats
let sqrt = Single::from_bits(f32::from_bits(op).sqrt().to_bits().into());
let rsqrt = (Single::from_u128(1).value / sqrt).value;
// Apply a relative error with a magnitude on the order of 2^-12 to simulate the
// inaccuracy of RSQRT.
let res = apply_random_float_error(this, rsqrt, -12);
Ok(Scalar::from_f32(res))
}
}
}

/// Disturbes a floating-point result by a relative error on the order of (-2^scale, 2^scale).
#[allow(clippy::arithmetic_side_effects)] // floating point arithmetic cannot panic
fn apply_random_float_error<F: rustc_apfloat::Float>(
this: &mut crate::MiriInterpCx<'_, '_>,
val: F,
err_scale: i32,
) -> F {
let rng = this.machine.rng.get_mut();
// generates rand(0, 2^64) * 2^(scale - 64) = rand(0, 1) * 2^scale
let err =
F::from_u128(rng.gen::<u64>().into()).value.scalbn(err_scale.checked_sub(64).unwrap());
// give it a random sign
let err = if rng.gen::<bool>() { -err } else { err };
// multiple the value with (1+err)
(val * (F::from_u128(1).value + err).value).value
}

/// Performs `which` operation on the first component of `op` and copies
/// the other components. The result is stored in `dest`.
fn unary_op_ss<'tcx>(
this: &mut crate::MiriInterpCx<'_, 'tcx>,
which: FloatUnaryOp,
op: &OpTy<'tcx, Provenance>,
dest: &PlaceTy<'tcx, Provenance>,
) -> InterpResult<'tcx, ()> {
let (op, op_len) = this.operand_to_simd(op)?;
let (dest, dest_len) = this.place_to_simd(dest)?;

assert_eq!(dest_len, op_len);

let res0 = unary_op_f32(this, which, &this.read_immediate(&this.project_index(&op, 0)?)?)?;
this.write_scalar(res0, &this.project_index(&dest, 0)?)?;

for i in 1..dest_len {
this.copy_op(
&this.project_index(&op, i)?,
&this.project_index(&dest, i)?,
/*allow_transmute*/ false,
)?;
}

Ok(())
}

/// Performs `which` operation on each component of `op`, storing the
/// result is stored in `dest`.
fn unary_op_ps<'tcx>(
this: &mut crate::MiriInterpCx<'_, 'tcx>,
which: FloatUnaryOp,
op: &OpTy<'tcx, Provenance>,
dest: &PlaceTy<'tcx, Provenance>,
) -> InterpResult<'tcx, ()> {
let (op, op_len) = this.operand_to_simd(op)?;
let (dest, dest_len) = this.place_to_simd(dest)?;

assert_eq!(dest_len, op_len);

for i in 0..dest_len {
let op = this.read_immediate(&this.project_index(&op, i)?)?;
let dest = this.project_index(&dest, i)?;

let res = unary_op_f32(this, which, &op)?;
this.write_scalar(res, &dest)?;
}

Ok(())
}

/// Converts each element of `op` from floating point to signed integer.
///
/// When the input value is NaN or out of range, fall back to minimum value.
Expand Down
130 changes: 5 additions & 125 deletions src/tools/miri/src/shims/x86/sse.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use rustc_apfloat::{ieee::Single, Float as _};
use rustc_apfloat::ieee::Single;
use rustc_middle::mir;
use rustc_span::Symbol;
use rustc_target::spec::abi::Abi;

use rand::Rng as _;

use super::{bin_op_simd_float_all, bin_op_simd_float_first, FloatBinOp};
use super::{
bin_op_simd_float_all, bin_op_simd_float_first, unary_op_ps, unary_op_ss, FloatBinOp,
FloatUnaryOp,
};
use crate::*;
use shims::foreign_items::EmulateForeignItemResult;

Expand Down Expand Up @@ -219,124 +220,3 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
Ok(EmulateForeignItemResult::NeedsJumping)
}
}

#[derive(Copy, Clone)]
enum FloatUnaryOp {
/// sqrt(x)
///
/// <https://www.felixcloutier.com/x86/sqrtss>
/// <https://www.felixcloutier.com/x86/sqrtps>
Sqrt,
/// Approximation of 1/x
///
/// <https://www.felixcloutier.com/x86/rcpss>
/// <https://www.felixcloutier.com/x86/rcpps>
Rcp,
/// Approximation of 1/sqrt(x)
///
/// <https://www.felixcloutier.com/x86/rsqrtss>
/// <https://www.felixcloutier.com/x86/rsqrtps>
Rsqrt,
}

/// Performs `which` scalar operation on `op` and returns the result.
#[allow(clippy::arithmetic_side_effects)] // floating point operations without side effects
fn unary_op_f32<'tcx>(
this: &mut crate::MiriInterpCx<'_, 'tcx>,
which: FloatUnaryOp,
op: &ImmTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Scalar<Provenance>> {
match which {
FloatUnaryOp::Sqrt => {
let op = op.to_scalar();
// FIXME using host floats
Ok(Scalar::from_u32(f32::from_bits(op.to_u32()?).sqrt().to_bits()))
}
FloatUnaryOp::Rcp => {
let op = op.to_scalar().to_f32()?;
let div = (Single::from_u128(1).value / op).value;
// Apply a relative error with a magnitude on the order of 2^-12 to simulate the
// inaccuracy of RCP.
let res = apply_random_float_error(this, div, -12);
Ok(Scalar::from_f32(res))
}
FloatUnaryOp::Rsqrt => {
let op = op.to_scalar().to_u32()?;
// FIXME using host floats
let sqrt = Single::from_bits(f32::from_bits(op).sqrt().to_bits().into());
let rsqrt = (Single::from_u128(1).value / sqrt).value;
// Apply a relative error with a magnitude on the order of 2^-12 to simulate the
// inaccuracy of RSQRT.
let res = apply_random_float_error(this, rsqrt, -12);
Ok(Scalar::from_f32(res))
}
}
}

/// Disturbes a floating-point result by a relative error on the order of (-2^scale, 2^scale).
#[allow(clippy::arithmetic_side_effects)] // floating point arithmetic cannot panic
fn apply_random_float_error<F: rustc_apfloat::Float>(
this: &mut crate::MiriInterpCx<'_, '_>,
val: F,
err_scale: i32,
) -> F {
let rng = this.machine.rng.get_mut();
// generates rand(0, 2^64) * 2^(scale - 64) = rand(0, 1) * 2^scale
let err =
F::from_u128(rng.gen::<u64>().into()).value.scalbn(err_scale.checked_sub(64).unwrap());
// give it a random sign
let err = if rng.gen::<bool>() { -err } else { err };
// multiple the value with (1+err)
(val * (F::from_u128(1).value + err).value).value
}

/// Performs `which` operation on the first component of `op` and copies
/// the other components. The result is stored in `dest`.
fn unary_op_ss<'tcx>(
this: &mut crate::MiriInterpCx<'_, 'tcx>,
which: FloatUnaryOp,
op: &OpTy<'tcx, Provenance>,
dest: &PlaceTy<'tcx, Provenance>,
) -> InterpResult<'tcx, ()> {
let (op, op_len) = this.operand_to_simd(op)?;
let (dest, dest_len) = this.place_to_simd(dest)?;

assert_eq!(dest_len, op_len);

let res0 = unary_op_f32(this, which, &this.read_immediate(&this.project_index(&op, 0)?)?)?;
this.write_scalar(res0, &this.project_index(&dest, 0)?)?;

for i in 1..dest_len {
this.copy_op(
&this.project_index(&op, i)?,
&this.project_index(&dest, i)?,
/*allow_transmute*/ false,
)?;
}

Ok(())
}

/// Performs `which` operation on each component of `op`, storing the
/// result is stored in `dest`.
fn unary_op_ps<'tcx>(
this: &mut crate::MiriInterpCx<'_, 'tcx>,
which: FloatUnaryOp,
op: &OpTy<'tcx, Provenance>,
dest: &PlaceTy<'tcx, Provenance>,
) -> InterpResult<'tcx, ()> {
let (op, op_len) = this.operand_to_simd(op)?;
let (dest, dest_len) = this.place_to_simd(dest)?;

assert_eq!(dest_len, op_len);

for i in 0..dest_len {
let op = this.read_immediate(&this.project_index(&op, i)?)?;
let dest = this.project_index(&dest, i)?;

let res = unary_op_f32(this, which, &op)?;
this.write_scalar(res, &dest)?;
}

Ok(())
}

0 comments on commit c37f4d6

Please sign in to comment.