Skip to content

Commit 14ced80

Browse files
committed
Auto merge of rust-lang#3601 - RalfJung:intrinsics, r=RalfJung
a bit of intrinsics organization
2 parents fd5e037 + 01b5430 commit 14ced80

File tree

6 files changed

+220
-226
lines changed

6 files changed

+220
-226
lines changed

src/tools/miri/src/intrinsics/mod.rs

Lines changed: 92 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
167167
// This is a "bitwise" operation, so there's no NaN non-determinism.
168168
this.write_scalar(Scalar::from_f64(f.abs()), dest)?;
169169
}
170+
170171
"floorf32" | "ceilf32" | "truncf32" | "roundf32" | "rintf32" => {
171172
let [f] = check_arg_count(args)?;
172173
let f = this.read_scalar(f)?.to_f32()?;
@@ -182,6 +183,22 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
182183
let res = this.adjust_nan(res, &[f]);
183184
this.write_scalar(res, dest)?;
184185
}
186+
"floorf64" | "ceilf64" | "truncf64" | "roundf64" | "rintf64" => {
187+
let [f] = check_arg_count(args)?;
188+
let f = this.read_scalar(f)?.to_f64()?;
189+
let mode = match intrinsic_name {
190+
"floorf64" => Round::TowardNegative,
191+
"ceilf64" => Round::TowardPositive,
192+
"truncf64" => Round::TowardZero,
193+
"roundf64" => Round::NearestTiesToAway,
194+
"rintf64" => Round::NearestTiesToEven,
195+
_ => bug!(),
196+
};
197+
let res = f.round_to_integral(mode).value;
198+
let res = this.adjust_nan(res, &[f]);
199+
this.write_scalar(res, dest)?;
200+
}
201+
185202
#[rustfmt::skip]
186203
| "sinf32"
187204
| "cosf32"
@@ -211,22 +228,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
211228
let res = this.adjust_nan(res, &[f]);
212229
this.write_scalar(res, dest)?;
213230
}
214-
215-
"floorf64" | "ceilf64" | "truncf64" | "roundf64" | "rintf64" => {
216-
let [f] = check_arg_count(args)?;
217-
let f = this.read_scalar(f)?.to_f64()?;
218-
let mode = match intrinsic_name {
219-
"floorf64" => Round::TowardNegative,
220-
"ceilf64" => Round::TowardPositive,
221-
"truncf64" => Round::TowardZero,
222-
"roundf64" => Round::NearestTiesToAway,
223-
"rintf64" => Round::NearestTiesToEven,
224-
_ => bug!(),
225-
};
226-
let res = f.round_to_integral(mode).value;
227-
let res = this.adjust_nan(res, &[f]);
228-
this.write_scalar(res, dest)?;
229-
}
230231
#[rustfmt::skip]
231232
| "sinf64"
232233
| "cosf64"
@@ -256,84 +257,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
256257
let res = this.adjust_nan(res, &[f]);
257258
this.write_scalar(res, dest)?;
258259
}
259-
#[rustfmt::skip]
260-
| "fadd_algebraic"
261-
| "fsub_algebraic"
262-
| "fmul_algebraic"
263-
| "fdiv_algebraic"
264-
| "frem_algebraic"
265-
=> {
266-
let [a, b] = check_arg_count(args)?;
267-
let a = this.read_immediate(a)?;
268-
let b = this.read_immediate(b)?;
269-
let op = match intrinsic_name {
270-
"fadd_algebraic" => mir::BinOp::Add,
271-
"fsub_algebraic" => mir::BinOp::Sub,
272-
"fmul_algebraic" => mir::BinOp::Mul,
273-
"fdiv_algebraic" => mir::BinOp::Div,
274-
"frem_algebraic" => mir::BinOp::Rem,
275-
_ => bug!(),
276-
};
277-
let res = this.wrapping_binary_op(op, &a, &b)?;
278-
// `wrapping_binary_op` already called `generate_nan` if necessary.
279-
this.write_immediate(*res, dest)?;
280-
}
281-
282-
#[rustfmt::skip]
283-
| "fadd_fast"
284-
| "fsub_fast"
285-
| "fmul_fast"
286-
| "fdiv_fast"
287-
| "frem_fast"
288-
=> {
289-
let [a, b] = check_arg_count(args)?;
290-
let a = this.read_immediate(a)?;
291-
let b = this.read_immediate(b)?;
292-
let op = match intrinsic_name {
293-
"fadd_fast" => mir::BinOp::Add,
294-
"fsub_fast" => mir::BinOp::Sub,
295-
"fmul_fast" => mir::BinOp::Mul,
296-
"fdiv_fast" => mir::BinOp::Div,
297-
"frem_fast" => mir::BinOp::Rem,
298-
_ => bug!(),
299-
};
300-
let float_finite = |x: &ImmTy<'tcx, _>| -> InterpResult<'tcx, bool> {
301-
let ty::Float(fty) = x.layout.ty.kind() else {
302-
bug!("float_finite: non-float input type {}", x.layout.ty)
303-
};
304-
Ok(match fty {
305-
FloatTy::F16 => unimplemented!("f16_f128"),
306-
FloatTy::F32 => x.to_scalar().to_f32()?.is_finite(),
307-
FloatTy::F64 => x.to_scalar().to_f64()?.is_finite(),
308-
FloatTy::F128 => unimplemented!("f16_f128"),
309-
})
310-
};
311-
match (float_finite(&a)?, float_finite(&b)?) {
312-
(false, false) => throw_ub_format!(
313-
"`{intrinsic_name}` intrinsic called with non-finite value as both parameters",
314-
),
315-
(false, _) => throw_ub_format!(
316-
"`{intrinsic_name}` intrinsic called with non-finite value as first parameter",
317-
),
318-
(_, false) => throw_ub_format!(
319-
"`{intrinsic_name}` intrinsic called with non-finite value as second parameter",
320-
),
321-
_ => {}
322-
}
323-
let res = this.wrapping_binary_op(op, &a, &b)?;
324-
if !float_finite(&res)? {
325-
throw_ub_format!("`{intrinsic_name}` intrinsic produced non-finite value as result");
326-
}
327-
// This cannot be a NaN so we also don't have to apply any non-determinism.
328-
// (Also, `wrapping_binary_op` already called `generate_nan` if needed.)
329-
this.write_immediate(*res, dest)?;
330-
}
331260

