Skip to content

Commit 487d6d8

Browse files
committed
Refactor implementation of float minmax intrinsics
1 parent 9666c78 commit 487d6d8

File tree

2 files changed

+75
-106
lines changed

2 files changed

+75
-106
lines changed

compiler/rustc_const_eval/src/interpret/intrinsics.rs

Lines changed: 61 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,22 @@ enum MulAddType {
3434
Nondeterministic,
3535
}
3636

37+
#[derive(Copy, Clone)]
38+
pub(crate) enum MinMax {
39+
/// The IEEE `Minimum` operation - see `f32::minimum` etc
40+
/// In particular, `-0.0` is considered smaller than `+0.0`.
41+
Minimum,
42+
/// The IEEE `MinNum` operation - see `f32::min` etc
43+
/// In particular, if the inputs are `-0.0` and `+0.0`, the result is non-deterministic.
44+
MinNum,
45+
/// The IEEE `Maximum` operation - see `f32::maximum` etc
46+
/// In particular, `-0.0` is considered smaller than `+0.0`.
47+
Maximum,
48+
/// The IEEE `MaxNum` operation - see `f32::max` etc
49+
/// In particular, if the inputs are `-0.0` and `+0.0`, the result is non-deterministic.
50+
MaxNum,
51+
}
52+
3753
/// Directly returns an `Allocation` containing an absolute path representation of the given type.
3854
pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> (AllocId, u64) {
3955
let path = crate::util::type_name(tcx, ty);
@@ -495,25 +511,33 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
495511
self.write_scalar(Scalar::from_target_usize(align.bytes(), self), dest)?;
496512
}
497513

498-
sym::minnumf16 => self.float_min_intrinsic::<Half>(args, dest)?,
499-
sym::minnumf32 => self.float_min_intrinsic::<Single>(args, dest)?,
500-
sym::minnumf64 => self.float_min_intrinsic::<Double>(args, dest)?,
501-
sym::minnumf128 => self.float_min_intrinsic::<Quad>(args, dest)?,
514+
sym::minnumf16 => self.float_minmax_intrinsic::<Half>(args, MinMax::MinNum, dest)?,
515+
sym::minnumf32 => self.float_minmax_intrinsic::<Single>(args, MinMax::MinNum, dest)?,
516+
sym::minnumf64 => self.float_minmax_intrinsic::<Double>(args, MinMax::MinNum, dest)?,
517+
sym::minnumf128 => self.float_minmax_intrinsic::<Quad>(args, MinMax::MinNum, dest)?,
502518

503-
sym::minimumf16 => self.float_minimum_intrinsic::<Half>(args, dest)?,
504-
sym::minimumf32 => self.float_minimum_intrinsic::<Single>(args, dest)?,
505-
sym::minimumf64 => self.float_minimum_intrinsic::<Double>(args, dest)?,
506-
sym::minimumf128 => self.float_minimum_intrinsic::<Quad>(args, dest)?,
519+
sym::minimumf16 => self.float_minmax_intrinsic::<Half>(args, MinMax::Minimum, dest)?,
520+
sym::minimumf32 => {
521+
self.float_minmax_intrinsic::<Single>(args, MinMax::Minimum, dest)?
522+
}
523+
sym::minimumf64 => {
524+
self.float_minmax_intrinsic::<Double>(args, MinMax::Minimum, dest)?
525+
}
526+
sym::minimumf128 => self.float_minmax_intrinsic::<Quad>(args, MinMax::Minimum, dest)?,
507527

508-
sym::maxnumf16 => self.float_max_intrinsic::<Half>(args, dest)?,
509-
sym::maxnumf32 => self.float_max_intrinsic::<Single>(args, dest)?,
510-
sym::maxnumf64 => self.float_max_intrinsic::<Double>(args, dest)?,
511-
sym::maxnumf128 => self.float_max_intrinsic::<Quad>(args, dest)?,
528+
sym::maxnumf16 => self.float_minmax_intrinsic::<Half>(args, MinMax::MaxNum, dest)?,
529+
sym::maxnumf32 => self.float_minmax_intrinsic::<Single>(args, MinMax::MaxNum, dest)?,
530+
sym::maxnumf64 => self.float_minmax_intrinsic::<Double>(args, MinMax::MaxNum, dest)?,
531+
sym::maxnumf128 => self.float_minmax_intrinsic::<Quad>(args, MinMax::MaxNum, dest)?,
512532

513-
sym::maximumf16 => self.float_maximum_intrinsic::<Half>(args, dest)?,
514-
sym::maximumf32 => self.float_maximum_intrinsic::<Single>(args, dest)?,
515-
sym::maximumf64 => self.float_maximum_intrinsic::<Double>(args, dest)?,
516-
sym::maximumf128 => self.float_maximum_intrinsic::<Quad>(args, dest)?,
533+
sym::maximumf16 => self.float_minmax_intrinsic::<Half>(args, MinMax::Maximum, dest)?,
534+
sym::maximumf32 => {
535+
self.float_minmax_intrinsic::<Single>(args, MinMax::Maximum, dest)?
536+
}
537+
sym::maximumf64 => {
538+
self.float_minmax_intrinsic::<Double>(args, MinMax::Maximum, dest)?
539+
}
540+
sym::maximumf128 => self.float_minmax_intrinsic::<Quad>(args, MinMax::Maximum, dest)?,
517541

