diff options
| author | Ralf Jung <post@ralfj.de> | 2024-05-12 10:09:45 +0200 |
|---|---|---|
| committer | Ralf Jung <post@ralfj.de> | 2024-05-12 10:12:33 +0200 |
| commit | 9fc569d67f5e07941ed27d16148df8ebfad0a97b (patch) | |
| tree | e0eabb43949bd2a22dfe162b9e0187cbbab10096 | |
| parent | fd5e0374e927f2a3acfa198fb25667f0b5e23044 (diff) | |
| download | rust-9fc569d67f5e07941ed27d16148df8ebfad0a97b.tar.gz rust-9fc569d67f5e07941ed27d16148df8ebfad0a97b.zip | |
organize float intrinsic implementations a bit
| -rw-r--r-- | src/tools/miri/src/intrinsics/mod.rs | 194 |
1 files changed, 92 insertions, 102 deletions
diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs index cf5d9913817..7344846d6d9 100644 --- a/src/tools/miri/src/intrinsics/mod.rs +++ b/src/tools/miri/src/intrinsics/mod.rs @@ -167,6 +167,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // This is a "bitwise" operation, so there's no NaN non-determinism. this.write_scalar(Scalar::from_f64(f.abs()), dest)?; } + "floorf32" | "ceilf32" | "truncf32" | "roundf32" | "rintf32" => { let [f] = check_arg_count(args)?; let f = this.read_scalar(f)?.to_f32()?; @@ -182,6 +183,22 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let res = this.adjust_nan(res, &[f]); this.write_scalar(res, dest)?; } + "floorf64" | "ceilf64" | "truncf64" | "roundf64" | "rintf64" => { + let [f] = check_arg_count(args)?; + let f = this.read_scalar(f)?.to_f64()?; + let mode = match intrinsic_name { + "floorf64" => Round::TowardNegative, + "ceilf64" => Round::TowardPositive, + "truncf64" => Round::TowardZero, + "roundf64" => Round::NearestTiesToAway, + "rintf64" => Round::NearestTiesToEven, + _ => bug!(), + }; + let res = f.round_to_integral(mode).value; + let res = this.adjust_nan(res, &[f]); + this.write_scalar(res, dest)?; + } + #[rustfmt::skip] | "sinf32" | "cosf32" @@ -211,22 +228,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let res = this.adjust_nan(res, &[f]); this.write_scalar(res, dest)?; } - - "floorf64" | "ceilf64" | "truncf64" | "roundf64" | "rintf64" => { - let [f] = check_arg_count(args)?; - let f = this.read_scalar(f)?.to_f64()?; - let mode = match intrinsic_name { - "floorf64" => Round::TowardNegative, - "ceilf64" => Round::TowardPositive, - "truncf64" => Round::TowardZero, - "roundf64" => Round::NearestTiesToAway, - "rintf64" => Round::NearestTiesToEven, - _ => bug!(), - }; - let res = f.round_to_integral(mode).value; - let res = this.adjust_nan(res, &[f]); - this.write_scalar(res, dest)?; - } #[rustfmt::skip] | "sinf64" | "cosf64" @@ -256,84 +257,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let res = this.adjust_nan(res, &[f]); this.write_scalar(res, dest)?; } - #[rustfmt::skip] - | "fadd_algebraic" - | "fsub_algebraic" - | "fmul_algebraic" - | "fdiv_algebraic" - | "frem_algebraic" - => { - let [a, b] = check_arg_count(args)?; - let a = this.read_immediate(a)?; - let b = this.read_immediate(b)?; - let op = match intrinsic_name { - "fadd_algebraic" => mir::BinOp::Add, - "fsub_algebraic" => mir::BinOp::Sub, - "fmul_algebraic" => mir::BinOp::Mul, - "fdiv_algebraic" => mir::BinOp::Div, - "frem_algebraic" => mir::BinOp::Rem, - _ => bug!(), - }; - let res = this.wrapping_binary_op(op, &a, &b)?; - // `wrapping_binary_op` already called `generate_nan` if necessary. - this.write_immediate(*res, dest)?; - } - - #[rustfmt::skip] - | "fadd_fast" - | "fsub_fast" - | "fmul_fast" - | "fdiv_fast" - | "frem_fast" - => { - let [a, b] = check_arg_count(args)?; - let a = this.read_immediate(a)?; - let b = this.read_immediate(b)?; - let op = match intrinsic_name { - "fadd_fast" => mir::BinOp::Add, - "fsub_fast" => mir::BinOp::Sub, - "fmul_fast" => mir::BinOp::Mul, - "fdiv_fast" => mir::BinOp::Div, - "frem_fast" => mir::BinOp::Rem, - _ => bug!(), - }; - let float_finite = |x: &ImmTy<'tcx, _>| -> InterpResult<'tcx, bool> { - let ty::Float(fty) = x.layout.ty.kind() else { - bug!("float_finite: non-float input type {}", x.layout.ty) - }; - Ok(match fty { - FloatTy::F16 => unimplemented!("f16_f128"), - FloatTy::F32 => x.to_scalar().to_f32()?.is_finite(), - FloatTy::F64 => x.to_scalar().to_f64()?.is_finite(), - FloatTy::F128 => unimplemented!("f16_f128"), - }) - }; - match (float_finite(&a)?, float_finite(&b)?) { - (false, false) => throw_ub_format!( - "`{intrinsic_name}` intrinsic called with non-finite value as both parameters", - ), - (false, _) => throw_ub_format!( - "`{intrinsic_name}` intrinsic called with non-finite value as first parameter", - ), - (_, false) => throw_ub_format!( - "`{intrinsic_name}` intrinsic called with non-finite value as second parameter", - ), - _ => {} - } - let res = this.wrapping_binary_op(op, &a, &b)?; - if !float_finite(&res)? { - throw_ub_format!("`{intrinsic_name}` intrinsic produced non-finite value as result"); - } - // This cannot be a NaN so we also don't have to apply any non-determinism. - // (Also, `wrapping_binary_op` already called `generate_nan` if needed.) - this.write_immediate(*res, dest)?; - } - #[rustfmt::skip] - | "minnumf32" - | "maxnumf32" - | "copysignf32" - => { + "minnumf32" | "maxnumf32" | "copysignf32" => { let [a, b] = check_arg_count(args)?; let a = this.read_scalar(a)?.to_f32()?; let b = this.read_scalar(b)?.to_f32()?; @@ -345,12 +270,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { }; this.write_scalar(Scalar::from_f32(res), dest)?; } - - #[rustfmt::skip] - | "minnumf64" - | "maxnumf64" - | "copysignf64" - => { + "minnumf64" | "maxnumf64" | "copysignf64" => { let [a, b] = check_arg_count(args)?; let a = this.read_scalar(a)?.to_f64()?; let b = this.read_scalar(b)?.to_f64()?; @@ -373,7 +293,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let res = this.adjust_nan(res, &[a, b, c]); this.write_scalar(res, dest)?; } - "fmaf64" => { let [a, b, c] = check_arg_count(args)?; let a = this.read_scalar(a)?.to_f64()?; @@ -394,7 +313,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let res = this.adjust_nan(res, &[f1, f2]); this.write_scalar(res, dest)?; } - "powf64" => { let [f1, f2] = check_arg_count(args)?; let f1 = this.read_scalar(f1)?.to_f64()?; @@ -414,7 +332,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let res = this.adjust_nan(res, &[f]); this.write_scalar(res, dest)?; } - "powif64" => { let [f, i] = check_arg_count(args)?; let f = this.read_scalar(f)?.to_f64()?; @@ -425,6 +342,79 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.write_scalar(res, dest)?; } + #[rustfmt::skip] + | "fadd_algebraic" + | "fsub_algebraic" + | "fmul_algebraic" + | "fdiv_algebraic" + | "frem_algebraic" + => { + let [a, b] = check_arg_count(args)?; + let a = this.read_immediate(a)?; + let b = this.read_immediate(b)?; + let op = match intrinsic_name { + "fadd_algebraic" => mir::BinOp::Add, + "fsub_algebraic" => mir::BinOp::Sub, + "fmul_algebraic" => mir::BinOp::Mul, + "fdiv_algebraic" => mir::BinOp::Div, + "frem_algebraic" => mir::BinOp::Rem, + _ => bug!(), + }; + let res = this.wrapping_binary_op(op, &a, &b)?; + // `wrapping_binary_op` already called `generate_nan` if necessary. + this.write_immediate(*res, dest)?; + } + + #[rustfmt::skip] + | "fadd_fast" + | "fsub_fast" + | "fmul_fast" + | "fdiv_fast" + | "frem_fast" + => { + let [a, b] = check_arg_count(args)?; + let a = this.read_immediate(a)?; + let b = this.read_immediate(b)?; + let op = match intrinsic_name { + "fadd_fast" => mir::BinOp::Add, + "fsub_fast" => mir::BinOp::Sub, + "fmul_fast" => mir::BinOp::Mul, + "fdiv_fast" => mir::BinOp::Div, + "frem_fast" => mir::BinOp::Rem, + _ => bug!(), + }; + let float_finite = |x: &ImmTy<'tcx, _>| -> InterpResult<'tcx, bool> { + let ty::Float(fty) = x.layout.ty.kind() else { + bug!("float_finite: non-float input type {}", x.layout.ty) + }; + Ok(match fty { + FloatTy::F16 => unimplemented!("f16_f128"), + FloatTy::F32 => x.to_scalar().to_f32()?.is_finite(), + FloatTy::F64 => x.to_scalar().to_f64()?.is_finite(), + FloatTy::F128 => unimplemented!("f16_f128"), + }) + }; + match (float_finite(&a)?, float_finite(&b)?) { + (false, false) => throw_ub_format!( + "`{intrinsic_name}` intrinsic called with non-finite value as both parameters", + ), + (false, _) => throw_ub_format!( + "`{intrinsic_name}` intrinsic called with non-finite value as first parameter", + ), + (_, false) => throw_ub_format!( + "`{intrinsic_name}` intrinsic called with non-finite value as second parameter", + ), + _ => {} + } + let res = this.wrapping_binary_op(op, &a, &b)?; + if !float_finite(&res)? { + throw_ub_format!("`{intrinsic_name}` intrinsic produced non-finite value as result"); + } + // This cannot be a NaN so we also don't have to apply any non-determinism. + // (Also, `wrapping_binary_op` already called `generate_nan` if needed.) + this.write_immediate(*res, dest)?; + } + "float_to_int_unchecked" => { let [val] = check_arg_count(args)?; let val = this.read_immediate(val)?; |
