Add -ffp-contract = { fast | on | off }   command line option support.

This flag sets the 'fp-contract' mode, which controls the formation of fused
floating point operations. Available modes are:

- Fast: Form fused operations anywhere. 
- On: Form fused operations where allowed by FP_CONTRACT. This is the default
      mode.
- Off: Don't form fused operations (in future this may be relaxed to forming
       fused operations where it can be proved that the result won't be
       affected).

Currently clang doesn't support the FP_CONTRACT pragma, so the 'On' and 'Off'
modes are equivalent.



git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@159794 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/Basic/LangOptions.def b/include/clang/Basic/LangOptions.def
index 381b1ba..aa4d25c 100644
--- a/include/clang/Basic/LangOptions.def
+++ b/include/clang/Basic/LangOptions.def
@@ -148,6 +148,7 @@
              "stack protector mode")
 ENUM_LANGOPT(SignedOverflowBehavior, SignedOverflowBehaviorTy, 2, SOB_Undefined,
              "signed integer overflow handling")
+ENUM_LANGOPT(FPContractMode, FPContractModeTy, 2, FPC_On, "FP_CONTRACT mode")
 
 BENIGN_LANGOPT(InstantiationDepth, 32, 512, 
                "maximum template instantiation depth")
diff --git a/include/clang/Basic/LangOptions.h b/include/clang/Basic/LangOptions.h
index 10f496f..fbb014e 100644
--- a/include/clang/Basic/LangOptions.h
+++ b/include/clang/Basic/LangOptions.h
@@ -56,6 +56,12 @@
     SOB_Trapping    // -ftrapv
   };
 
+  enum FPContractModeKind {
+    FPC_Off,        // Form fused FP ops only where result will not be affected.
+    FPC_On,         // Form fused FP ops according to FP_CONTRACT rules.
+    FPC_Fast        // Aggressively fuse FP ops (E.g. FMA).
+  };
+
 public:
   clang::ObjCRuntime ObjCRuntime;
 
diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td
index 4d23f47..48f9cd6 100644
--- a/include/clang/Driver/Options.td
+++ b/include/clang/Driver/Options.td
@@ -410,6 +410,9 @@
 def fno_honor_infinites : Flag<"-fno-honor-infinites">, Alias<fno_honor_infinities>;
 def ftrapping_math : Flag<"-ftrapping-math">, Group<f_Group>;
 def fno_trapping_math : Flag<"-fno-trapping-math">, Group<f_Group>;
+def ffp_contract : Joined<"-ffp-contract=">, Group<f_Group>,
+  Flags<[CC1Option]>, HelpText<"Form fused FP ops (e.g. FMAs): fast (everywhere)"
+  " | on (according to FP_CONTRACT pragma, default) | off (never fuse)">;
 
 def ffor_scope : Flag<"-ffor-scope">, Group<f_Group>;
 def fno_for_scope : Flag<"-fno-for-scope">, Group<f_Group>;
diff --git a/lib/CodeGen/BackendUtil.cpp b/lib/CodeGen/BackendUtil.cpp
index 5a97875..0a1915b 100644
--- a/lib/CodeGen/BackendUtil.cpp
+++ b/lib/CodeGen/BackendUtil.cpp
@@ -351,6 +351,19 @@
     Options.FloatABIType = llvm::FloatABI::Default;
   }
 
+  // Set FP fusion mode.
+  switch (LangOpts.getFPContractMode()) {
+  case LangOptions::FPC_Off:
+    Options.AllowFPOpFusion = llvm::FPOpFusion::Strict;
+    break;
+  case LangOptions::FPC_On:
+    Options.AllowFPOpFusion = llvm::FPOpFusion::Standard;
+    break;
+  case LangOptions::FPC_Fast:
+    Options.AllowFPOpFusion = llvm::FPOpFusion::Fast;
+    break;              
+  }
+
   Options.LessPreciseFPMADOption = CodeGenOpts.LessPreciseFPMAD;
   Options.NoInfsFPMath = CodeGenOpts.NoInfsFPMath;
   Options.NoNaNsFPMath = CodeGenOpts.NoNaNsFPMath;
diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp
index 98beb22..dd198a1 100644
--- a/lib/Driver/Tools.cpp
+++ b/lib/Driver/Tools.cpp
@@ -1805,6 +1805,24 @@
       !TrappingMath)
     CmdArgs.push_back("-menable-unsafe-fp-math");
 
