Index: clang/include/clang/Basic/DiagnosticFrontendKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticFrontendKinds.td +++ clang/include/clang/Basic/DiagnosticFrontendKinds.td @@ -48,6 +48,16 @@ "overriding currently unsupported use of floating point exceptions " "on this target">, InGroup; +def err_incompatible_fp_eval_method_option_with_fapprox_func : Error< + "'fapprox-func' produces inaccurate arithmetic results. It is illegal when" + " 'ffp-eval-method' is set.">; +def err_incompatible_fp_eval_method_option_with_mreassociate : Error< + "'mreassociate' produces inaccurate arithmetic results. It is illegal when" + " 'ffp-eval-method' is set.">; +def err_incompatible_fp_eval_method_option_with_freciprocal : Error< + "'freciprocal' produces inaccurate arithmetic results. It is illegal when" + " 'ffp-eval-method' is set.">; + def remark_fe_backend_optimization_remark : Remark<"%0">, BackendInfo, InGroup; def remark_fe_backend_optimization_remark_missed : Remark<"%0">, BackendInfo, Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -6475,6 +6475,19 @@ "comparing floating point with == or != is unsafe">, InGroup>, DefaultIgnore; +def warn_pragma_clang_fp_reassociate_used_with_eval_method_setting : Warning< + "eval method setting via '%0' cannot be used with " + "'pragma_clang_fp_eval_reassociate'">, InGroup; +def warn_pragma_clang_fp_eval_method_used_with_fapprox_func : Warning< + "'#pragma clang fp eval_method' cannot be used with 'fapprox_func'">, + InGroup; +def warn_pragma_clang_fp_eval_method_used_with_mreassociate : Warning< + "'#pragma clang fp eval_method' cannot be used with 'mreassociate'">, + InGroup; +def warn_pragma_clang_fp_eval_method_used_with_freciprocal : Warning< + "'#pragma clang fp eval_method' cannot be used with 'freciprocal'">, + InGroup; + def warn_remainder_division_by_zero : Warning< "%select{remainder|division}0 by zero is undefined">, InGroup; Index: clang/lib/Frontend/CompilerInvocation.cpp =================================================================== --- clang/lib/Frontend/CompilerInvocation.cpp +++ clang/lib/Frontend/CompilerInvocation.cpp @@ -503,6 +503,25 @@ Diags.Report(diag::warn_ignored_hip_only_option) << Args.getLastArg(OPT_gpu_max_threads_per_block_EQ)->getAsString(Args); + // When these options are used, the compiler is allowed to apply + // optimizations that may affect the final result. For example + // (x+y)+z is transformed to x+(y+z) but may not give the same + // final result; it's not value safe. + // Another example can be to simplify x/x to 1.0 but x could be 0.0, INF + // or NaN. Final result may then differ. An error is issued when the eval + // method is set with one of these options. + if (Args.hasArg(OPT_ffp_eval_method_EQ)) { + if (LangOpts.ApproxFunc) + Diags.Report( + diag::err_incompatible_fp_eval_method_option_with_fapprox_func); + if (LangOpts.AllowFPReassoc) + Diags.Report( + diag::err_incompatible_fp_eval_method_option_with_mreassociate); + if (LangOpts.AllowRecip) + Diags.Report( + diag::err_incompatible_fp_eval_method_option_with_freciprocal); + } + // -cl-strict-aliasing needs to emit diagnostic in the case where CL > 1.0. // This option should be deprecated for CL > 1.0 because // this option was added for compatibility with OpenCL 1.0. Index: clang/lib/Sema/SemaAttr.cpp =================================================================== --- clang/lib/Sema/SemaAttr.cpp +++ clang/lib/Sema/SemaAttr.cpp @@ -486,6 +486,15 @@ NewFPFeatures.setFPEvalMethodOverride(LangOptions::FEM_Extended); break; } + if (getLangOpts().ApproxFunc) + Diag(Loc, diag::warn_pragma_clang_fp_eval_method_used_with_fapprox_func) + << "#pragma clang fp eval_method"; + if (getLangOpts().AllowFPReassoc) + Diag(Loc, diag::warn_pragma_clang_fp_eval_method_used_with_mreassociate) + << "#pragma clang fp eval_method"; + if (getLangOpts().AllowRecip) + Diag(Loc, diag::warn_pragma_clang_fp_eval_method_used_with_freciprocal) + << "#pragma clang fp eval_method"; FpPragmaStack.Act(Loc, PSK_Set, StringRef(), NewFPFeatures); CurFPFeatures = NewFPFeatures.applyOverrides(getLangOpts()); PP.setCurrentFPEvalMethod(Loc, Value); @@ -1136,6 +1145,19 @@ } void Sema::ActOnPragmaFPReassociate(SourceLocation Loc, bool IsEnabled) { + if (IsEnabled) { + StringRef Reason = ""; + if (getLangOpts().getFPEvalMethod() != LangOptions::FEM_UnsetOnCommandLine) + Reason = "option ffp-eval-method"; + // Here we could have both an option and a pragma that set the eval method. + // The pragma overrides the option in the command line. + if (PP.getLastFPEvalPragmaLocation().isValid()) + Reason = "#pragma clang fp eval_method"; + if (Reason != "") + Diag(Loc, + diag::warn_pragma_clang_fp_reassociate_used_with_eval_method_setting) + << Reason; + } FPOptionsOverride NewFPFeatures = CurFPFeatureOverrides(); NewFPFeatures.setAllowFPReassociateOverride(IsEnabled); FpPragmaStack.Act(Loc, PSK_Set, StringRef(), NewFPFeatures); Index: clang/test/Sema/eval-method-with-unsafe-math.c =================================================================== --- /dev/null +++ clang/test/Sema/eval-method-with-unsafe-math.c @@ -0,0 +1,82 @@ +// RUN: not %clang_cc1 -fexperimental-strict-floating-point \ +// RUN: -triple x86_64-linux-gnu \ +// RUN: -verify %s 2>&1 | FileCheck %s --check-prefixes=WARN-PRGM + +// RUN: not %clang_cc1 -fexperimental-strict-floating-point \ +// RUN: -triple x86_64-linux-gnu -freciprocal-math \ +// RUN: -verify %s 2>&1 | FileCheck %s --check-prefixes=WARN-RECPR,WARN-PRGM + +// RUN: not %clang_cc1 -fexperimental-strict-floating-point \ +// RUN: -triple x86_64-linux-gnu -mreassociate \ +// RUN: -verify %s 2>&1 | FileCheck %s --check-prefixes=WARN-REC,WARN-PRGM + +// RUN: not %clang_cc1 -fexperimental-strict-floating-point \ +// RUN: -triple x86_64-linux-gnu -fapprox-func \ +// RUN: -verify %s 2>&1 | FileCheck %s --check-prefixes=WARN-FUNC,WARN-PRGM + +// RUN: not %clang_cc1 -fexperimental-strict-floating-point \ +// RUN: -triple x86_64-linux-gnu -freciprocal-math -mreassociate -verify \ +// RUN: %s 2>&1 | FileCheck %s --check-prefixes=WARN-REC,WARN-RECPR,WARN-PRGM + +// RUN: not %clang_cc1 -fexperimental-strict-floating-point \ +// RUN: -triple x86_64-linux-gnu -freciprocal-math -mreassociate -fapprox-func \ +// RUN: -verify %s 2>&1 \ +// RUN: | FileCheck %s --check-prefixes=WARN-FUNC,WARN-REC,WARN-RECPR,WARN-PRGM + +// RUN: not %clang_cc1 -fexperimental-strict-floating-point \ +// RUN: -triple x86_64-linux-gnu -fapprox-func -ffp-eval-method=source \ +// RUN: -verify %s 2>&1 | FileCheck %s --check-prefixes=CHECK-FUNC + +// RUN: not %clang_cc1 -fexperimental-strict-floating-point \ +// RUN: -triple x86_64-linux-gnu -mreassociate -ffp-eval-method=source \ +// RUN: -verify %s 2>&1 | FileCheck %s --check-prefixes=CHECK-ASSOC + +// RUN: not %clang_cc1 -fexperimental-strict-floating-point \ +// RUN: -triple x86_64-linux-gnu -freciprocal-math -ffp-eval-method=source \ +// RUN: -verify %s 2>&1 | FileCheck %s --check-prefixes=CHECK-RECPR + +// RUN: not %clang_cc1 -fexperimental-strict-floating-point \ +// RUN: -triple x86_64-linux-gnu -freciprocal-math -mreassociate \ +// RUN: -ffp-eval-method=source \ +// RUN: -verify %s 2>&1 | FileCheck %s --check-prefixes=CHECK-ASSOC,CHECK-RECPR + +// RUN: not %clang_cc1 -fexperimental-strict-floating-point \ +// RUN: -triple x86_64-linux-gnu -freciprocal-math -mreassociate -fapprox-func \ +// RUN: -ffp-eval-method=source -verify %s 2>&1 \ +// RUN: | FileCheck %s --check-prefixes=CHECK-ASSOC,CHECK-RECPR,CHECK-FUNC + +// RUN: not %clang_cc1 -fexperimental-strict-floating-point \ +// RUN: -triple x86_64-linux-gnu -ffp-eval-method=source \ +// RUN: -verify %s 2>&1 | FileCheck %s --check-prefixes=WARN-FUNC-OPT,WARN-PRGM + +// CHECK-FUNC: (frontend): 'fapprox-func' produces inaccurate arithmetic results. It is illegal when 'ffp-eval-method' is set. +// CHECK-ASSOC: (frontend): 'mreassociate' produces inaccurate arithmetic results. It is illegal when 'ffp-eval-method' is set. +// CHECK-RECPR: (frontend): 'freciprocal' produces inaccurate arithmetic results. It is illegal when 'ffp-eval-method' is set. + +// expected-no-diagnostics + +float f1(float a, float b, float c) { + a = b + c; + return a * b + c; +} + +float f2(float a, float b, float c) { + // WARN-FUNC-OPT: eval method setting via 'option ffp-eval-method' cannot be used with 'pragma_clang_fp_eval_reassociate' +#pragma clang fp reassociate (on) + return (a + b) + c; +} + +float f3(float a, float b, float c) { +#pragma clang fp reassociate (off) + return (a - b) - c; +} + +float f4(float a, float b, float c) { +#pragma clang fp eval_method (double) + // WARN-FUNC: '#pragma clang fp eval_method' cannot be used with 'fapprox_func' + // WARN-REC: '#pragma clang fp eval_method' cannot be used with 'mreassociate' + // WARN-RECPR: '#pragma clang fp eval_method' cannot be used with 'freciprocal' + // WARN-PRGM: eval method setting via '#pragma clang fp eval_method' cannot be used with 'pragma_clang_fp_eval_reassociate' +#pragma clang fp reassociate (on) + return (a * c) - (b * c); +}