332-
#[rustfmt::skip]
333-
| "minnumf32"
334-
| "maxnumf32"
335-
| "copysignf32"
336-
=> {
261+
"minnumf32" | "maxnumf32" | "copysignf32" => {
337262
let [a, b] = check_arg_count(args)?;
338263
let a = this.read_scalar(a)?.to_f32()?;
339264
let b = this.read_scalar(b)?.to_f32()?;
@@ -345,12 +270,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
345270
};
346271
this.write_scalar(Scalar::from_f32(res), dest)?;
347272
}
348-
349-
#[rustfmt::skip]
350-
| "minnumf64"
351-
| "maxnumf64"
352-
| "copysignf64"
353-
=> {
273+
"minnumf64" | "maxnumf64" | "copysignf64" => {
354274
let [a, b] = check_arg_count(args)?;
355275
let a = this.read_scalar(a)?.to_f64()?;
356276
let b = this.read_scalar(b)?.to_f64()?;
@@ -373,7 +293,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
373293
let res = this.adjust_nan(res, &[a, b, c]);
374294
this.write_scalar(res, dest)?;
375295
}
376-
377296
"fmaf64" => {
378297
let [a, b, c] = check_arg_count(args)?;
379298
let a = this.read_scalar(a)?.to_f64()?;
@@ -394,7 +313,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
394313
let res = this.adjust_nan(res, &[f1, f2]);
395314
this.write_scalar(res, dest)?;
396315
}
397-
398316
"powf64" => {
399317
let [f1, f2] = check_arg_count(args)?;
400318
let f1 = this.read_scalar(f1)?.to_f64()?;
@@ -414,7 +332,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
414332
let res = this.adjust_nan(res, &[f]);
415333
this.write_scalar(res, dest)?;
416334
}
417-
418335
"powif64" => {
419336
let [f, i] = check_arg_count(args)?;
420337
let f = this.read_scalar(f)?.to_f64()?;
@@ -425,6 +342,79 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
425342
this.write_scalar(res, dest)?;
426343
}
427344

345+
#[rustfmt::skip]
346+
| "fadd_algebraic"
347+
| "fsub_algebraic"
348+
| "fmul_algebraic"
349+
| "fdiv_algebraic"
350+
| "frem_algebraic"
351+
=> {
352+
let [a, b] = check_arg_count(args)?;
353+
let a = this.read_immediate(a)?;
354+
let b = this.read_immediate(b)?;
355+
let op = match intrinsic_name {
356+
"fadd_algebraic" => mir::BinOp::Add,
357+
"fsub_algebraic" => mir::BinOp::Sub,
358+
"fmul_algebraic" => mir::BinOp::Mul,
359+
"fdiv_algebraic" => mir::BinOp::Div,
360+
"frem_algebraic" => mir::BinOp::Rem,
361+
_ => bug!(),
362+
};
363+
let res = this.wrapping_binary_op(op, &a, &b)?;
364+
// `wrapping_binary_op` already called `generate_nan` if necessary.
365+
this.write_immediate(*res, dest)?;
366+
}
367+
368+
#[rustfmt::skip]
369+
| "fadd_fast"
370+
| "fsub_fast"
371+
| "fmul_fast"
372+
| "fdiv_fast"
373+
| "frem_fast"
374+
=> {
375+
let [a, b] = check_arg_count(args)?;
376+
let a = this.read_immediate(a)?;
377+
let b = this.read_immediate(b)?;
378+
let op = match intrinsic_name {
379+
"fadd_fast" => mir::BinOp::Add,
380+
"fsub_fast" => mir::BinOp::Sub,
381+
"fmul_fast" => mir::BinOp::Mul,
382+
"fdiv_fast" => mir::BinOp::Div,
383+
"frem_fast" => mir::BinOp::Rem,
384+
_ => bug!(),
385+
};
386+
let float_finite = |x: &ImmTy<'tcx, _>| -> InterpResult<'tcx, bool> {
387+
let ty::Float(fty) = x.layout.ty.kind() else {
388+
bug!("float_finite: non-float input type {}", x.layout.ty)
389+
};
390+
Ok(match fty {
391+
FloatTy::F16 => unimplemented!("f16_f128"),
392+
FloatTy::F32 => x.to_scalar().to_f32()?.is_finite(),
393+
FloatTy::F64 => x.to_scalar().to_f64()?.is_finite(),
394+
FloatTy::F128 => unimplemented!("f16_f128"),
395+
})
396+
};
397+
match (float_finite(&a)?, float_finite(&b)?) {
398+
(false, false) => throw_ub_format!(
399+
"`{intrinsic_name}` intrinsic called with non-finite value as both parameters",
400+
),
401+
(false, _) => throw_ub_format!(
402+
"`{intrinsic_name}` intrinsic called with non-finite value as first parameter",
403+
),
404+
(_, false) => throw_ub_format!(
405+
"`{intrinsic_name}` intrinsic called with non-finite value as second parameter",
406+
),
407+
_ => {}
408+
}
409+
let res = this.wrapping_binary_op(op, &a, &b)?;
410+
if !float_finite(&res)? {
411+
throw_ub_format!("`{intrinsic_name}` intrinsic produced non-finite value as result");
412+
}
413+
// This cannot be a NaN so we also don't have to apply any non-determinism.
414+
// (Also, `wrapping_binary_op` already called `generate_nan` if needed.)
415+
this.write_immediate(*res, dest)?;
416+
}
417+
428418
"float_to_int_unchecked" => {
429419
let [val] = check_arg_count(args)?;
430420
let val = this.read_immediate(val)?;

src/tools/miri/tests/pass/float.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#![feature(stmt_expr_attributes)]
22
#![feature(float_gamma)]
3+
#![feature(core_intrinsics)]
34
#![allow(arithmetic_overflow)]
45

56
use std::fmt::Debug;
@@ -22,6 +23,8 @@ fn main() {
2223
rounding();
2324
mul_add();
2425
libm();
26+
test_fast();
27+
test_algebraic();
2528
}
2629

2730
// Helper function to avoid promotion so that this tests "run-time" casts, not CTFE.
@@ -751,3 +754,67 @@ pub fn libm() {
751754
assert_approx_eq!(val, (2.0 * f64::consts::PI.sqrt()).ln());
752755
assert_eq!(sign, -1);
753756
}
757+
758+
fn test_fast() {
759+
use std::intrinsics::{fadd_fast, fdiv_fast, fmul_fast, frem_fast, fsub_fast};
760+
761+
#[inline(never)]
762+
pub fn test_operations_f64(a: f64, b: f64) {
763+
// make sure they all map to the correct operation
764+
unsafe {
765+
assert_eq!(fadd_fast(a, b), a + b);
766+
assert_eq!(fsub_fast(a, b), a - b);
767+
assert_eq!(fmul_fast(a, b), a * b);
768+
assert_eq!(fdiv_fast(a, b), a / b);
769+
assert_eq!(frem_fast(a, b), a % b);
770+
}
771+
}
772+
773+
#[inline(never)]
774+
pub fn test_operations_f32(a: f32, b: f32) {
775+
// make sure they all map to the correct operation
776+
unsafe {
777+
assert_eq!(fadd_fast(a, b), a + b);
778+
assert_eq!(fsub_fast(a, b), a - b);
779+
assert_eq!(fmul_fast(a, b), a * b);
780+
assert_eq!(fdiv_fast(a, b), a / b);
781+
assert_eq!(frem_fast(a, b), a % b);
782+
}
783+
}
784+
785+
test_operations_f64(1., 2.);
786+
test_operations_f64(10., 5.);
787+
test_operations_f32(11., 2.);
788+
test_operations_f32(10., 15.);
789+
}
790+
791+
fn test_algebraic() {
792+
use std::intrinsics::{
793+
fadd_algebraic, fdiv_algebraic, fmul_algebraic, frem_algebraic, fsub_algebraic,
794+
};
795+
796+
#[inline(never)]
797+
pub fn test_operations_f64(a: f64, b: f64) {
798+
// make sure they all map to the correct operation
799+
assert_eq!(fadd_algebraic(a, b), a + b);
800+
assert_eq!(fsub_algebraic(a, b), a - b);
801+
assert_eq!(fmul_algebraic(a, b), a * b);
802+
assert_eq!(fdiv_algebraic(a, b), a / b);
803+
assert_eq!(frem_algebraic(a, b), a % b);
804+
}
805+
806+
#[inline(never)]
807+
pub fn test_operations_f32(a: f32, b: f32) {
808+
// make sure they all map to the correct operation
809+
assert_eq!(fadd_algebraic(a, b), a + b);
810+
assert_eq!(fsub_algebraic(a, b), a - b);
811+
assert_eq!(fmul_algebraic(a, b), a * b);
812+
assert_eq!(fdiv_algebraic(a, b), a / b);
813+
assert_eq!(frem_algebraic(a, b), a % b);
814+
}
815+
816+
test_operations_f64(1., 2.);
817+
test_operations_f64(10., 5.);
818+
test_operations_f32(11., 2.);
819+
test_operations_f32(10., 15.);
820+
}

src/tools/miri/tests/pass/float_fast_math.rs

Lines changed: 0 additions & 34 deletions
This file was deleted.

0 commit comments

Comments
 (0)