518542
sym::copysignf16 => self.float_copysign_intrinsic::<Half>(args, dest)?,
519543
sym::copysignf32 => self.float_copysign_intrinsic::<Single>(args, dest)?,
@@ -918,76 +942,45 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
918942
interp_ok(Scalar::from_bool(lhs_bytes == rhs_bytes))
919943
}
920944

921-
fn float_min_intrinsic<F>(
922-
&mut self,
923-
args: &[OpTy<'tcx, M::Provenance>],
924-
dest: &PlaceTy<'tcx, M::Provenance>,
925-
) -> InterpResult<'tcx, ()>
926-
where
927-
F: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F> + Into<Scalar<M::Provenance>>,
928-
{
929-
let a: F = self.read_scalar(&args[0])?.to_float()?;
930-
let b: F = self.read_scalar(&args[1])?.to_float()?;
931-
let res = if a == b {
932-
// They are definitely not NaN (those are never equal), but they could be `+0` and `-0`.
933-
// Let the machine decide which one to return.
934-
M::equal_float_min_max(self, a, b)
935-
} else {
936-
self.adjust_nan(a.min(b), &[a, b])
937-
};
938-
self.write_scalar(res, dest)?;
939-
interp_ok(())
940-
}
941-
942-
fn float_max_intrinsic<F>(
943-
&mut self,
944-
args: &[OpTy<'tcx, M::Provenance>],
945-
dest: &PlaceTy<'tcx, M::Provenance>,
946-
) -> InterpResult<'tcx, ()>
945+
fn float_minmax<F>(
946+
&self,
947+
a: Scalar<M::Provenance>,
948+
b: Scalar<M::Provenance>,
949+
op: MinMax,
950+
) -> InterpResult<'tcx, Scalar<M::Provenance>>
947951
where
948952
F: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F> + Into<Scalar<M::Provenance>>,
949953
{
950-
let a: F = self.read_scalar(&args[0])?.to_float()?;
951-
let b: F = self.read_scalar(&args[1])?.to_float()?;
952-
let res = if a == b {
954+
let a: F = a.to_float()?;
955+
let b: F = b.to_float()?;
956+
let res = if matches!(op, MinMax::MinNum | MinMax::MaxNum) && a == b {
953957
// They are definitely not NaN (those are never equal), but they could be `+0` and `-0`.
954958
// Let the machine decide which one to return.
955959
M::equal_float_min_max(self, a, b)
956960
} else {
957-
self.adjust_nan(a.max(b), &[a, b])
961+
let result = match op {
962+
MinMax::Minimum => a.minimum(b),
963+
MinMax::MinNum => a.min(b),
964+
MinMax::Maximum => a.maximum(b),
965+
MinMax::MaxNum => a.max(b),
966+
};
967+
self.adjust_nan(result, &[a, b])
958968
};
959-
self.write_scalar(res, dest)?;
960-
interp_ok(())
961-
}
962969

963-
fn float_minimum_intrinsic<F>(
964-
&mut self,
965-
args: &[OpTy<'tcx, M::Provenance>],
966-
dest: &PlaceTy<'tcx, M::Provenance>,
967-
) -> InterpResult<'tcx, ()>
968-
where
969-
F: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F> + Into<Scalar<M::Provenance>>,
970-
{
971-
let a: F = self.read_scalar(&args[0])?.to_float()?;
972-
let b: F = self.read_scalar(&args[1])?.to_float()?;
973-
let res = a.minimum(b);
974-
let res = self.adjust_nan(res, &[a, b]);
975-
self.write_scalar(res, dest)?;
976-
interp_ok(())
970+
interp_ok(res.into())
977971
}
978972

979-
fn float_maximum_intrinsic<F>(
973+
fn float_minmax_intrinsic<F>(
980974
&mut self,
981975
args: &[OpTy<'tcx, M::Provenance>],
976+
op: MinMax,
982977
dest: &PlaceTy<'tcx, M::Provenance>,
983978
) -> InterpResult<'tcx, ()>
984979
where
985980
F: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F> + Into<Scalar<M::Provenance>>,
986981
{
987-
let a: F = self.read_scalar(&args[0])?.to_float()?;
988-
let b: F = self.read_scalar(&args[1])?.to_float()?;
989-
let res = a.maximum(b);
990-
let res = self.adjust_nan(res, &[a, b]);
982+
let res =
983+
self.float_minmax::<F>(self.read_scalar(&args[0])?, self.read_scalar(&args[1])?, op)?;
991984
self.write_scalar(res, dest)?;
992985
interp_ok(())
993986
}

compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs

Lines changed: 14 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,11 @@ use rustc_span::{Symbol, sym};
99
use tracing::trace;
1010

1111
use super::{
12-
ImmTy, InterpCx, InterpResult, Machine, MulAddType, OpTy, PlaceTy, Provenance, Scalar, Size,
13-
interp_ok, throw_ub_format,
12+
ImmTy, InterpCx, InterpResult, Machine, MinMax, MulAddType, OpTy, PlaceTy, Provenance, Scalar,
13+
Size, interp_ok, throw_ub_format,
1414
};
1515
use crate::interpret::Writeable;
1616

17-
#[derive(Copy, Clone)]
18-
pub(crate) enum MinMax {
19-
Min,
20-
Max,
21-
}
22-
2317
impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
2418
/// Returns `true` if emulation happened.
2519
/// Here we implement the intrinsics that are common to all CTFE instances; individual machines can add their own
@@ -217,8 +211,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
217211
sym::simd_le => Op::MirOp(BinOp::Le),
218212
sym::simd_gt => Op::MirOp(BinOp::Gt),
219213
sym::simd_ge => Op::MirOp(BinOp::Ge),
220-
sym::simd_fmax => Op::FMinMax(MinMax::Max),
221-
sym::simd_fmin => Op::FMinMax(MinMax::Min),
214+
sym::simd_fmax => Op::FMinMax(MinMax::MaxNum),
215+
sym::simd_fmin => Op::FMinMax(MinMax::MinNum),
222216
sym::simd_saturating_add => Op::SaturatingOp(BinOp::Add),
223217
sym::simd_saturating_sub => Op::SaturatingOp(BinOp::Sub),
224218
sym::simd_arith_offset => Op::WrappingOffset,
@@ -310,8 +304,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
310304
sym::simd_reduce_xor => Op::MirOp(BinOp::BitXor),
311305
sym::simd_reduce_any => Op::MirOpBool(BinOp::BitOr),
312306
sym::simd_reduce_all => Op::MirOpBool(BinOp::BitAnd),
313-
sym::simd_reduce_max => Op::MinMax(MinMax::Max),
314-
sym::simd_reduce_min => Op::MinMax(MinMax::Min),
307+
sym::simd_reduce_max => Op::MinMax(MinMax::MaxNum),
308+
sym::simd_reduce_min => Op::MinMax(MinMax::MinNum),
315309
_ => unreachable!(),
316310
};
317311

@@ -335,8 +329,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
335329
} else {
336330
// Just boring integers, so NaNs to worry about
337331
let mirop = match mmop {
338-
MinMax::Min => BinOp::Le,
339-
MinMax::Max => BinOp::Ge,
332+
MinMax::MinNum | MinMax::Minimum => BinOp::Le,
333+
MinMax::MaxNum | MinMax::Maximum => BinOp::Ge,
340334
};
341335
if self.binary_op(mirop, &res, &op)?.to_scalar().to_bool()? {
342336
res
@@ -749,12 +743,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
749743
interp_ok(true)
750744
}
751745

752-
fn fminmax_op<Prov: Provenance>(
746+
fn fminmax_op(
753747
&self,
754748
op: MinMax,
755-
left: &ImmTy<'tcx, Prov>,
756-
right: &ImmTy<'tcx, Prov>,
757-
) -> InterpResult<'tcx, Scalar<Prov>> {
749+
left: &ImmTy<'tcx, M::Provenance>,
750+
right: &ImmTy<'tcx, M::Provenance>,
751+
) -> InterpResult<'tcx, Scalar<M::Provenance>> {
758752
assert_eq!(left.layout.ty, right.layout.ty);
759753
let ty::Float(float_ty) = left.layout.ty.kind() else {
760754
bug!("fmax operand is not a float")
@@ -763,26 +757,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
763757
let right = right.to_scalar();
764758
interp_ok(match float_ty {
765759
FloatTy::F16 => unimplemented!("f16_f128"),
766-
FloatTy::F32 => {
767-
let left = left.to_f32()?;
768-
let right = right.to_f32()?;
769-
let res = match op {
770-
MinMax::Min => left.min(right),
771-
MinMax::Max => left.max(right),
772-
};
773-
let res = self.adjust_nan(res, &[left, right]);
774-
Scalar::from_f32(res)
775-
}
776-
FloatTy::F64 => {
777-
let left = left.to_f64()?;
778-
let right = right.to_f64()?;
779-
let res = match op {
780-
MinMax::Min => left.min(right),
781-
MinMax::Max => left.max(right),
782-
};
783-
let res = self.adjust_nan(res, &[left, right]);
784-
Scalar::from_f64(res)
785-
}
760+
FloatTy::F32 => self.float_minmax::<Single>(left, right, op)?,
761+
FloatTy::F64 => self.float_minmax::<Double>(left, right, op)?,
786762
FloatTy::F128 => unimplemented!("f16_f128"),
787763
})
788764
}

0 commit comments

Comments
 (0)