From 207bc77e15acc9444bf6c3e74fa46607490a8e8a Mon Sep 17 00:00:00 2001 From: David Wood Date: Wed, 11 Sep 2024 13:57:12 +0100 Subject: codegen_ssa: consolidate tied feature checking `rustc_codegen_llvm` and `rustc_codegen_gcc` duplicated logic for checking if tied target features were partially enabled. This commit consolidates these checks into `rustc_codegen_ssa` in the `codegen_fn_attrs` query, which also is run pre-monomorphisation for each function, which ensures that this check is run for unused functions, as would be expected. --- compiler/rustc_codegen_gcc/src/attributes.rs | 22 ++--------------- compiler/rustc_codegen_gcc/src/errors.rs | 36 ---------------------------- compiler/rustc_codegen_gcc/src/gcc_util.rs | 24 +++---------------- 3 files changed, 5 insertions(+), 77 deletions(-) (limited to 'compiler/rustc_codegen_gcc/src') diff --git a/compiler/rustc_codegen_gcc/src/attributes.rs b/compiler/rustc_codegen_gcc/src/attributes.rs index 5fdf2680aac..d20e13e15b9 100644 --- a/compiler/rustc_codegen_gcc/src/attributes.rs +++ b/compiler/rustc_codegen_gcc/src/attributes.rs @@ -7,11 +7,9 @@ use rustc_attr::InstructionSetAttr; #[cfg(feature = "master")] use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::ty; -use rustc_span::symbol::sym; use crate::context::CodegenCx; -use crate::errors::TiedTargetFeatures; -use crate::gcc_util::{check_tied_features, to_gcc_features}; +use crate::gcc_util::to_gcc_features; /// Get GCC attribute for the provided inline heuristic. #[cfg(feature = "master")] @@ -72,26 +70,10 @@ pub fn from_fn_attrs<'gcc, 'tcx>( } } - let function_features = codegen_fn_attrs + let mut function_features = codegen_fn_attrs .target_features .iter() .map(|features| features.name.as_str()) - .collect::>(); - - if let Some(features) = check_tied_features( - cx.tcx.sess, - &function_features.iter().map(|features| (*features, true)).collect(), - ) { - let span = cx - .tcx - .get_attr(instance.def_id(), sym::target_feature) - .map_or_else(|| cx.tcx.def_span(instance.def_id()), |a| a.span); - cx.tcx.dcx().create_err(TiedTargetFeatures { features: features.join(", "), span }).emit(); - return; - } - - let mut function_features = function_features - .iter() .flat_map(|feat| to_gcc_features(cx.tcx.sess, feat).into_iter()) .chain(codegen_fn_attrs.instruction_set.iter().map(|x| match *x { InstructionSetAttr::ArmA32 => "-thumb-mode", // TODO(antoyo): support removing feature. diff --git a/compiler/rustc_codegen_gcc/src/errors.rs b/compiler/rustc_codegen_gcc/src/errors.rs index 6bada3d334c..dc1895f437b 100644 --- a/compiler/rustc_codegen_gcc/src/errors.rs +++ b/compiler/rustc_codegen_gcc/src/errors.rs @@ -1,9 +1,6 @@ -use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::Span; -use crate::fluent_generated as fluent; - #[derive(Diagnostic)] #[diag(codegen_gcc_unknown_ctarget_feature_prefix)] #[note] @@ -45,15 +42,6 @@ pub(crate) struct InvalidMinimumAlignment { pub err: String, } -#[derive(Diagnostic)] -#[diag(codegen_gcc_tied_target_features)] -#[help] -pub(crate) struct TiedTargetFeatures { - #[primary_span] - pub span: Span, - pub features: String, -} - #[derive(Diagnostic)] #[diag(codegen_gcc_copy_bitcode)] pub(crate) struct CopyBitcode { @@ -78,27 +66,3 @@ pub(crate) struct LtoDylib; pub(crate) struct LtoBitcodeFromRlib { pub gcc_err: String, } - -pub(crate) struct TargetFeatureDisableOrEnable<'a> { - pub features: &'a [&'a str], - pub span: Option, - pub missing_features: Option, -} - -#[derive(Subdiagnostic)] -#[help(codegen_gcc_missing_features)] -pub(crate) struct MissingFeatures; - -impl Diagnostic<'_, G> for TargetFeatureDisableOrEnable<'_> { - fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { - let mut diag = Diag::new(dcx, level, fluent::codegen_gcc_target_feature_disable_or_enable); - if let Some(span) = self.span { - diag.span(span); - }; - if let Some(missing_features) = self.missing_features { - diag.subdiagnostic(missing_features); - } - diag.arg("features", self.features.join(", ")); - diag - } -} diff --git a/compiler/rustc_codegen_gcc/src/gcc_util.rs b/compiler/rustc_codegen_gcc/src/gcc_util.rs index 01dd1a8856a..3104088e0d5 100644 --- a/compiler/rustc_codegen_gcc/src/gcc_util.rs +++ b/compiler/rustc_codegen_gcc/src/gcc_util.rs @@ -1,15 +1,14 @@ #[cfg(feature = "master")] use gccjit::Context; +use rustc_codegen_ssa::codegen_attrs::check_tied_features; +use rustc_codegen_ssa::errors::TargetFeatureDisableOrEnable; use rustc_data_structures::fx::FxHashMap; use rustc_middle::bug; use rustc_session::Session; use rustc_target::target_features::RUSTC_SPECIFIC_FEATURES; use smallvec::{SmallVec, smallvec}; -use crate::errors::{ - PossibleFeature, TargetFeatureDisableOrEnable, UnknownCTargetFeature, - UnknownCTargetFeaturePrefix, -}; +use crate::errors::{PossibleFeature, UnknownCTargetFeature, UnknownCTargetFeaturePrefix}; /// The list of GCC features computed from CLI flags (`-Ctarget-cpu`, `-Ctarget-feature`, /// `--target` and similar). @@ -185,23 +184,6 @@ pub fn to_gcc_features<'a>(sess: &Session, s: &'a str) -> SmallVec<[&'a str; 2]> } } -// Given a map from target_features to whether they are enabled or disabled, -// ensure only valid combinations are allowed. -pub fn check_tied_features( - sess: &Session, - features: &FxHashMap<&str, bool>, -) -> Option<&'static [&'static str]> { - for tied in sess.target.tied_target_features() { - // Tied features must be set to the same value, or not set at all - let mut tied_iter = tied.iter(); - let enabled = features.get(tied_iter.next().unwrap()); - if tied_iter.any(|feature| enabled != features.get(feature)) { - return Some(tied); - } - } - None -} - fn arch_to_gcc(name: &str) -> &str { match name { "M68020" => "68020", -- cgit 1.4.1-3-g733a5 From d92aee556d92214f972ed91499e61474ad38aa9d Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Tue, 8 Oct 2024 17:27:02 -0700 Subject: cg_gcc: Factor out rustc_target::abi --- compiler/rustc_codegen_gcc/src/builder.rs | 7 ++++--- compiler/rustc_codegen_gcc/src/common.rs | 4 +++- compiler/rustc_codegen_gcc/src/lib.rs | 1 + compiler/rustc_codegen_gcc/src/type_of.rs | 8 ++++---- 4 files changed, 12 insertions(+), 8 deletions(-) (limited to 'compiler/rustc_codegen_gcc/src') diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index acd53588831..b611f9ba8bc 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -7,6 +7,8 @@ use gccjit::{ BinaryOp, Block, ComparisonOp, Context, Function, LValue, Location, RValue, ToRValue, Type, UnaryOp, }; +use rustc_abi as abi; +use rustc_abi::{Align, HasDataLayout, Size, TargetDataLayout, WrappingRange}; use rustc_apfloat::{Float, Round, Status, ieee}; use rustc_codegen_ssa::MemFlags; use rustc_codegen_ssa::common::{ @@ -28,7 +30,6 @@ use rustc_middle::ty::{Instance, ParamEnv, Ty, TyCtxt}; use rustc_span::Span; use rustc_span::def_id::DefId; use rustc_target::abi::call::FnAbi; -use rustc_target::abi::{self, Align, HasDataLayout, Size, TargetDataLayout, WrappingRange}; use rustc_target::spec::{HasTargetSpec, HasWasmCAbiOpt, Target, WasmCAbi}; use crate::common::{SignType, TypeReflection, type_is_pointer}; @@ -998,12 +999,12 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { ) { let vr = scalar.valid_range(bx); match scalar.primitive() { - abi::Int(..) => { + abi::Primitive::Int(..) => { if !scalar.is_always_valid(bx) { bx.range_metadata(load, vr); } } - abi::Pointer(_) if vr.start < vr.end && !vr.contains(0) => { + abi::Primitive::Pointer(_) if vr.start < vr.end && !vr.contains(0) => { bx.nonnull_metadata(load); } _ => {} diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index 726b126e727..0d3e7083d56 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -1,11 +1,13 @@ use gccjit::{LValue, RValue, ToRValue, Type}; +use rustc_abi as abi; +use rustc_abi::HasDataLayout; +use rustc_abi::Primitive::Pointer; use rustc_codegen_ssa::traits::{ BaseTypeCodegenMethods, ConstCodegenMethods, MiscCodegenMethods, StaticCodegenMethods, }; use rustc_middle::mir::Mutability; use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc, Scalar}; use rustc_middle::ty::layout::LayoutOf; -use rustc_target::abi::{self, HasDataLayout, Pointer}; use crate::consts::const_alloc_to_gcc; use crate::context::CodegenCx; diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index a7a32e285d8..7486eefeb85 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -32,6 +32,7 @@ extern crate tempfile; extern crate tracing; // The rustc crates we need +extern crate rustc_abi; extern crate rustc_apfloat; extern crate rustc_ast; extern crate rustc_attr; diff --git a/compiler/rustc_codegen_gcc/src/type_of.rs b/compiler/rustc_codegen_gcc/src/type_of.rs index 5b0d862ae6d..183e9ddf8bf 100644 --- a/compiler/rustc_codegen_gcc/src/type_of.rs +++ b/compiler/rustc_codegen_gcc/src/type_of.rs @@ -1,6 +1,9 @@ use std::fmt::Write; use gccjit::{Struct, Type}; +use rustc_abi as abi; +use rustc_abi::Primitive::*; +use rustc_abi::{Abi, FieldsShape, Integer, PointeeInfo, Size, Variants}; use rustc_codegen_ssa::traits::{ BaseTypeCodegenMethods, DerivedTypeCodegenMethods, LayoutTypeCodegenMethods, }; @@ -8,11 +11,8 @@ use rustc_middle::bug; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, CoroutineArgsExt, Ty, TypeVisitableExt}; +use rustc_target::abi::TyAbiInterface; use rustc_target::abi::call::{CastTarget, FnAbi, Reg}; -use rustc_target::abi::{ - self, Abi, FieldsShape, Float, Int, Integer, PointeeInfo, Pointer, Size, TyAbiInterface, - Variants, -}; use crate::abi::{FnAbiGcc, FnAbiGccExt, GccType}; use crate::context::CodegenCx; -- cgit 1.4.1-3-g733a5 From 0d8a978e8a55b08778ec6ee861c2c5ed6703eb6c Mon Sep 17 00:00:00 2001 From: Jed Brown Date: Fri, 5 Jan 2024 21:04:41 -0700 Subject: intrinsics.fmuladdf{16,32,64,128}: expose llvm.fmuladd.* semantics Add intrinsics `fmuladd{f16,f32,f64,f128}`. This computes `(a * b) + c`, to be fused if the code generator determines that (i) the target instruction set has support for a fused operation, and (ii) that the fused operation is more efficient than the equivalent, separate pair of `mul` and `add` instructions. https://llvm.org/docs/LangRef.html#llvm-fmuladd-intrinsic MIRI support is included for f32 and f64. The codegen_cranelift uses the `fma` function from libc, which is a correct implementation, but without the desired performance semantic. I think this requires an update to cranelift to expose a suitable instruction in its IR. I have not tested with codegen_gcc, but it should behave the same way (using `fma` from libc). --- .../rustc_codegen_cranelift/src/intrinsics/mod.rs | 5 +- compiler/rustc_codegen_gcc/src/intrinsic/mod.rs | 3 ++ compiler/rustc_codegen_llvm/src/context.rs | 5 ++ compiler/rustc_codegen_llvm/src/intrinsic.rs | 5 ++ compiler/rustc_hir_analysis/src/check/intrinsic.rs | 13 ++++++ compiler/rustc_span/src/symbol.rs | 4 ++ library/core/src/intrinsics.rs | 53 ++++++++++++++++++++++ src/tools/miri/src/intrinsics/mod.rs | 31 +++++++++++++ src/tools/miri/tests/pass/float.rs | 18 ++++++++ .../pass/intrinsics/fmuladd_nondeterministic.rs | 44 ++++++++++++++++++ tests/ui/intrinsics/intrinsic-fmuladd.rs | 42 +++++++++++++++++ 11 files changed, 222 insertions(+), 1 deletion(-) create mode 100644 src/tools/miri/tests/pass/intrinsics/fmuladd_nondeterministic.rs create mode 100644 tests/ui/intrinsics/intrinsic-fmuladd.rs (limited to 'compiler/rustc_codegen_gcc/src') diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index 19e5adc2538..35f0ccff3f9 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -328,6 +328,9 @@ fn codegen_float_intrinsic_call<'tcx>( sym::fabsf64 => ("fabs", 1, fx.tcx.types.f64, types::F64), sym::fmaf32 => ("fmaf", 3, fx.tcx.types.f32, types::F32), sym::fmaf64 => ("fma", 3, fx.tcx.types.f64, types::F64), + // FIXME: calling `fma` from libc without FMA target feature uses expensive sofware emulation + sym::fmuladdf32 => ("fmaf", 3, fx.tcx.types.f32, types::F32), // TODO: use cranelift intrinsic analogous to llvm.fmuladd.f32 + sym::fmuladdf64 => ("fma", 3, fx.tcx.types.f64, types::F64), // TODO: use cranelift intrinsic analogous to llvm.fmuladd.f64 sym::copysignf32 => ("copysignf", 2, fx.tcx.types.f32, types::F32), sym::copysignf64 => ("copysign", 2, fx.tcx.types.f64, types::F64), sym::floorf32 => ("floorf", 1, fx.tcx.types.f32, types::F32), @@ -381,7 +384,7 @@ fn codegen_float_intrinsic_call<'tcx>( let layout = fx.layout_of(ty); let res = match intrinsic { - sym::fmaf32 | sym::fmaf64 => { + sym::fmaf32 | sym::fmaf64 | sym::fmuladdf32 | sym::fmuladdf64 => { CValue::by_val(fx.bcx.ins().fma(args[0], args[1], args[2]), layout) } sym::copysignf32 | sym::copysignf64 => { diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index 945eedf5556..972d6632140 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -66,6 +66,9 @@ fn get_simple_intrinsic<'gcc, 'tcx>( sym::log2f64 => "log2", sym::fmaf32 => "fmaf", sym::fmaf64 => "fma", + // FIXME: calling `fma` from libc without FMA target feature uses expensive sofware emulation + sym::fmuladdf32 => "fmaf", // TODO: use gcc intrinsic analogous to llvm.fmuladd.f32 + sym::fmuladdf64 => "fma", // TODO: use gcc intrinsic analogous to llvm.fmuladd.f64 sym::fabsf32 => "fabsf", sym::fabsf64 => "fabs", sym::minnumf32 => "fminf", diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 0a116971e07..c836dd5473f 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -884,6 +884,11 @@ impl<'ll> CodegenCx<'ll, '_> { ifn!("llvm.fma.f64", fn(t_f64, t_f64, t_f64) -> t_f64); ifn!("llvm.fma.f128", fn(t_f128, t_f128, t_f128) -> t_f128); + ifn!("llvm.fmuladd.f16", fn(t_f16, t_f16, t_f16) -> t_f16); + ifn!("llvm.fmuladd.f32", fn(t_f32, t_f32, t_f32) -> t_f32); + ifn!("llvm.fmuladd.f64", fn(t_f64, t_f64, t_f64) -> t_f64); + ifn!("llvm.fmuladd.f128", fn(t_f128, t_f128, t_f128) -> t_f128); + ifn!("llvm.fabs.f16", fn(t_f16) -> t_f16); ifn!("llvm.fabs.f32", fn(t_f32) -> t_f32); ifn!("llvm.fabs.f64", fn(t_f64) -> t_f64); diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 30c6f08e894..bfe623e7fc3 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -86,6 +86,11 @@ fn get_simple_intrinsic<'ll>( sym::fmaf64 => "llvm.fma.f64", sym::fmaf128 => "llvm.fma.f128", + sym::fmuladdf16 => "llvm.fmuladd.f16", + sym::fmuladdf32 => "llvm.fmuladd.f32", + sym::fmuladdf64 => "llvm.fmuladd.f64", + sym::fmuladdf128 => "llvm.fmuladd.f128", + sym::fabsf16 => "llvm.fabs.f16", sym::fabsf32 => "llvm.fabs.f32", sym::fabsf64 => "llvm.fabs.f64", diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 25e219ef3f2..06317a3b304 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -357,6 +357,19 @@ pub fn check_intrinsic_type( (0, 0, vec![tcx.types.f128, tcx.types.f128, tcx.types.f128], tcx.types.f128) } + sym::fmuladdf16 => { + (0, 0, vec![tcx.types.f16, tcx.types.f16, tcx.types.f16], tcx.types.f16) + } + sym::fmuladdf32 => { + (0, 0, vec![tcx.types.f32, tcx.types.f32, tcx.types.f32], tcx.types.f32) + } + sym::fmuladdf64 => { + (0, 0, vec![tcx.types.f64, tcx.types.f64, tcx.types.f64], tcx.types.f64) + } + sym::fmuladdf128 => { + (0, 0, vec![tcx.types.f128, tcx.types.f128, tcx.types.f128], tcx.types.f128) + } + sym::fabsf16 => (0, 0, vec![tcx.types.f16], tcx.types.f16), sym::fabsf32 => (0, 0, vec![tcx.types.f32], tcx.types.f32), sym::fabsf64 => (0, 0, vec![tcx.types.f64], tcx.types.f64), diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 8e0009695db..cc3bda99a11 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -914,6 +914,10 @@ symbols! { fmt_debug, fmul_algebraic, fmul_fast, + fmuladdf128, + fmuladdf16, + fmuladdf32, + fmuladdf64, fn_align, fn_delegation, fn_must_use, diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index d7a2f1909ca..061fba9a1f7 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -1795,6 +1795,59 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn fmaf128(a: f128, b: f128, c: f128) -> f128; + /// Returns `a * b + c` for `f16` values, non-deterministically executing + /// either a fused multiply-add or two operations with rounding of the + /// intermediate result. + /// + /// The operation is fused if the code generator determines that target + /// instruction set has support for a fused operation, and that the fused + /// operation is more efficient than the equivalent, separate pair of mul + /// and add instructions. It is unspecified whether or not a fused operation + /// is selected, and that may depend on optimization level and context, for + /// example. + #[rustc_nounwind] + #[cfg(not(bootstrap))] + pub fn fmuladdf16(a: f16, b: f16, c: f16) -> f16; + /// Returns `a * b + c` for `f32` values, non-deterministically executing + /// either a fused multiply-add or two operations with rounding of the + /// intermediate result. + /// + /// The operation is fused if the code generator determines that target + /// instruction set has support for a fused operation, and that the fused + /// operation is more efficient than the equivalent, separate pair of mul + /// and add instructions. It is unspecified whether or not a fused operation + /// is selected, and that may depend on optimization level and context, for + /// example. + #[rustc_nounwind] + #[cfg(not(bootstrap))] + pub fn fmuladdf32(a: f32, b: f32, c: f32) -> f32; + /// Returns `a * b + c` for `f64` values, non-deterministically executing + /// either a fused multiply-add or two operations with rounding of the + /// intermediate result. + /// + /// The operation is fused if the code generator determines that target + /// instruction set has support for a fused operation, and that the fused + /// operation is more efficient than the equivalent, separate pair of mul + /// and add instructions. It is unspecified whether or not a fused operation + /// is selected, and that may depend on optimization level and context, for + /// example. + #[rustc_nounwind] + #[cfg(not(bootstrap))] + pub fn fmuladdf64(a: f64, b: f64, c: f64) -> f64; + /// Returns `a * b + c` for `f128` values, non-deterministically executing + /// either a fused multiply-add or two operations with rounding of the + /// intermediate result. + /// + /// The operation is fused if the code generator determines that target + /// instruction set has support for a fused operation, and that the fused + /// operation is more efficient than the equivalent, separate pair of mul + /// and add instructions. It is unspecified whether or not a fused operation + /// is selected, and that may depend on optimization level and context, for + /// example. + #[rustc_nounwind] + #[cfg(not(bootstrap))] + pub fn fmuladdf128(a: f128, b: f128, c: f128) -> f128; + /// Returns the absolute value of an `f16`. /// /// The stabilized version of this intrinsic is diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs index 665dd7c441a..9f772cfa982 100644 --- a/src/tools/miri/src/intrinsics/mod.rs +++ b/src/tools/miri/src/intrinsics/mod.rs @@ -295,6 +295,37 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(res, dest)?; } + "fmuladdf32" => { + let [a, b, c] = check_arg_count(args)?; + let a = this.read_scalar(a)?.to_f32()?; + let b = this.read_scalar(b)?.to_f32()?; + let c = this.read_scalar(c)?.to_f32()?; + let fuse: bool = this.machine.rng.get_mut().gen(); + let res = if fuse { + // FIXME: Using host floats, to work around https://github.com/rust-lang/rustc_apfloat/issues/11 + a.to_host().mul_add(b.to_host(), c.to_host()).to_soft() + } else { + ((a * b).value + c).value + }; + let res = this.adjust_nan(res, &[a, b, c]); + this.write_scalar(res, dest)?; + } + "fmuladdf64" => { + let [a, b, c] = check_arg_count(args)?; + let a = this.read_scalar(a)?.to_f64()?; + let b = this.read_scalar(b)?.to_f64()?; + let c = this.read_scalar(c)?.to_f64()?; + let fuse: bool = this.machine.rng.get_mut().gen(); + let res = if fuse { + // FIXME: Using host floats, to work around https://github.com/rust-lang/rustc_apfloat/issues/11 + a.to_host().mul_add(b.to_host(), c.to_host()).to_soft() + } else { + ((a * b).value + c).value + }; + let res = this.adjust_nan(res, &[a, b, c]); + this.write_scalar(res, dest)?; + } + "powf32" => { let [f1, f2] = check_arg_count(args)?; let f1 = this.read_scalar(f1)?.to_f32()?; diff --git a/src/tools/miri/tests/pass/float.rs b/src/tools/miri/tests/pass/float.rs index 6ab18a5345e..853d3e80517 100644 --- a/src/tools/miri/tests/pass/float.rs +++ b/src/tools/miri/tests/pass/float.rs @@ -30,6 +30,7 @@ fn main() { libm(); test_fast(); test_algebraic(); + test_fmuladd(); } trait Float: Copy + PartialEq + Debug { @@ -1041,3 +1042,20 @@ fn test_algebraic() { test_operations_f32(11., 2.); test_operations_f32(10., 15.); } + +fn test_fmuladd() { + use std::intrinsics::{fmuladdf32, fmuladdf64}; + + #[inline(never)] + pub fn test_operations_f32(a: f32, b: f32, c: f32) { + assert_approx_eq!(unsafe { fmuladdf32(a, b, c) }, a * b + c); + } + + #[inline(never)] + pub fn test_operations_f64(a: f64, b: f64, c: f64) { + assert_approx_eq!(unsafe { fmuladdf64(a, b, c) }, a * b + c); + } + + test_operations_f32(0.1, 0.2, 0.3); + test_operations_f64(1.1, 1.2, 1.3); +} diff --git a/src/tools/miri/tests/pass/intrinsics/fmuladd_nondeterministic.rs b/src/tools/miri/tests/pass/intrinsics/fmuladd_nondeterministic.rs new file mode 100644 index 00000000000..b46cf1ddf65 --- /dev/null +++ b/src/tools/miri/tests/pass/intrinsics/fmuladd_nondeterministic.rs @@ -0,0 +1,44 @@ +#![feature(core_intrinsics)] +use std::intrinsics::{fmuladdf32, fmuladdf64}; + +fn main() { + let mut saw_zero = false; + let mut saw_nonzero = false; + for _ in 0..50 { + let a = std::hint::black_box(0.1_f64); + let b = std::hint::black_box(0.2); + let c = std::hint::black_box(-a * b); + // It is unspecified whether the following operation is fused or not. The + // following evaluates to 0.0 if unfused, and nonzero (-1.66e-18) if fused. + let x = unsafe { fmuladdf64(a, b, c) }; + if x == 0.0 { + saw_zero = true; + } else { + saw_nonzero = true; + } + } + assert!( + saw_zero && saw_nonzero, + "`fmuladdf64` failed to be evaluated as both fused and unfused" + ); + + let mut saw_zero = false; + let mut saw_nonzero = false; + for _ in 0..50 { + let a = std::hint::black_box(0.1_f32); + let b = std::hint::black_box(0.2); + let c = std::hint::black_box(-a * b); + // It is unspecified whether the following operation is fused or not. The + // following evaluates to 0.0 if unfused, and nonzero (-8.1956386e-10) if fused. + let x = unsafe { fmuladdf32(a, b, c) }; + if x == 0.0 { + saw_zero = true; + } else { + saw_nonzero = true; + } + } + assert!( + saw_zero && saw_nonzero, + "`fmuladdf32` failed to be evaluated as both fused and unfused" + ); +} diff --git a/tests/ui/intrinsics/intrinsic-fmuladd.rs b/tests/ui/intrinsics/intrinsic-fmuladd.rs new file mode 100644 index 00000000000..d03297884f7 --- /dev/null +++ b/tests/ui/intrinsics/intrinsic-fmuladd.rs @@ -0,0 +1,42 @@ +//@ run-pass +#![feature(core_intrinsics)] + +use std::intrinsics::*; + +macro_rules! assert_approx_eq { + ($a:expr, $b:expr) => {{ + let (a, b) = (&$a, &$b); + assert!((*a - *b).abs() < 1.0e-6, "{} is not approximately equal to {}", *a, *b); + }}; +} + +fn main() { + unsafe { + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + assert_approx_eq!(fmuladdf32(1.23, 4.5, 0.67), 6.205); + assert_approx_eq!(fmuladdf32(-1.23, -4.5, -0.67), 4.865); + assert_approx_eq!(fmuladdf32(0.0, 8.9, 1.2), 1.2); + assert_approx_eq!(fmuladdf32(3.4, -0.0, 5.6), 5.6); + assert!(fmuladdf32(nan, 7.8, 9.0).is_nan()); + assert_eq!(fmuladdf32(inf, 7.8, 9.0), inf); + assert_eq!(fmuladdf32(neg_inf, 7.8, 9.0), neg_inf); + assert_eq!(fmuladdf32(8.9, inf, 3.2), inf); + assert_eq!(fmuladdf32(-3.2, 2.4, neg_inf), neg_inf); + } + unsafe { + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + assert_approx_eq!(fmuladdf64(1.23, 4.5, 0.67), 6.205); + assert_approx_eq!(fmuladdf64(-1.23, -4.5, -0.67), 4.865); + assert_approx_eq!(fmuladdf64(0.0, 8.9, 1.2), 1.2); + assert_approx_eq!(fmuladdf64(3.4, -0.0, 5.6), 5.6); + assert!(fmuladdf64(nan, 7.8, 9.0).is_nan()); + assert_eq!(fmuladdf64(inf, 7.8, 9.0), inf); + assert_eq!(fmuladdf64(neg_inf, 7.8, 9.0), neg_inf); + assert_eq!(fmuladdf64(8.9, inf, 3.2), inf); + assert_eq!(fmuladdf64(-3.2, 2.4, neg_inf), neg_inf); + } +} -- cgit 1.4.1-3-g733a5