+
+  // Validate and pass through -fp-contract option. 
+  if (Arg *A = Args.getLastArg(options::OPT_ffast_math,
+                               options::OPT_ffp_contract)) {
+    if (A->getOption().getID() == options::OPT_ffp_contract) {
+      StringRef Val = A->getValue(Args);
+      if (Val == "fast" || Val == "on" || Val == "off") {
+        CmdArgs.push_back(Args.MakeArgString("-ffp-contract=" + Val));
+      } else {
+        D.Diag(diag::err_drv_unsupported_option_argument)
+          << A->getOption().getName() << Val;
+      }
+    } else { // A is OPT_ffast_math
+      // If fast-math is set then set the fp-contract mode to fast.
+      CmdArgs.push_back(Args.MakeArgString("-ffp-contract=fast"));
+    }
+  }
+
   // We separately look for the '-ffast-math' flag, and if we find it, tell the
   // frontend to provide the appropriate preprocessor macros. This is distinct
   // from enabling any optimizations as it induces a language change which must
diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp
index 943cb20..cf08913 100644
--- a/lib/Frontend/CompilerInvocation.cpp
+++ b/lib/Frontend/CompilerInvocation.cpp
@@ -759,6 +759,11 @@
       Res.push_back("-ftrapv-handler", Opts.OverflowHandler);
     break;
   }
+  switch (Opts.getFPContractMode()) {
+  case LangOptions::FPC_Off:  Res.push_back("-ffp-contract=off"); break;
+  case LangOptions::FPC_On:   Res.push_back("-ffp-contract=on"); break;
+  case LangOptions::FPC_Fast: Res.push_back("-ffp-contract=fast"); break;
+  }
   if (Opts.HeinousExtensions)
     Res.push_back("-fheinous-gnu-extensions");
   // Optimize is implicit.
@@ -1975,6 +1980,18 @@
     Diags.Report(diag::err_drv_invalid_value)
       << Args.getLastArg(OPT_fvisibility)->getAsString(Args) << Vis;
 
+  if (Arg *A = Args.getLastArg(OPT_ffp_contract)) {
+    StringRef Val = A->getValue(Args);
+    if (Val == "fast")
+      Opts.setFPContractMode(LangOptions::FPC_Fast);
+    else if (Val == "on")
+      Opts.setFPContractMode(LangOptions::FPC_On);
+    else if (Val == "off")
+      Opts.setFPContractMode(LangOptions::FPC_Off);
+    else
+      Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Val;
+  }
+
   if (Args.hasArg(OPT_fvisibility_inlines_hidden))
     Opts.InlineVisibilityHidden = 1;
 
diff --git a/test/CodeGen/fp-contract.c b/test/CodeGen/fp-contract.c
new file mode 100644
index 0000000..c048d69
--- /dev/null
+++ b/test/CodeGen/fp-contract.c
@@ -0,0 +1,8 @@
+// RUN: %clang_cc1 -O3 -ffp-contract=fast -triple=powerpc-apple-darwin10 -S -o - %s | FileCheck %s
+
+float fma_test1(float a, float b, float c) {
+// CHECK: fmadds
+  float x = a * b;
+  float y = x + c;
+  return y;
+}
diff --git a/test/Driver/clang_f_opts.c b/test/Driver/clang_f_opts.c
index 4eed4aa..b74b285 100644
--- a/test/Driver/clang_f_opts.c
+++ b/test/Driver/clang_f_opts.c
@@ -29,3 +29,9 @@
 // RUN: %clang -### -c -Wdeprecated %s 2>&1 | FileCheck -check-prefix=DEPRECATED-OFF-CHECK %s
 // DEPRECATED-ON-CHECK: -fdeprecated-macro
 // DEPRECATED-OFF-CHECK-NOT: -fdeprecated-macro
+
+// RUN: %clang -### -S -ffp-contract=fast %s 2>&1 | FileCheck -check-prefix=FP-CONTRACT-FAST-CHECK %s
+// RUN: %clang -### -S -ffast-math %s 2>&1 | FileCheck -check-prefix=FP-CONTRACT-FAST-CHECK %s
+// RUN: %clang -### -S -ffp-contract=off %s 2>&1 | FileCheck -check-prefix=FP-CONTRACT-OFF-CHECK %s
+// FP-CONTRACT-FAST-CHECK: -ffp-contract=fast
+// FP-CONTRACT-OFF-CHECK: -ffp-contract=off