mibintc updated this revision to Diff 244894.
mibintc added a comment.

I found the problem in the #pragma float_control (push/pop) stack, it was just 
a dumb bug.

I also added -include-pch test cases, and added code to ASTWriter ASTReader to 
preserve the floatcontrol pragma stack

For 2 of the new floatcontrol test cases, I temporarily added XFAIL because the 
patch to default -ffp-precise=fast and ffp-contract=on has been withdrawn (will 
re-commit soon).


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D72841/new/

https://reviews.llvm.org/D72841

Files:
  clang/docs/LanguageExtensions.rst
  clang/include/clang/AST/Stmt.h
  clang/include/clang/Basic/DiagnosticParseKinds.td
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/include/clang/Basic/LangOptions.def
  clang/include/clang/Basic/LangOptions.h
  clang/include/clang/Basic/PragmaKinds.h
  clang/include/clang/Basic/TokenKinds.def
  clang/include/clang/Parse/Parser.h
  clang/include/clang/Sema/Sema.h
  clang/include/clang/Serialization/ASTBitCodes.h
  clang/include/clang/Serialization/ASTReader.h
  clang/include/clang/Serialization/ASTWriter.h
  clang/lib/CodeGen/CGExprScalar.cpp
  clang/lib/CodeGen/CodeGenFunction.cpp
  clang/lib/CodeGen/CodeGenFunction.h
  clang/lib/Frontend/CompilerInvocation.cpp
  clang/lib/Parse/ParseDeclCXX.cpp
  clang/lib/Parse/ParsePragma.cpp
  clang/lib/Parse/ParseStmt.cpp
  clang/lib/Parse/Parser.cpp
  clang/lib/Sema/Sema.cpp
  clang/lib/Sema/SemaAttr.cpp
  clang/lib/Sema/SemaExpr.cpp
  clang/lib/Sema/SemaStmt.cpp
  clang/lib/Serialization/ASTReader.cpp
  clang/lib/Serialization/ASTWriter.cpp
  clang/test/CodeGen/constrained-math-builtins.c
  clang/test/CodeGen/fast-math.c
  clang/test/CodeGen/fp-contract-on-pragma.cpp
  clang/test/CodeGen/fp-contract-pragma.cpp
  clang/test/CodeGen/fp-floatcontrol-class.cpp
  clang/test/CodeGen/fp-floatcontrol-pragma.cpp
  clang/test/CodeGen/fp-floatcontrol-stack.cpp
  clang/test/CodeGen/fpconstrained.c
  clang/test/CodeGen/fpconstrained.cpp
  clang/test/CodeGenOpenCL/builtins-amdgcn-dl-insts.cl
  clang/test/CodeGenOpenCL/builtins-amdgcn-gfx9.cl
  clang/test/CodeGenOpenCL/builtins-amdgcn-interp.cl
  clang/test/CodeGenOpenCL/builtins-amdgcn-mfma.cl
  clang/test/CodeGenOpenCL/builtins-amdgcn-vi.cl
  clang/test/CodeGenOpenCL/builtins-amdgcn.cl
  clang/test/CodeGenOpenCL/builtins-f16.cl
  clang/test/CodeGenOpenCL/builtins-r600.cl
  clang/test/CodeGenOpenCL/relaxed-fpmath.cl
  clang/test/CodeGenOpenCL/single-precision-constant.cl
  clang/test/PCH/pragma-floatcontrol.c
  clang/test/Parser/fp-floatcontrol-syntax.cpp
  llvm/include/llvm/IR/IRBuilder.h

Index: llvm/include/llvm/IR/IRBuilder.h
===================================================================
--- llvm/include/llvm/IR/IRBuilder.h
+++ llvm/include/llvm/IR/IRBuilder.h
@@ -215,6 +215,8 @@
   /// Get the flags to be applied to created floating point ops
   FastMathFlags getFastMathFlags() const { return FMF; }
 
+  FastMathFlags& getFastMathFlags() { return FMF; }
+
   /// Clear the fast-math flags.
   void clearFastMathFlags() { FMF.clear(); }
 
@@ -299,10 +301,16 @@
     IRBuilderBase &Builder;
     FastMathFlags FMF;
     MDNode *FPMathTag;
+    bool IsFPConstrained;
+    fp::ExceptionBehavior DefaultConstrainedExcept;
+    fp::RoundingMode DefaultConstrainedRounding;
 
   public:
     FastMathFlagGuard(IRBuilderBase &B)
-        : Builder(B), FMF(B.FMF), FPMathTag(B.DefaultFPMathTag) {}
+        : Builder(B), FMF(B.FMF), FPMathTag(B.DefaultFPMathTag),
+          IsFPConstrained(B.IsFPConstrained),
+          DefaultConstrainedExcept(B.DefaultConstrainedExcept),
+          DefaultConstrainedRounding(B.DefaultConstrainedRounding) {}
 
     FastMathFlagGuard(const FastMathFlagGuard &) = delete;
     FastMathFlagGuard &operator=(const FastMathFlagGuard &) = delete;
@@ -310,6 +318,9 @@
     ~FastMathFlagGuard() {
       Builder.FMF = FMF;
       Builder.DefaultFPMathTag = FPMathTag;
+      Builder.IsFPConstrained = IsFPConstrained;
+      Builder.DefaultConstrainedExcept = DefaultConstrainedExcept;
+      Builder.DefaultConstrainedRounding = DefaultConstrainedRounding;
     }
   };
 
Index: clang/test/Parser/fp-floatcontrol-syntax.cpp
===================================================================
--- /dev/null
+++ clang/test/Parser/fp-floatcontrol-syntax.cpp
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -DCHECK_ERROR %s
+
+float function_scope(float a) {
+#  pragma float_control(precise, on) junk // expected-warning {{extra tokens at end of '#pragma float_control' - ignored}}
+  return a;
+}
+
+#ifdef CHECK_ERROR
+#  pragma float_control(push)
+#  pragma float_control(pop)
+#  pragma float_control(precise,on,push)
+void check_stack() {
+#pragma float_control(push) // expected-error {{can only appear at file scope}}
+#pragma float_control(pop) // expected-error {{can only appear at file scope}}
+#pragma float_control(precise,on,push) // expected-error {{can only appear at file scope}}
+#pragma float_control(except,on,push) // expected-error {{can only appear at file scope}}
+#pragma float_control(except,on,push,junk) // expected-error {{float_control is malformed}}
+  return;
+}
+#endif
Index: clang/test/PCH/pragma-floatcontrol.c
===================================================================
--- /dev/null
+++ clang/test/PCH/pragma-floatcontrol.c
@@ -0,0 +1,55 @@
+// Test this without pch.
+// RUN: %clang_cc1 %s -include %s -verify -fsyntax-only -DSET
+// RUN: %clang_cc1 %s -include %s -verify -fsyntax-only -DPUSH
+// RUN: %clang_cc1 %s -include %s -verify -fsyntax-only -DPUSH_POP
+
+// Test with pch.
+// RUN: %clang_cc1 %s -DSET -emit-pch -o %t
+// RUN: %clang_cc1 %s -DSET -include-pch %t -emit-llvm -o - | FileCheck --check-prefix=CHECK-EBSTRICT %s
+// RUN: %clang_cc1 %s -DPUSH -emit-pch -o %t
+// RUN: %clang_cc1 %s -DPUSH -verify -include-pch %t
+// RUN: %clang_cc1 %s -DPUSH_POP -emit-pch -o %t
+// RUN: %clang_cc1 %s -DPUSH_POP -verify -include-pch %t
+
+#ifndef HEADER
+#define HEADER
+
+#ifdef SET
+#pragma float_control(except, on)
+#endif
+
+#ifdef PUSH
+#pragma float_control(precise, on)
+#pragma float_control (push)
+#pragma float_control(precise, off)
+#endif
+
+#ifdef PUSH_POP
+#pragma float_control (precise, on, push)
+#pragma float_control (push)
+#pragma float_control (pop)
+#endif
+#else
+
+#ifdef SET
+float fun(float a, float b) {
+// CHECK-LABEL: define float @fun{{.*}}
+//CHECK-EBSTRICT: llvm.experimental.constrained.fmul{{.*}}tonearest{{.*}}strict
+//CHECK-EBSTRICT: llvm.experimental.constrained.fadd{{.*}}tonearest{{.*}}strict
+  return a*b + 2;
+}
+#pragma float_control(pop) // expected-warning {{#pragma float_control(pop, ...) failed: stack empty}}
+#pragma float_control(pop) // expected-warning {{#pragma float_control(pop, ...) failed: stack empty}}
+#endif
+
+#ifdef PUSH
+#pragma float_control(pop)
+#pragma float_control(pop) // expected-warning {{#pragma float_control(pop, ...) failed: stack empty}}
+#endif
+
+#ifdef PUSH_POP
+#pragma float_control(pop)
+#pragma float_control(pop) // expected-warning {{#pragma float_control(pop, ...) failed: stack empty}}
+#endif
+
+#endif //ifndef HEADER
Index: clang/test/CodeGenOpenCL/single-precision-constant.cl
===================================================================
--- clang/test/CodeGenOpenCL/single-precision-constant.cl
+++ clang/test/CodeGenOpenCL/single-precision-constant.cl
@@ -1,6 +1,6 @@
 // RUN: %clang_cc1 %s -cl-single-precision-constant -emit-llvm -o - | FileCheck %s
 
 float fn(float f) {
-  // CHECK: tail call float @llvm.fmuladd.f32(float %f, float 2.000000e+00, float 1.000000e+00)
+  // CHECK: tail call contract float @llvm.fmuladd.f32(float %f, float 2.000000e+00, float 1.000000e+00)
   return f*2. + 1.;
 }
Index: clang/test/CodeGenOpenCL/relaxed-fpmath.cl
===================================================================
--- clang/test/CodeGenOpenCL/relaxed-fpmath.cl
+++ clang/test/CodeGenOpenCL/relaxed-fpmath.cl
@@ -8,12 +8,12 @@
 float spscalardiv(float a, float b) {
   // CHECK: @spscalardiv(
 
-  // NORMAL: fdiv float
+  // NORMAL: fdiv contract float
   // FAST: fdiv fast float
-  // FINITE: fdiv nnan ninf float
-  // UNSAFE: fdiv nnan nsz float
-  // MAD: fdiv float
-  // NOSIGNED: fdiv nsz float
+  // FINITE: fdiv nnan ninf contract float
+  // UNSAFE: fdiv nnan nsz contract float
+  // MAD: fdiv contract float
+  // NOSIGNED: fdiv nsz contract float
   return a / b;
 }
 // CHECK: attributes
Index: clang/test/CodeGenOpenCL/builtins-r600.cl
===================================================================
--- clang/test/CodeGenOpenCL/builtins-r600.cl
+++ clang/test/CodeGenOpenCL/builtins-r600.cl
@@ -2,7 +2,7 @@
 // RUN: %clang_cc1 -triple r600-unknown-unknown -target-cpu cypress -S -emit-llvm -o - %s | FileCheck %s
 
 // CHECK-LABEL: @test_recipsqrt_ieee_f32
-// CHECK: call float @llvm.r600.recipsqrt.ieee.f32
+// CHECK: call contract float @llvm.r600.recipsqrt.ieee.f32
 void test_recipsqrt_ieee_f32(global float* out, float a)
 {
   *out = __builtin_r600_recipsqrt_ieeef(a);
@@ -10,7 +10,7 @@
 
 #if cl_khr_fp64
 // XCHECK-LABEL: @test_recipsqrt_ieee_f64
-// XCHECK: call double @llvm.r600.recipsqrt.ieee.f64
+// XCHECK: call contract double @llvm.r600.recipsqrt.ieee.f64
 void test_recipsqrt_ieee_f64(global double* out, double a)
 {
   *out = __builtin_r600_recipsqrt_ieee(a);
Index: clang/test/CodeGenOpenCL/builtins-f16.cl
===================================================================
--- clang/test/CodeGenOpenCL/builtins-f16.cl
+++ clang/test/CodeGenOpenCL/builtins-f16.cl
@@ -6,66 +6,66 @@
 void test_half_builtins(half h0, half h1, half h2) {
   volatile half res;
 
-  // CHECK: call half @llvm.copysign.f16(half %h0, half %h1)
+  // CHECK: call contract half @llvm.copysign.f16(half %h0, half %h1)
   res = __builtin_copysignf16(h0, h1);
 
-  // CHECK: call half @llvm.fabs.f16(half %h0)
+  // CHECK: call contract half @llvm.fabs.f16(half %h0)
   res = __builtin_fabsf16(h0);
 
-  // CHECK: call half @llvm.ceil.f16(half %h0)
+  // CHECK: call contract half @llvm.ceil.f16(half %h0)
   res = __builtin_ceilf16(h0);
 
-  // CHECK: call half @llvm.cos.f16(half %h0)
+  // CHECK: call contract half @llvm.cos.f16(half %h0)
   res = __builtin_cosf16(h0);
 
-  // CHECK: call half @llvm.exp.f16(half %h0)
+  // CHECK: call contract half @llvm.exp.f16(half %h0)
   res = __builtin_expf16(h0);
 
-  // CHECK: call half @llvm.exp2.f16(half %h0)
+  // CHECK: call contract half @llvm.exp2.f16(half %h0)
   res = __builtin_exp2f16(h0);
 
-  // CHECK: call half @llvm.floor.f16(half %h0)
+  // CHECK: call contract half @llvm.floor.f16(half %h0)
   res = __builtin_floorf16(h0);
 
-  // CHECK: call half @llvm.fma.f16(half %h0, half %h1, half %h2)
+  // CHECK: call contract half @llvm.fma.f16(half %h0, half %h1, half %h2)
   res = __builtin_fmaf16(h0, h1 ,h2);
 
-  // CHECK: call half @llvm.maxnum.f16(half %h0, half %h1)
+  // CHECK: call contract half @llvm.maxnum.f16(half %h0, half %h1)
   res = __builtin_fmaxf16(h0, h1);
 
-  // CHECK: call half @llvm.minnum.f16(half %h0, half %h1)
+  // CHECK: call contract half @llvm.minnum.f16(half %h0, half %h1)
   res = __builtin_fminf16(h0, h1);
 
-  // CHECK: frem half %h0, %h1
+  // CHECK: frem contract half %h0, %h1
   res = __builtin_fmodf16(h0, h1);
 
-  // CHECK: call half @llvm.pow.f16(half %h0, half %h1)
+  // CHECK: call contract half @llvm.pow.f16(half %h0, half %h1)
   res = __builtin_powf16(h0, h1);
 
-  // CHECK: call half @llvm.log10.f16(half %h0)
+  // CHECK: call contract half @llvm.log10.f16(half %h0)
   res = __builtin_log10f16(h0);
 
-  // CHECK: call half @llvm.log2.f16(half %h0)
+  // CHECK: call contract half @llvm.log2.f16(half %h0)
   res = __builtin_log2f16(h0);
 
-  // CHECK: call half @llvm.log.f16(half %h0)
+  // CHECK: call contract half @llvm.log.f16(half %h0)
   res = __builtin_logf16(h0);
 
-  // CHECK: call half @llvm.rint.f16(half %h0)
+  // CHECK: call contract half @llvm.rint.f16(half %h0)
   res = __builtin_rintf16(h0);
 
-  // CHECK: call half @llvm.round.f16(half %h0)
+  // CHECK: call contract half @llvm.round.f16(half %h0)
   res = __builtin_roundf16(h0);
 
-  // CHECK: call half @llvm.sin.f16(half %h0)
+  // CHECK: call contract half @llvm.sin.f16(half %h0)
   res = __builtin_sinf16(h0);
 
-  // CHECK: call half @llvm.sqrt.f16(half %h0)
+  // CHECK: call contract half @llvm.sqrt.f16(half %h0)
   res = __builtin_sqrtf16(h0);
 
-  // CHECK: call half @llvm.trunc.f16(half %h0)
+  // CHECK: call contract half @llvm.trunc.f16(half %h0)
   res = __builtin_truncf16(h0);
 
-  // CHECK: call half @llvm.canonicalize.f16(half %h0)
+  // CHECK: call contract half @llvm.canonicalize.f16(half %h0)
   res = __builtin_canonicalizef16(h0);
 }
Index: clang/test/CodeGenOpenCL/builtins-amdgcn.cl
===================================================================
--- clang/test/CodeGenOpenCL/builtins-amdgcn.cl
+++ clang/test/CodeGenOpenCL/builtins-amdgcn.cl
@@ -61,133 +61,133 @@
 }
 
 // CHECK-LABEL: @test_div_fmas_f32
-// CHECK: call float @llvm.amdgcn.div.fmas.f32
+// CHECK: call contract float @llvm.amdgcn.div.fmas.f32
 void test_div_fmas_f32(global float* out, float a, float b, float c, int d)
 {
   *out = __builtin_amdgcn_div_fmasf(a, b, c, d);
 }
 
 // CHECK-LABEL: @test_div_fmas_f64
-// CHECK: call double @llvm.amdgcn.div.fmas.f64
+// CHECK: call contract double @llvm.amdgcn.div.fmas.f64
 void test_div_fmas_f64(global double* out, double a, double b, double c, int d)
 {
   *out = __builtin_amdgcn_div_fmas(a, b, c, d);
 }
 
 // CHECK-LABEL: @test_div_fixup_f32
-// CHECK: call float @llvm.amdgcn.div.fixup.f32
+// CHECK: call contract float @llvm.amdgcn.div.fixup.f32
 void test_div_fixup_f32(global float* out, float a, float b, float c)
 {
   *out = __builtin_amdgcn_div_fixupf(a, b, c);
 }
 
 // CHECK-LABEL: @test_div_fixup_f64
-// CHECK: call double @llvm.amdgcn.div.fixup.f64
+// CHECK: call contract double @llvm.amdgcn.div.fixup.f64
 void test_div_fixup_f64(global double* out, double a, double b, double c)
 {
   *out = __builtin_amdgcn_div_fixup(a, b, c);
 }
 
 // CHECK-LABEL: @test_trig_preop_f32
-// CHECK: call float @llvm.amdgcn.trig.preop.f32
+// CHECK: call contract float @llvm.amdgcn.trig.preop.f32
 void test_trig_preop_f32(global float* out, float a, int b)
 {
   *out = __builtin_amdgcn_trig_preopf(a, b);
 }
 
 // CHECK-LABEL: @test_trig_preop_f64
-// CHECK: call double @llvm.amdgcn.trig.preop.f64
+// CHECK: call contract double @llvm.amdgcn.trig.preop.f64
 void test_trig_preop_f64(global double* out, double a, int b)
 {
   *out = __builtin_amdgcn_trig_preop(a, b);
 }
 
 // CHECK-LABEL: @test_rcp_f32
-// CHECK: call float @llvm.amdgcn.rcp.f32
+// CHECK: call contract float @llvm.amdgcn.rcp.f32
 void test_rcp_f32(global float* out, float a)
 {
   *out = __builtin_amdgcn_rcpf(a);
 }
 
 // CHECK-LABEL: @test_rcp_f64
-// CHECK: call double @llvm.amdgcn.rcp.f64
+// CHECK: call contract double @llvm.amdgcn.rcp.f64
 void test_rcp_f64(global double* out, double a)
 {
   *out = __builtin_amdgcn_rcp(a);
 }
 
 // CHECK-LABEL: @test_rsq_f32
-// CHECK: call float @llvm.amdgcn.rsq.f32
+// CHECK: call contract float @llvm.amdgcn.rsq.f32
 void test_rsq_f32(global float* out, float a)
 {
   *out = __builtin_amdgcn_rsqf(a);
 }
 
 // CHECK-LABEL: @test_rsq_f64
-// CHECK: call double @llvm.amdgcn.rsq.f64
+// CHECK: call contract double @llvm.amdgcn.rsq.f64
 void test_rsq_f64(global double* out, double a)
 {
   *out = __builtin_amdgcn_rsq(a);
 }
 
 // CHECK-LABEL: @test_rsq_clamp_f32
-// CHECK: call float @llvm.amdgcn.rsq.clamp.f32
+// CHECK: call contract float @llvm.amdgcn.rsq.clamp.f32
 void test_rsq_clamp_f32(global float* out, float a)
 {
   *out = __builtin_amdgcn_rsq_clampf(a);
 }
 
 // CHECK-LABEL: @test_rsq_clamp_f64
-// CHECK: call double @llvm.amdgcn.rsq.clamp.f64
+// CHECK: call contract double @llvm.amdgcn.rsq.clamp.f64
 void test_rsq_clamp_f64(global double* out, double a)
 {
   *out = __builtin_amdgcn_rsq_clamp(a);
 }
 
 // CHECK-LABEL: @test_sin_f32
-// CHECK: call float @llvm.amdgcn.sin.f32
+// CHECK: call contract float @llvm.amdgcn.sin.f32
 void test_sin_f32(global float* out, float a)
 {
   *out = __builtin_amdgcn_sinf(a);
 }
 
 // CHECK-LABEL: @test_cos_f32
-// CHECK: call float @llvm.amdgcn.cos.f32
+// CHECK: call contract float @llvm.amdgcn.cos.f32
 void test_cos_f32(global float* out, float a)
 {
   *out = __builtin_amdgcn_cosf(a);
 }
 
 // CHECK-LABEL: @test_log_clamp_f32
-// CHECK: call float @llvm.amdgcn.log.clamp.f32
+// CHECK: call contract float @llvm.amdgcn.log.clamp.f32
 void test_log_clamp_f32(global float* out, float a)
 {
   *out = __builtin_amdgcn_log_clampf(a);
 }
 
 // CHECK-LABEL: @test_ldexp_f32
-// CHECK: call float @llvm.amdgcn.ldexp.f32
+// CHECK: call contract float @llvm.amdgcn.ldexp.f32
 void test_ldexp_f32(global float* out, float a, int b)
 {
   *out = __builtin_amdgcn_ldexpf(a, b);
 }
 
 // CHECK-LABEL: @test_ldexp_f64
-// CHECK: call double @llvm.amdgcn.ldexp.f64
+// CHECK: call contract double @llvm.amdgcn.ldexp.f64
 void test_ldexp_f64(global double* out, double a, int b)
 {
   *out = __builtin_amdgcn_ldexp(a, b);
 }
 
 // CHECK-LABEL: @test_frexp_mant_f32
-// CHECK: call float @llvm.amdgcn.frexp.mant.f32
+// CHECK: call contract float @llvm.amdgcn.frexp.mant.f32
 void test_frexp_mant_f32(global float* out, float a)
 {
   *out = __builtin_amdgcn_frexp_mantf(a);
 }
 
 // CHECK-LABEL: @test_frexp_mant_f64
-// CHECK: call double @llvm.amdgcn.frexp.mant.f64
+// CHECK: call contract double @llvm.amdgcn.frexp.mant.f64
 void test_frexp_mant_f64(global double* out, double a)
 {
   *out = __builtin_amdgcn_frexp_mant(a);
@@ -208,14 +208,14 @@
 }
 
 // CHECK-LABEL: @test_fract_f32
-// CHECK: call float @llvm.amdgcn.fract.f32
+// CHECK: call contract float @llvm.amdgcn.fract.f32
 void test_fract_f32(global int* out, float a)
 {
   *out = __builtin_amdgcn_fractf(a);
 }
 
 // CHECK-LABEL: @test_fract_f64
-// CHECK: call double @llvm.amdgcn.fract.f64
+// CHECK: call contract double @llvm.amdgcn.fract.f64
 void test_fract_f64(global int* out, double a)
 {
   *out = __builtin_amdgcn_fract(a);
@@ -417,25 +417,25 @@
 }
 
 // CHECK-LABEL: @test_cubeid(
-// CHECK: call float @llvm.amdgcn.cubeid(float %a, float %b, float %c)
+// CHECK: call contract float @llvm.amdgcn.cubeid(float %a, float %b, float %c)
 void test_cubeid(global float* out, float a, float b, float c) {
   *out = __builtin_amdgcn_cubeid(a, b, c);
 }
 
 // CHECK-LABEL: @test_cubesc(
-// CHECK: call float @llvm.amdgcn.cubesc(float %a, float %b, float %c)
+// CHECK: call contract float @llvm.amdgcn.cubesc(float %a, float %b, float %c)
 void test_cubesc(global float* out, float a, float b, float c) {
   *out = __builtin_amdgcn_cubesc(a, b, c);
 }
 
 // CHECK-LABEL: @test_cubetc(
-// CHECK: call float @llvm.amdgcn.cubetc(float %a, float %b, float %c)
+// CHECK: call contract float @llvm.amdgcn.cubetc(float %a, float %b, float %c)
 void test_cubetc(global float* out, float a, float b, float c) {
   *out = __builtin_amdgcn_cubetc(a, b, c);
 }
 
 // CHECK-LABEL: @test_cubema(
-// CHECK: call float @llvm.amdgcn.cubema(float %a, float %b, float %c)
+// CHECK: call contract float @llvm.amdgcn.cubema(float %a, float %b, float %c)
 void test_cubema(global float* out, float a, float b, float c) {
   *out = __builtin_amdgcn_cubema(a, b, c);
 }
@@ -528,7 +528,7 @@
 }
 
 // CHECK-LABEL: @test_fmed3_f32
-// CHECK: call float @llvm.amdgcn.fmed3.f32(
+// CHECK: call contract float @llvm.amdgcn.fmed3.f32(
 void test_fmed3_f32(global float* out, float a, float b, float c)
 {
   *out = __builtin_amdgcn_fmed3f(a, b, c);
@@ -620,7 +620,7 @@
 }
 
 // CHECK-LABEL: @test_cvt_pkrtz(
-// CHECK: tail call <2 x half> @llvm.amdgcn.cvt.pkrtz(float %src0, float %src1)
+// CHECK: tail call contract <2 x half> @llvm.amdgcn.cvt.pkrtz(float %src0, float %src1)
 kernel void test_cvt_pkrtz(global half2* out, float src0, float src1) {
   *out = __builtin_amdgcn_cvt_pkrtz(src0, src1);
 }
Index: clang/test/CodeGenOpenCL/builtins-amdgcn-vi.cl
===================================================================
--- clang/test/CodeGenOpenCL/builtins-amdgcn-vi.cl
+++ clang/test/CodeGenOpenCL/builtins-amdgcn-vi.cl
@@ -9,49 +9,49 @@
 typedef unsigned long ulong;
 
 // CHECK-LABEL: @test_div_fixup_f16
-// CHECK: call half @llvm.amdgcn.div.fixup.f16
+// CHECK: call contract half @llvm.amdgcn.div.fixup.f16
 void test_div_fixup_f16(global half* out, half a, half b, half c)
 {
   *out = __builtin_amdgcn_div_fixuph(a, b, c);
 }
 
 // CHECK-LABEL: @test_rcp_f16
-// CHECK: call half @llvm.amdgcn.rcp.f16
+// CHECK: call contract half @llvm.amdgcn.rcp.f16
 void test_rcp_f16(global half* out, half a)
 {
   *out = __builtin_amdgcn_rcph(a);
 }
 
 // CHECK-LABEL: @test_rsq_f16
-// CHECK: call half @llvm.amdgcn.rsq.f16
+// CHECK: call contract half @llvm.amdgcn.rsq.f16
 void test_rsq_f16(global half* out, half a)
 {
   *out = __builtin_amdgcn_rsqh(a);
 }
 
 // CHECK-LABEL: @test_sin_f16
-// CHECK: call half @llvm.amdgcn.sin.f16
+// CHECK: call contract half @llvm.amdgcn.sin.f16
 void test_sin_f16(global half* out, half a)
 {
   *out = __builtin_amdgcn_sinh(a);
 }
 
 // CHECK-LABEL: @test_cos_f16
-// CHECK: call half @llvm.amdgcn.cos.f16
+// CHECK: call contract half @llvm.amdgcn.cos.f16
 void test_cos_f16(global half* out, half a)
 {
   *out = __builtin_amdgcn_cosh(a);
 }
 
 // CHECK-LABEL: @test_ldexp_f16
-// CHECK: call half @llvm.amdgcn.ldexp.f16
+// CHECK: call contract half @llvm.amdgcn.ldexp.f16
 void test_ldexp_f16(global half* out, half a, int b)
 {
   *out = __builtin_amdgcn_ldexph(a, b);
 }
 
 // CHECK-LABEL: @test_frexp_mant_f16
-// CHECK: call half @llvm.amdgcn.frexp.mant.f16
+// CHECK: call contract half @llvm.amdgcn.frexp.mant.f16
 void test_frexp_mant_f16(global half* out, half a)
 {
   *out = __builtin_amdgcn_frexp_manth(a);
@@ -65,7 +65,7 @@
 }
 
 // CHECK-LABEL: @test_fract_f16
-// CHECK: call half @llvm.amdgcn.fract.f16
+// CHECK: call contract half @llvm.amdgcn.fract.f16
 void test_fract_f16(global half* out, half a)
 {
   *out = __builtin_amdgcn_fracth(a);
@@ -107,19 +107,19 @@
 }
 
 // CHECK-LABEL: @test_ds_fadd
-// CHECK: call float @llvm.amdgcn.ds.fadd(float addrspace(3)* %out, float %src, i32 0, i32 0, i1 false)
+// CHECK: call contract float @llvm.amdgcn.ds.fadd(float addrspace(3)* %out, float %src, i32 0, i32 0, i1 false)
 void test_ds_faddf(local float *out, float src) {
   *out = __builtin_amdgcn_ds_faddf(out, src, 0, 0, false);
 }
 
 // CHECK-LABEL: @test_ds_fmin
-// CHECK: call float @llvm.amdgcn.ds.fmin(float addrspace(3)* %out, float %src, i32 0, i32 0, i1 false)
+// CHECK: call contract float @llvm.amdgcn.ds.fmin(float addrspace(3)* %out, float %src, i32 0, i32 0, i1 false)
 void test_ds_fminf(local float *out, float src) {
   *out = __builtin_amdgcn_ds_fminf(out, src, 0, 0, false);
 }
 
 // CHECK-LABEL: @test_ds_fmax
-// CHECK: call float @llvm.amdgcn.ds.fmax(float addrspace(3)* %out, float %src, i32 0, i32 0, i1 false)
+// CHECK: call contract float @llvm.amdgcn.ds.fmax(float addrspace(3)* %out, float %src, i32 0, i32 0, i1 false)
 void test_ds_fmaxf(local float *out, float src) {
   *out = __builtin_amdgcn_ds_fmaxf(out, src, 0, 0, false);
 }
Index: clang/test/CodeGenOpenCL/builtins-amdgcn-mfma.cl
===================================================================
--- clang/test/CodeGenOpenCL/builtins-amdgcn-mfma.cl
+++ clang/test/CodeGenOpenCL/builtins-amdgcn-mfma.cl
@@ -20,70 +20,70 @@
 
 
 // CHECK-LABEL: @test_mfma_f32_32x32x1f32
-// CHECK: call <32 x float> @llvm.amdgcn.mfma.f32.32x32x1f32(float %a, float %b, <32 x float> %c, i32 0, i32 0, i32 0)
+// CHECK: call contract <32 x float> @llvm.amdgcn.mfma.f32.32x32x1f32(float %a, float %b, <32 x float> %c, i32 0, i32 0, i32 0)
 void test_mfma_f32_32x32x1f32(global v32f* out, float a, float b, v32f c)
 {
   *out = __builtin_amdgcn_mfma_f32_32x32x1f32(a, b, c, 0, 0, 0);
 }
 
 // CHECK-LABEL: @test_mfma_f32_16x16x1f32
-// CHECK: call <16 x float> @llvm.amdgcn.mfma.f32.16x16x1f32(float %a, float %b, <16 x float> %c, i32 0, i32 0, i32 0)
+// CHECK: call contract <16 x float> @llvm.amdgcn.mfma.f32.16x16x1f32(float %a, float %b, <16 x float> %c, i32 0, i32 0, i32 0)
 void test_mfma_f32_16x16x1f32(global v16f* out, float a, float b, v16f c)
 {
   *out = __builtin_amdgcn_mfma_f32_16x16x1f32(a, b, c, 0, 0, 0);
 }
 
 // CHECK-LABEL: @test_mfma_f32_4x4x1f32
-// CHECK: call <4 x float> @llvm.amdgcn.mfma.f32.4x4x1f32(float %a, float %b, <4 x float> %c, i32 0, i32 0, i32 0)
+// CHECK: call contract <4 x float> @llvm.amdgcn.mfma.f32.4x4x1f32(float %a, float %b, <4 x float> %c, i32 0, i32 0, i32 0)
 void test_mfma_f32_4x4x1f32(global v4f* out, float a, float b, v4f c)
 {
   *out = __builtin_amdgcn_mfma_f32_4x4x1f32(a, b, c, 0, 0, 0);
 }
 
 // CHECK-LABEL: @test_mfma_f32_32x32x2f32
-// CHECK: call <16 x float> @llvm.amdgcn.mfma.f32.32x32x2f32(float %a, float %b, <16 x float> %c, i32 0, i32 0, i32 0)
+// CHECK: call contract <16 x float> @llvm.amdgcn.mfma.f32.32x32x2f32(float %a, float %b, <16 x float> %c, i32 0, i32 0, i32 0)
 void test_mfma_f32_32x32x2f32(global v16f* out, float a, float b, v16f c)
 {
   *out = __builtin_amdgcn_mfma_f32_32x32x2f32(a, b, c, 0, 0, 0);
 }
 
 // CHECK-LABEL: @test_mfma_f32_16x16x4f32
-// CHECK: call <4 x float> @llvm.amdgcn.mfma.f32.16x16x4f32(float %a, float %b, <4 x float> %c, i32 0, i32 0, i32 0)
+// CHECK: call contract <4 x float> @llvm.amdgcn.mfma.f32.16x16x4f32(float %a, float %b, <4 x float> %c, i32 0, i32 0, i32 0)
 void test_mfma_f32_16x16x4f32(global v4f* out, float a, float b, v4f c)
 {
   *out = __builtin_amdgcn_mfma_f32_16x16x4f32(a, b, c, 0, 0, 0);
 }
 
 // CHECK-LABEL: @test_mfma_f32_32x32x4f16
-// CHECK: call <32 x float> @llvm.amdgcn.mfma.f32.32x32x4f16(<4 x half> %a, <4 x half> %b, <32 x float> %c, i32 0, i32 0, i32 0)
+// CHECK: call contract <32 x float> @llvm.amdgcn.mfma.f32.32x32x4f16(<4 x half> %a, <4 x half> %b, <32 x float> %c, i32 0, i32 0, i32 0)
 void test_mfma_f32_32x32x4f16(global v32f* out, v4h a, v4h b, v32f c)
 {
   *out = __builtin_amdgcn_mfma_f32_32x32x4f16(a, b, c, 0, 0, 0);
 }
 
 // CHECK-LABEL: @test_mfma_f32_16x16x4f16
-// CHECK: call <16 x float> @llvm.amdgcn.mfma.f32.16x16x4f16(<4 x half> %a, <4 x half> %b, <16 x float> %c, i32 0, i32 0, i32 0)
+// CHECK: call contract <16 x float> @llvm.amdgcn.mfma.f32.16x16x4f16(<4 x half> %a, <4 x half> %b, <16 x float> %c, i32 0, i32 0, i32 0)
 void test_mfma_f32_16x16x4f16(global v16f* out, v4h a, v4h b, v16f c)
 {
   *out = __builtin_amdgcn_mfma_f32_16x16x4f16(a, b, c, 0, 0, 0);
 }
 
 // CHECK-LABEL: @test_mfma_f32_4x4x4f16
-// CHECK: call <4 x float> @llvm.amdgcn.mfma.f32.4x4x4f16(<4 x half> %a, <4 x half> %b, <4 x float> %c, i32 0, i32 0, i32 0)
+// CHECK: call contract <4 x float> @llvm.amdgcn.mfma.f32.4x4x4f16(<4 x half> %a, <4 x half> %b, <4 x float> %c, i32 0, i32 0, i32 0)
 void test_mfma_f32_4x4x4f16(global v4f* out, v4h a, v4h b, v4f c)
 {
   *out = __builtin_amdgcn_mfma_f32_4x4x4f16(a, b, c, 0, 0, 0);
 }
 
 // CHECK-LABEL: @test_mfma_f32_32x32x8f16
-// CHECK: call <16 x float> @llvm.amdgcn.mfma.f32.32x32x8f16(<4 x half> %a, <4 x half> %b, <16 x float> %c, i32 0, i32 0, i32 0)
+// CHECK: call contract <16 x float> @llvm.amdgcn.mfma.f32.32x32x8f16(<4 x half> %a, <4 x half> %b, <16 x float> %c, i32 0, i32 0, i32 0)
 void test_mfma_f32_32x32x8f16(global v16f* out, v4h a, v4h b, v16f c)
 {
   *out = __builtin_amdgcn_mfma_f32_32x32x8f16(a, b, c, 0, 0, 0);
 }
 
 // CHECK-LABEL: @test_mfma_f32_16x16x16f16
-// CHECK: call <4 x float> @llvm.amdgcn.mfma.f32.16x16x16f16(<4 x half> %a, <4 x half> %b, <4 x float> %c, i32 0, i32 0, i32 0)
+// CHECK: call contract <4 x float> @llvm.amdgcn.mfma.f32.16x16x16f16(<4 x half> %a, <4 x half> %b, <4 x float> %c, i32 0, i32 0, i32 0)
 void test_mfma_f32_16x16x16f16(global v4f* out, v4h a, v4h b, v4f c)
 {
   *out = __builtin_amdgcn_mfma_f32_16x16x16f16(a, b, c, 0, 0, 0);
@@ -125,35 +125,35 @@
 }
 
 // CHECK-LABEL: @test_mfma_f32_32x32x2bf16
-// CHECK: call <32 x float> @llvm.amdgcn.mfma.f32.32x32x2bf16(<2 x i16> %a, <2 x i16> %b, <32 x float> %c, i32 0, i32 0, i32 0)
+// CHECK: call contract <32 x float> @llvm.amdgcn.mfma.f32.32x32x2bf16(<2 x i16> %a, <2 x i16> %b, <32 x float> %c, i32 0, i32 0, i32 0)
 void test_mfma_f32_32x32x2bf16(global v32f* out, v2s a, v2s b, v32f c)
 {
   *out = __builtin_amdgcn_mfma_f32_32x32x2bf16(a, b, c, 0, 0, 0);
 }
 
 // CHECK-LABEL: @test_mfma_f32_16x16x2bf16
-// CHECK: call <16 x float> @llvm.amdgcn.mfma.f32.16x16x2bf16(<2 x i16> %a, <2 x i16> %b, <16 x float> %c, i32 0, i32 0, i32 0)
+// CHECK: call contract <16 x float> @llvm.amdgcn.mfma.f32.16x16x2bf16(<2 x i16> %a, <2 x i16> %b, <16 x float> %c, i32 0, i32 0, i32 0)
 void test_mfma_f32_16x16x2bf16(global v16f* out, v2s a, v2s b, v16f c)
 {
   *out = __builtin_amdgcn_mfma_f32_16x16x2bf16(a, b, c, 0, 0, 0);
 }
 
 // CHECK-LABEL: @test_mfma_f32_4x4x2bf16
-// CHECK: call <4 x float> @llvm.amdgcn.mfma.f32.4x4x2bf16(<2 x i16> %a, <2 x i16> %b, <4 x float> %c, i32 0, i32 0, i32 0)
+// CHECK: call contract <4 x float> @llvm.amdgcn.mfma.f32.4x4x2bf16(<2 x i16> %a, <2 x i16> %b, <4 x float> %c, i32 0, i32 0, i32 0)
 void test_mfma_f32_4x4x2bf16(global v4f* out, v2s a, v2s b, v4f c)
 {
   *out = __builtin_amdgcn_mfma_f32_4x4x2bf16(a, b, c, 0, 0, 0);
 }
 
 // CHECK-LABEL: @test_mfma_f32_32x32x4bf16
-// CHECK: call <16 x float> @llvm.amdgcn.mfma.f32.32x32x4bf16(<2 x i16> %a, <2 x i16> %b, <16 x float> %c, i32 0, i32 0, i32 0)
+// CHECK: call contract <16 x float> @llvm.amdgcn.mfma.f32.32x32x4bf16(<2 x i16> %a, <2 x i16> %b, <16 x float> %c, i32 0, i32 0, i32 0)
 void test_mfma_f32_32x32x4bf16(global v16f* out, v2s a, v2s b, v16f c)
 {
   *out = __builtin_amdgcn_mfma_f32_32x32x4bf16(a, b, c, 0, 0, 0);
 }
 
 // CHECK-LABEL: @test_mfma_f32_16x16x8bf16
-// CHECK: call <4 x float> @llvm.amdgcn.mfma.f32.16x16x8bf16(<2 x i16> %a, <2 x i16> %b, <4 x float> %c, i32 0, i32 0, i32 0)
+// CHECK: call contract <4 x float> @llvm.amdgcn.mfma.f32.16x16x8bf16(<2 x i16> %a, <2 x i16> %b, <4 x float> %c, i32 0, i32 0, i32 0)
 void test_mfma_f32_16x16x8bf16(global v4f* out, v2s a, v2s b, v4f c)
 {
   *out = __builtin_amdgcn_mfma_f32_16x16x8bf16(a, b, c, 0, 0, 0);
Index: clang/test/CodeGenOpenCL/builtins-amdgcn-interp.cl
===================================================================
--- clang/test/CodeGenOpenCL/builtins-amdgcn-interp.cl
+++ clang/test/CodeGenOpenCL/builtins-amdgcn-interp.cl
@@ -19,7 +19,7 @@
 
 // CHECK-LABEL: test_interp_f32
 // CHECK: call float @llvm.amdgcn.interp.p1
-// CHECK: call float @llvm.amdgcn.interp.p2
+// CHECK: call contract float @llvm.amdgcn.interp.p2
 void test_interp_f32(global float* out, float i, float j, int m0)
 {
   float p1 = __builtin_amdgcn_interp_p1(i, 1, 4, m0);
@@ -27,7 +27,7 @@
 }
 
 // CHECK-LABEL: test_interp_mov
-// CHECK: call float @llvm.amdgcn.interp.mov
+// CHECK: call contract float @llvm.amdgcn.interp.mov
 void test_interp_mov(global float* out, float i, float j, int m0)
 {
   *out = __builtin_amdgcn_interp_mov(2, 3, 4, m0);
Index: clang/test/CodeGenOpenCL/builtins-amdgcn-gfx9.cl
===================================================================
--- clang/test/CodeGenOpenCL/builtins-amdgcn-gfx9.cl
+++ clang/test/CodeGenOpenCL/builtins-amdgcn-gfx9.cl
@@ -5,7 +5,7 @@
 #pragma OPENCL EXTENSION cl_khr_fp16 : enable
 
 // CHECK-LABEL: @test_fmed3_f16
-// CHECK: call half @llvm.amdgcn.fmed3.f16(half %a, half %b, half %c)
+// CHECK: call contract half @llvm.amdgcn.fmed3.f16(half %a, half %b, half %c)
 void test_fmed3_f16(global half* out, half a, half b, half c)
 {
   *out = __builtin_amdgcn_fmed3h(a, b, c);
Index: clang/test/CodeGenOpenCL/builtins-amdgcn-dl-insts.cl
===================================================================
--- clang/test/CodeGenOpenCL/builtins-amdgcn-dl-insts.cl
+++ clang/test/CodeGenOpenCL/builtins-amdgcn-dl-insts.cl
@@ -10,8 +10,8 @@
 typedef unsigned short __attribute__((ext_vector_type(2))) ushort2;
 
 // CHECK-LABEL: @builtins_amdgcn_dl_insts
-// CHECK: call float @llvm.amdgcn.fdot2(<2 x half> %v2hA, <2 x half> %v2hB, float %fC, i1 false)
-// CHECK: call float @llvm.amdgcn.fdot2(<2 x half> %v2hA, <2 x half> %v2hB, float %fC, i1 true)
+// CHECK: call contract float @llvm.amdgcn.fdot2(<2 x half> %v2hA, <2 x half> %v2hB, float %fC, i1 false)
+// CHECK: call contract float @llvm.amdgcn.fdot2(<2 x half> %v2hA, <2 x half> %v2hB, float %fC, i1 true)
 
 // CHECK: call i32 @llvm.amdgcn.sdot2(<2 x i16> %v2ssA, <2 x i16> %v2ssB, i32 %siC, i1 false)
 // CHECK: call i32 @llvm.amdgcn.sdot2(<2 x i16> %v2ssA, <2 x i16> %v2ssB, i32 %siC, i1 true)
Index: clang/test/CodeGen/fpconstrained.cpp
===================================================================
--- clang/test/CodeGen/fpconstrained.cpp
+++ clang/test/CodeGen/fpconstrained.cpp
@@ -1,7 +1,7 @@
 // RUN: %clang_cc1 -x c++ -ftrapping-math -fexceptions -fcxx-exceptions -frounding-math -ffp-exception-behavior=strict -emit-llvm -o - %s | FileCheck %s -check-prefix=FPMODELSTRICT
 // RUN: %clang_cc1 -x c++ -ffp-contract=fast -fexceptions -fcxx-exceptions -emit-llvm -o - %s | FileCheck %s -check-prefix=PRECISE
 // RUN: %clang_cc1 -x c++ -ffast-math -fexceptions -fcxx-exceptions -ffp-contract=fast -emit-llvm -o - %s | FileCheck %s -check-prefix=FAST
-// RUN: %clang_cc1 -x c++ -ffast-math -fexceptions -fcxx-exceptions -emit-llvm -o - %s | FileCheck %s -check-prefix=FAST
+// RUN: %clang_cc1 -x c++ -ffast-math -fexceptions -fcxx-exceptions -emit-llvm -o - %s | FileCheck %s -check-prefix=FASTNOCONTRACT
 // RUN: %clang_cc1 -x c++ -ffast-math -fexceptions -fcxx-exceptions -ffp-contract=fast -ffp-exception-behavior=ignore -emit-llvm -o - %s | FileCheck %s -check-prefix=FAST
 // RUN: %clang_cc1 -x c++ -ffast-math -fexceptions -fcxx-exceptions -ffp-contract=fast -ffp-exception-behavior=strict -emit-llvm -o - %s | FileCheck %s -check-prefix=EXCEPT
 // RUN: %clang_cc1 -x c++ -ffast-math -fexceptions -fcxx-exceptions -ffp-contract=fast -ffp-exception-behavior=maytrap -emit-llvm -o - %s | FileCheck %s -check-prefix=MAYTRAP
@@ -27,6 +27,7 @@
   // STRICTNOEXCEPT: llvm.experimental.constrained.fadd.f32(float %{{.*}}, float %{{.*}}, metadata !"round.dynamic", metadata !"fpexcept.ignore")
   // PRECISE: fadd contract float %{{.*}}, %{{.*}}
   // FAST: fadd fast
+  // FASTNOCONTRACT: fadd reassoc nnan ninf nsz arcp afn float
   f0 = f1 + f2;
 
   // CHECK: ret void
Index: clang/test/CodeGen/fpconstrained.c
===================================================================
--- clang/test/CodeGen/fpconstrained.c
+++ clang/test/CodeGen/fpconstrained.c
@@ -1,7 +1,7 @@
 // RUN: %clang_cc1 -ftrapping-math -frounding-math -ffp-exception-behavior=strict -emit-llvm -o - %s | FileCheck %s -check-prefix=FPMODELSTRICT
 // RUN: %clang_cc1 -ffp-contract=fast -emit-llvm -o - %s | FileCheck %s -check-prefix=PRECISE
 // RUN: %clang_cc1 -ffast-math -ffp-contract=fast -emit-llvm -o - %s | FileCheck %s -check-prefix=FAST
-// RUN: %clang_cc1 -ffast-math -emit-llvm -o - %s | FileCheck %s -check-prefix=FAST
+// RUN: %clang_cc1 -ffast-math -emit-llvm -o - %s | FileCheck %s -check-prefix=FASTNOCONTRACT
 // RUN: %clang_cc1 -ffast-math -ffp-contract=fast -ffp-exception-behavior=ignore -emit-llvm -o - %s | FileCheck %s -check-prefix=FAST
 // RUN: %clang_cc1 -ffast-math -ffp-contract=fast -ffp-exception-behavior=strict -emit-llvm -o - %s | FileCheck %s -check-prefix=EXCEPT
 // RUN: %clang_cc1 -ffast-math -ffp-contract=fast -ffp-exception-behavior=maytrap -emit-llvm -o - %s | FileCheck %s -check-prefix=MAYTRAP
@@ -17,6 +17,7 @@
   // STRICTNOEXCEPT: llvm.experimental.constrained.fadd.f32(float %{{.*}}, float %{{.*}}, metadata !"round.dynamic", metadata !"fpexcept.ignore")
   // PRECISE: fadd contract float %{{.*}}, %{{.*}}
   // FAST: fadd fast
+  // FASTNOCONTRACT: fadd reassoc nnan ninf nsz arcp afn float
   f0 = f1 + f2;
 
   // CHECK: ret
Index: clang/test/CodeGen/fp-floatcontrol-stack.cpp
===================================================================
--- /dev/null
+++ clang/test/CodeGen/fp-floatcontrol-stack.cpp
@@ -0,0 +1,257 @@
+// RUN: %clang -c -DDEFAULT=1 -Xclang -emit-llvm -o - %s | FileCheck --check-prefix=CHECK-DDEFAULT %s
+// RUN: %clang -c -DEBSTRICT=1 -ffp-exception-behavior=strict -Xclang -emit-llvm -o - %s | FileCheck --check-prefix=CHECK-DEBSTRICT %s
+// RUN: %clang -c -DFAST=1 -ffast-math -Xclang -emit-llvm -o - %s | FileCheck --check-prefix=CHECK-FAST %s
+// RUN: %clang -c -DNOHONOR=1 -fno-honor-nans -fno-honor-infinities -Xclang -emit-llvm -o - %s | FileCheck --check-prefix=CHECK-NOHONOR %s
+// XFAIL:*
+
+#define FUN(n) (float z) { return n * z + n; }
+
+float fun_default FUN(1)
+//CHECK-LABEL: define {{.*}} @_Z11fun_defaultf{{.*}}
+#if DEFAULT
+//CHECK-DDEFAULT: call contract float @llvm.fmuladd{{.*}}
+#endif
+#if EBSTRICT
+// Note that backend wants constrained intrinsics used
+// throughout the function if they are needed anywhere in the function.
+// In that case, operations are built with constrained intrinsics operator
+// but using default settings for exception behavior and rounding mode.
+//CHECK-DEBSTRICT: llvm.experimental.constrained.fmul{{.*}}tonearest{{.*}}strict
+#endif
+#if FAST
+//CHECK-FAST: fmul fast float
+//CHECK-FAST: fadd fast float
+#endif
+// class ResetScope;
+
+#pragma float_control(except, on, push)
+float exc_on FUN(2)
+//CHECK-LABEL: define {{.*}} @_Z6exc_onf{{.*}}
+#if DEFAULT
+//CHECK-DDEFAULT: llvm.experimental.constrained.fmul{{.*}}
+#endif
+#if EBSTRICT
+//CHECK-DEBSTRICT: llvm.experimental.constrained.fmuladd{{.*}}tonearest{{.*}}strict
+#endif
+#if NOHONOR
+//CHECK-NOHONOR: nnan ninf contract float {{.*}}llvm.experimental.constrained.fmuladd{{.*}}tonearest{{.*}}strict
+#endif
+#if FAST
+//CHECK-FAST: fast float {{.*}}llvm.experimental.constrained.fmul{{.*}}tonearest{{.*}}strict
+//CHECK-FAST: fast float {{.*}}llvm.experimental.constrained.fadd{{.*}}tonearest{{.*}}strict
+#endif
+
+// class ResetScope;
+#pragma float_control(pop)
+float exc_pop FUN(5)
+//CHECK-LABEL: define {{.*}} @_Z7exc_popf{{.*}}
+#if DEFAULT
+//CHECK-DDEFAULT: call contract float @llvm.fmuladd{{.*}}
+#endif
+#if EBSTRICT
+//CHECK-DEBSTRICT: llvm.experimental.constrained.fmuladd{{.*}}tonearest{{.*}}strict
+#endif
+#if NOHONOR
+//CHECK-NOHONOR: call nnan ninf contract float @llvm.fmuladd{{.*}}
+#endif
+#if FAST
+//CHECK-FAST: fmul fast float
+//CHECK-FAST: fadd fast float
+#endif
+
+// class ResetScope;
+#pragma float_control(except, off)
+float exc_off FUN(5)
+//CHECK-LABEL: define {{.*}} @_Z7exc_offf{{.*}}
+#if DEFAULT
+//CHECK-DDEFAULT: call contract float @llvm.fmuladd{{.*}}
+#endif
+#if EBSTRICT
+//CHECK-DEBSTRICT: call contract float @llvm.fmuladd{{.*}}
+#endif
+#if NOHONOR
+//CHECK-NOHONOR: call nnan ninf contract float @llvm.fmuladd{{.*}}
+#endif
+#if FAST
+//CHECK-FAST: fmul fast float
+//CHECK-FAST: fadd fast float
+#endif
+
+// class ResetScope;
+#pragma float_control(precise, on, push)
+float precise_on FUN(3)
+//CHECK-LABEL: define {{.*}} @_Z10precise_onf{{.*}}
+#if DEFAULT
+//CHECK-DDEFAULT: contract float {{.*}}llvm.fmuladd{{.*}}
+#endif
+#if EBSTRICT
+//CHECK-DEBSTRICT: contract float {{.*}}llvm.fmuladd{{.*}}
+#endif
+#if NOHONOR
+// If precise is pushed then all fast-math should be off!
+//CHECK-NOHONOR: call contract float {{.*}}llvm.fmuladd{{.*}}
+#endif
+#if FAST
+//CHECK-FAST: contract float {{.*}}llvm.fmuladd{{.*}}
+#endif
+
+// class ResetScope;
+#pragma float_control(pop)
+float precise_pop FUN(3)
+//CHECK-LABEL: define {{.*}} @_Z11precise_popf{{.*}}
+#if DEFAULT
+//CHECK-DDEFAULT: contract float {{.*}}llvm.fmuladd{{.*}}
+#endif
+#if EBSTRICT
+//CHECK-DEBSTRICT: contract float {{.*}}llvm.fmuladd{{.*}}
+#endif
+#if NOHONOR
+//CHECK-NOHONOR: call nnan ninf contract float @llvm.fmuladd{{.*}}
+#endif
+#if FAST
+//CHECK-FAST: fmul fast float
+//CHECK-FAST: fadd fast float
+#endif
+// class ResetScope;
+#pragma float_control(precise, off)
+float precise_off FUN(4)
+//CHECK-LABEL: define {{.*}} @_Z11precise_offf{{.*}}
+#if DEFAULT
+// Note: precise_off enables fp_contract=fast and the instructions
+// generated do not include the contract flag, although it was enabled
+// in IRBuilder.
+//CHECK-DDEFAULT: fmul fast float
+//CHECK-DDEFAULT: fadd fast float
+#endif
+#if EBSTRICT
+//CHECK-DEBSTRICT: fmul fast float
+//CHECK-DEBSTRICT: fadd fast float
+#endif
+#if NOHONOR
+// fast math should be enabled, and contract should be fast
+//CHECK-NOHONOR: fmul fast float
+//CHECK-NOHONOR: fadd fast float
+#endif
+#if FAST
+//CHECK-FAST: fmul fast float
+//CHECK-FAST: fadd fast float
+#endif
+
+// class ResetScope;
+#pragma float_control(precise, on)
+float precise_on2 FUN(3)
+//CHECK-LABEL: define {{.*}} @_Z11precise_on2f{{.*}}
+#if DEFAULT
+//CHECK-DDEFAULT: llvm.fmuladd{{.*}}
+#endif
+#if EBSTRICT
+//CHECK-DEBSTRICT: contract float {{.*}}llvm.fmuladd{{.*}}
+#endif
+#if NOHONOR
+// fast math should be off, and contract should be on
+//CHECK-NOHONOR: contract float {{.*}}llvm.fmuladd{{.*}}
+#endif
+#if FAST
+//CHECK-FAST: contract float {{.*}}llvm.fmuladd{{.*}}
+#endif
+
+// class ResetScope;
+#pragma float_control(push)
+float precise_push FUN(3)
+//CHECK-LABEL: define {{.*}} @_Z12precise_pushf{{.*}}
+#if DEFAULT
+//CHECK-DDEFAULT: llvm.fmuladd{{.*}}
+#endif
+#if EBSTRICT
+//CHECK-DEBSTRICT: contract float {{.*}}llvm.fmuladd{{.*}}
+#endif
+#if NOHONOR
+//CHECK-NOHONOR: contract float {{.*}}llvm.fmuladd{{.*}}
+#endif
+#if FAST
+//CHECK-FAST: contract float {{.*}}llvm.fmuladd{{.*}}
+#endif
+
+// class ResetScope;
+#pragma float_control(precise, off)
+float precise_off2 FUN(4)
+//CHECK-LABEL: define {{.*}} @_Z12precise_off2f{{.*}}
+#if DEFAULT
+//CHECK-DDEFAULT: fmul fast float
+//CHECK-DDEFAULT: fadd fast float
+#endif
+#if EBSTRICT
+//CHECK-DEBSTRICT: fmul fast float
+//CHECK-DEBSTRICT: fadd fast float
+#endif
+#if NOHONOR
+// fast math settings since precise is off
+//CHECK-NOHONOR: fmul fast float
+//CHECK-NOHONOR: fadd fast float
+#endif
+#if FAST
+//CHECK-FAST: fmul fast float
+//CHECK-FAST: fadd fast float
+#endif
+
+// class ResetScope;
+#pragma float_control(pop)
+float precise_pop2 FUN(3)
+//CHECK-LABEL: define {{.*}} @_Z12precise_pop2f{{.*}}
+#if DEFAULT
+//CHECK-DDEFAULT: llvm.fmuladd{{.*}}
+#endif
+#if EBSTRICT
+//CHECK-DEBSTRICT: contract float {{.*}}llvm.fmuladd{{.*}}
+#endif
+#if NOHONOR
+//CHECK-NOHONOR: contract float {{.*}}llvm.fmuladd{{.*}}
+#endif
+#if FAST
+//CHECK-FAST: contract float {{.*}}llvm.fmuladd{{.*}}
+#endif
+
+// class ResetScope;
+// --------- end of push pop test
+#pragma float_control(except, on)
+float y();
+class ON {
+// Settings for top level class initializer revert to command line
+// source pragma's do not pertain.
+float z = 2 + y() * 7;
+//CHECK-LABEL: define {{.*}} void @_ZN2ONC2Ev{{.*}}
+#if DEFAULT
+//CHECK-DDEFAULT: call contract float {{.*}}llvm.fmuladd
+#endif
+#if EBSTRICT
+//Currently, same as default [command line options not considered]
+//CHECK-DEBSTRICT: call contract float {{.*}}llvm.fmuladd
+#endif
+#if NOHONOR
+//CHECK-NOHONOR: call nnan ninf contract float @llvm.fmuladd{{.*}}
+#endif
+#if FAST
+//CHECK-FAST: fmul fast float
+//CHECK-FAST: fadd fast float
+#endif
+};
+ON on;
+#pragma float_control( except, off)
+class OFF {
+float w = 2 + y() * 7;
+//CHECK-LABEL: define {{.*}} void @_ZN3OFFC2Ev{{.*}}
+#if DEFAULT
+//CHECK-DDEFAULT: call contract float {{.*}}llvm.fmuladd
+#endif
+#if EBSTRICT
+//CHECK-DEBSTRICT: call contract float {{.*}}llvm.fmuladd
+#endif
+#if NOHONOR
+//CHECK-NOHONOR: call nnan ninf contract float @llvm.fmuladd{{.*}}
+#endif
+#if FAST
+//CHECK-FAST: fmul fast float
+//CHECK-FAST: fadd fast float
+#endif
+};
+OFF off;
Index: clang/test/CodeGen/fp-floatcontrol-pragma.cpp
===================================================================
--- /dev/null
+++ clang/test/CodeGen/fp-floatcontrol-pragma.cpp
@@ -0,0 +1,47 @@
+// RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck %s
+
+float fff(float x, float y) {
+// CHECK-LABEL: define float @_Z3fffff{{.*}}
+// CHECK: entry
+#pragma float_control(except, on)
+  float z;
+  z = z*z;
+//CHECK: llvm.experimental.constrained.fmul{{.*}}
+  {
+    z = x*y;
+//CHECK: llvm.experimental.constrained.fmul{{.*}}
+  }
+  {
+// This pragma has no effect since if there are any fp intrin in the
+// function then all the operations need to be fp intrin
+#pragma float_control(except, off)
+     z = z + x*y;
+//CHECK: llvm.experimental.constrained.fmul{{.*}}
+  }
+  z = z*z;
+//CHECK: llvm.experimental.constrained.fmul{{.*}}
+  return z;
+}
+float check_precise(float x, float y) {
+// CHECK-LABEL: define float @_Z13check_preciseff{{.*}}
+  float z;
+  {
+#pragma float_control(precise, on)
+    z = x*y + z;
+//CHECK: llvm.fmuladd{{.*}}
+  }
+  {
+#pragma float_control(precise, off)
+    z = x*y + z;
+//CHECK: fmul fast float
+//CHECK: fadd fast float
+  }
+  return z;
+}
+float fma_test1(float a, float b, float c) {
+// CHECK-LABEL define float @_Z9fma_test1fff{{.*}}
+#pragma float_control(precise, on)
+  float x = a * b + c;
+//CHECK: fmuladd
+  return x;
+}
Index: clang/test/CodeGen/fp-floatcontrol-class.cpp
===================================================================
--- /dev/null
+++ clang/test/CodeGen/fp-floatcontrol-class.cpp
@@ -0,0 +1,20 @@
+// RUN: %clang -c -Xclang -emit-llvm -o - %s | FileCheck %s
+// XFAIL:*
+// Verify that float_control does not pertain to initializer expressions
+
+float y();
+float z();
+#pragma float_control(except, on)
+class ON {
+float w = 2 + y() * z();
+// CHECK-LABEL: define {{.*}} void @_ZN2ONC2Ev{{.*}}
+//CHECK: call contract float {{.*}}llvm.fmuladd
+};
+ON on;
+#pragma float_control( except, off)
+class OFF {
+float w = 2 + y() * z();
+// CHECK-LABEL: define {{.*}} void @_ZN3OFFC2Ev{{.*}}
+//CHECK: call contract float {{.*}}llvm.fmuladd
+};
+OFF off;
Index: clang/test/CodeGen/fp-contract-pragma.cpp
===================================================================
--- clang/test/CodeGen/fp-contract-pragma.cpp
+++ clang/test/CodeGen/fp-contract-pragma.cpp
@@ -3,7 +3,7 @@
 // Is FP_CONTRACT honored in a simple case?
 float fp_contract_1(float a, float b, float c) {
 // CHECK: _Z13fp_contract_1fff
-// CHECK: tail call float @llvm.fmuladd
+// CHECK: tail call contract float @llvm.fmuladd
   #pragma STDC FP_CONTRACT ON
   return a * b + c;
 }
@@ -31,7 +31,7 @@
 
 float fp_contract_3(float a, float b, float c) {
 // CHECK: _Z13fp_contract_3fff
-// CHECK: tail call float @llvm.fmuladd
+// CHECK: tail call contract float @llvm.fmuladd
   return template_muladd<float>(a, b, c);
 }
 
@@ -44,13 +44,13 @@
 
 template class fp_contract_4<int>;
 // CHECK: _ZN13fp_contract_4IiE6methodEfff
-// CHECK: tail call float @llvm.fmuladd
+// CHECK: tail call contract float @llvm.fmuladd
 
 // Check file-scoped FP_CONTRACT
 #pragma STDC FP_CONTRACT ON
 float fp_contract_5(float a, float b, float c) {
 // CHECK: _Z13fp_contract_5fff
-// CHECK: tail call float @llvm.fmuladd
+// CHECK: tail call contract float @llvm.fmuladd
   return a * b + c;
 }
 
@@ -68,24 +68,24 @@
 
 float fp_contract_7(float a, float b, float c) {
 // CHECK: _Z13fp_contract_7fff
-// CHECK:  %[[M:.+]] = fmul float %b, 2.000000e+00
-// CHECK-NEXT: fsub float %[[M]], %c
+// CHECK:  %[[M:.+]] = fmul contract float %b, 2.000000e+00
+// CHECK-NEXT: fsub contract float %[[M]], %c
   #pragma STDC FP_CONTRACT ON
   return (a = 2 * b) - c;
 }
 
 float fp_contract_8(float a, float b, float c) {
 // CHECK: _Z13fp_contract_8fff
-// CHECK: fneg float %c
-// CHECK: tail call float @llvm.fmuladd
+// CHECK: fneg contract float %c
+// CHECK: tail call contract float @llvm.fmuladd
   #pragma STDC FP_CONTRACT ON
   return a * b - c;
 }
 
 float fp_contract_9(float a, float b, float c) {
 // CHECK: _Z13fp_contract_9fff
-// CHECK: fneg float %a
-// CHECK: tail call float @llvm.fmuladd
+// CHECK: fneg contract float %a
+// CHECK: tail call contract float @llvm.fmuladd
   #pragma STDC FP_CONTRACT ON
   return c - a * b;
 }
Index: clang/test/CodeGen/fp-contract-on-pragma.cpp
===================================================================
--- clang/test/CodeGen/fp-contract-on-pragma.cpp
+++ clang/test/CodeGen/fp-contract-on-pragma.cpp
@@ -3,7 +3,7 @@
 // Is FP_CONTRACT honored in a simple case?
 float fp_contract_1(float a, float b, float c) {
 // CHECK: _Z13fp_contract_1fff
-// CHECK: tail call float @llvm.fmuladd
+// CHECK: tail call contract float @llvm.fmuladd
 #pragma clang fp contract(on)
   return a * b + c;
 }
@@ -31,7 +31,7 @@
 
 float fp_contract_3(float a, float b, float c) {
   // CHECK: _Z13fp_contract_3fff
-  // CHECK: tail call float @llvm.fmuladd
+  // CHECK: tail call contract float @llvm.fmuladd
   return template_muladd<float>(a, b, c);
 }
 
@@ -45,13 +45,13 @@
 
 template class fp_contract_4<int>;
 // CHECK: _ZN13fp_contract_4IiE6methodEfff
-// CHECK: tail call float @llvm.fmuladd
+// CHECK: tail call contract float @llvm.fmuladd
 
 // Check file-scoped FP_CONTRACT
 #pragma clang fp contract(on)
 float fp_contract_5(float a, float b, float c) {
   // CHECK: _Z13fp_contract_5fff
-  // CHECK: tail call float @llvm.fmuladd
+  // CHECK: tail call contract float @llvm.fmuladd
   return a * b + c;
 }
 
@@ -69,8 +69,8 @@
 
 float fp_contract_7(float a, float b, float c) {
 // CHECK: _Z13fp_contract_7fff
-// CHECK:  %[[M:.+]] = fmul float %b, 2.000000e+00
-// CHECK-NEXT: fsub float %[[M]], %c
+// CHECK:  %[[M:.+]] = fmul contract float %b, 2.000000e+00
+// CHECK-NEXT: fsub contract float %[[M]], %c
 #pragma clang fp contract(on)
   return (a = 2 * b) - c;
 }
Index: clang/test/CodeGen/fast-math.c
===================================================================
--- clang/test/CodeGen/fast-math.c
+++ clang/test/CodeGen/fast-math.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -ffast-math -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -ffast-math -ffp-contract=fast -emit-llvm -o - %s | FileCheck %s
 float f0, f1, f2;
 
 void foo(void) {
Index: clang/test/CodeGen/constrained-math-builtins.c
===================================================================
--- clang/test/CodeGen/constrained-math-builtins.c
+++ clang/test/CodeGen/constrained-math-builtins.c
@@ -154,9 +154,9 @@
   (double)f * f - f;
   (long double)-f * f + f;
 
-// CHECK: call float @llvm.experimental.constrained.fmuladd.f32
+// CHECK: call contract float @llvm.experimental.constrained.fmuladd.f32
 // CHECK: fneg
-// CHECK: call double @llvm.experimental.constrained.fmuladd.f64
+// CHECK: call contract double @llvm.experimental.constrained.fmuladd.f64
 // CHECK: fneg
-// CHECK: call x86_fp80 @llvm.experimental.constrained.fmuladd.f80
+// CHECK: call contract x86_fp80 @llvm.experimental.constrained.fmuladd.f80
 };
Index: clang/lib/Serialization/ASTWriter.cpp
===================================================================
--- clang/lib/Serialization/ASTWriter.cpp
+++ clang/lib/Serialization/ASTWriter.cpp
@@ -4114,6 +4114,26 @@
   Stream.EmitRecord(PACK_PRAGMA_OPTIONS, Record);
 }
 
+/// Write the state of 'pragma float_control' at the end of the module.
+void ASTWriter::WriteFloatControlPragmaOptions(Sema &SemaRef) {
+  // Don't serialize pragma pack state for modules, since it should only take
+  // effect on a per-submodule basis.
+  if (WritingModule)
+    return;
+
+  RecordData Record;
+  Record.push_back(SemaRef.FpPragmaStack.CurrentValue);
+  AddSourceLocation(SemaRef.FpPragmaStack.CurrentPragmaLocation, Record);
+  Record.push_back(SemaRef.FpPragmaStack.Stack.size());
+  for (const auto &StackEntry : SemaRef.FpPragmaStack.Stack) {
+    Record.push_back(StackEntry.Value);
+    AddSourceLocation(StackEntry.PragmaLocation, Record);
+    AddSourceLocation(StackEntry.PragmaPushLocation, Record);
+    AddString(StackEntry.StackSlotLabel, Record);
+  }
+  Stream.EmitRecord(FLOAT_CONTROL_PRAGMA_OPTIONS, Record);
+}
+
 void ASTWriter::WriteModuleFileExtension(Sema &SemaRef,
                                          ModuleFileExtensionWriter &Writer) {
   // Enter the extension block.
@@ -4832,6 +4852,7 @@
     WriteMSPointersToMembersPragmaOptions(SemaRef);
   }
   WritePackPragmaOptions(SemaRef);
+  WriteFloatControlPragmaOptions(SemaRef);
 
   // Some simple statistics
   RecordData::value_type Record[] = {
Index: clang/lib/Serialization/ASTReader.cpp
===================================================================
--- clang/lib/Serialization/ASTReader.cpp
+++ clang/lib/Serialization/ASTReader.cpp
@@ -3774,6 +3774,29 @@
       }
       break;
     }
+
+    case FLOAT_CONTROL_PRAGMA_OPTIONS: {
+      if (Record.size() < 3) {
+        Error("invalid pragma pack record");
+        return Failure;
+      }
+      FpPragmaCurrentValue = Record[0];
+      FpPragmaCurrentLocation = ReadSourceLocation(F, Record[1]);
+      unsigned NumStackEntries = Record[2];
+      unsigned Idx = 3;
+      // Reset the stack when importing a new module.
+      FpPragmaStack.clear();
+      for (unsigned I = 0; I < NumStackEntries; ++I) {
+        FpPragmaStackEntry Entry;
+        Entry.Value = Record[Idx++];
+        Entry.Location = ReadSourceLocation(F, Record[Idx++]);
+        Entry.PushLocation = ReadSourceLocation(F, Record[Idx++]);
+        FpPragmaStrings.push_back(ReadString(Record, Idx));
+        Entry.SlotLabel = FpPragmaStrings.back();
+        FpPragmaStack.push_back(Entry);
+      }
+      break;
+    }
     }
   }
 }
@@ -7825,6 +7848,34 @@
       SemaObj->PackStack.CurrentPragmaLocation = PragmaPackCurrentLocation;
     }
   }
+  if (FpPragmaCurrentValue) {
+    // The bottom of the stack might have a default value. It must be adjusted
+    // to the current value to ensure that fp-pragma state is preserved after
+    // popping entries that were included/imported from a PCH/module.
+    bool DropFirst = false;
+    if (!FpPragmaStack.empty() &&
+        FpPragmaStack.front().Location.isInvalid()) {
+      assert(FpPragmaStack.front().Value == SemaObj->FpPragmaStack.DefaultValue
+             && "Expected a default pragma float_control value");
+      SemaObj->FpPragmaStack.Stack.emplace_back(
+          FpPragmaStack.front().SlotLabel, SemaObj->FpPragmaStack.CurrentValue,
+          SemaObj->FpPragmaStack.CurrentPragmaLocation,
+          FpPragmaStack.front().PushLocation);
+      DropFirst = true;
+    }
+    for (const auto &Entry :
+         llvm::makeArrayRef(FpPragmaStack).drop_front(DropFirst ? 1 : 0))
+      SemaObj->FpPragmaStack.Stack.emplace_back(Entry.SlotLabel, Entry.Value,
+                                            Entry.Location, Entry.PushLocation);
+    if (FpPragmaCurrentLocation.isInvalid()) {
+      assert(*FpPragmaCurrentValue == SemaObj->FpPragmaStack.DefaultValue &&
+             "Expected a default pragma float_control value");
+      // Keep the current values.
+    } else {
+      SemaObj->FpPragmaStack.CurrentValue = *FpPragmaCurrentValue;
+      SemaObj->FpPragmaStack.CurrentPragmaLocation = FpPragmaCurrentLocation;
+    }
+  }
 }
 
 IdentifierInfo *ASTReader::get(StringRef Name) {
Index: clang/lib/Sema/SemaStmt.cpp
===================================================================
--- clang/lib/Sema/SemaStmt.cpp
+++ clang/lib/Sema/SemaStmt.cpp
@@ -374,6 +374,13 @@
 }
 
 void Sema::ActOnStartOfCompoundStmt(bool IsStmtExpr) {
+  if (getFPOptions().isFPConstrained()) {
+    // Mark the current function as usng floating point constrained intrinsics
+    if (FunctionDecl *F = dyn_cast<FunctionDecl>(CurContext)) {
+      F->setUsesFPIntrin(true);
+    }
+  }
+
   PushCompoundScope(IsStmtExpr);
 }
 
Index: clang/lib/Sema/SemaExpr.cpp
===================================================================
--- clang/lib/Sema/SemaExpr.cpp
+++ clang/lib/Sema/SemaExpr.cpp
@@ -13130,14 +13130,6 @@
   if (ResultTy.isNull() || LHS.isInvalid() || RHS.isInvalid())
     return ExprError();
 
-  if (ResultTy->isRealFloatingType() &&
-      (getLangOpts().getFPRoundingMode() != LangOptions::FPR_ToNearest ||
-       getLangOpts().getFPExceptionMode() != LangOptions::FPE_Ignore))
-    // Mark the current function as usng floating point constrained intrinsics
-    if (FunctionDecl *F = dyn_cast<FunctionDecl>(CurContext)) {
-      F->setUsesFPIntrin(true);
-    }
-
   // Some of the binary operations require promoting operands of half vector to
   // float vectors and truncating the result back to half vector. For now, we do
   // this only when HalfArgsAndReturn is set (that is, when the target is arm or
Index: clang/lib/Sema/SemaAttr.cpp
===================================================================
--- clang/lib/Sema/SemaAttr.cpp
+++ clang/lib/Sema/SemaAttr.cpp
@@ -407,6 +407,56 @@
   Consumer.HandleTopLevelDecl(DeclGroupRef(PDMD));
 }
 
+void Sema::ActOnPragmaFloatControl(SourceLocation Loc,
+                              PragmaMsStackAction Action,
+                              PragmaFloatControlKind Value) {
+  auto NewValue = FpPragmaStack.CurrentValue;
+  if ((Action == PSK_Push_Set || Action == PSK_Push || Action == PSK_Pop) &&
+      !CurContext->isTranslationUnit()) {
+    // Push and pop can only occur at file scope.
+    Diag(Loc, diag::err_pragma_fc_pp_scope);
+    return;
+  }
+  switch(Value) {
+  default:
+    llvm_unreachable("invalid pragma float_control kind");
+  case PFC_Precise:
+  case PFC_NoPrecise:
+  case PFC_Except:
+  case PFC_NoExcept:
+    switch(Value) {
+    default:
+      llvm_unreachable("invalid pragma float_control kind");
+    case PFC_Precise:
+      FPFeatures.setFPPreciseEnabled(true);
+      break;
+    case PFC_NoPrecise:
+      FPFeatures.setFPPreciseEnabled(false);
+      break;
+    case PFC_Except:
+      FPFeatures.setExceptionMode(LangOptions::FPE_Strict);
+      break;
+    case PFC_NoExcept:
+      FPFeatures.setExceptionMode(LangOptions::FPE_Ignore);
+      break;
+    }
+    NewValue = FPFeatures.getInt();
+    FpPragmaStack.Act(Loc, Action, StringRef(), NewValue);
+    break;
+  case PFC_Push:
+  case PFC_Pop:
+    if (Value == PFC_Pop && FpPragmaStack.Stack.empty())
+      Diag(Loc, diag::warn_pragma_pop_failed) <<
+           "float_control" << "stack empty";
+    FpPragmaStack.Act(Loc, Action, StringRef(), NewValue);
+    if (Value == PFC_Pop) {
+      NewValue = FpPragmaStack.CurrentValue;
+      FPFeatures.Restore(NewValue);
+    }
+    break;
+  }
+}
+
 void Sema::ActOnPragmaMSPointersToMembers(
     LangOptions::PragmaMSPointersToMembersKind RepresentationMethod,
     SourceLocation PragmaLoc) {
Index: clang/lib/Sema/Sema.cpp
===================================================================
--- clang/lib/Sema/Sema.cpp
+++ clang/lib/Sema/Sema.cpp
@@ -157,8 +157,9 @@
           LangOpts.getMSPointerToMemberRepresentationMethod()),
       VtorDispStack(LangOpts.getVtorDispMode()), PackStack(0),
       DataSegStack(nullptr), BSSSegStack(nullptr), ConstSegStack(nullptr),
-      CodeSegStack(nullptr), CurInitSeg(nullptr), VisContext(nullptr),
-      PragmaAttributeCurrentTargetDecl(nullptr),
+      CodeSegStack(nullptr), FpPragmaStack(FPFeatures.getInt()),
+      CurInitSeg(nullptr),
+      VisContext(nullptr), PragmaAttributeCurrentTargetDecl(nullptr),
       IsBuildingRecoveryCallExpr(false), Cleanup{}, LateTemplateParser(nullptr),
       LateTemplateParserCleanup(nullptr), OpaqueParser(nullptr), IdResolver(pp),
       StdExperimentalNamespaceCache(nullptr), StdInitializerList(nullptr),
Index: clang/lib/Parse/Parser.cpp
===================================================================
--- clang/lib/Parse/Parser.cpp
+++ clang/lib/Parse/Parser.cpp
@@ -763,6 +763,9 @@
   case tok::annot_pragma_fenv_access:
     HandlePragmaFEnvAccess();
     return nullptr;
+  case tok::annot_pragma_float_control:
+    HandlePragmaFloatControl();
+    return nullptr;
   case tok::annot_pragma_fp:
     HandlePragmaFP();
     break;
Index: clang/lib/Parse/ParseStmt.cpp
===================================================================
--- clang/lib/Parse/ParseStmt.cpp
+++ clang/lib/Parse/ParseStmt.cpp
@@ -353,13 +353,13 @@
 
   case tok::annot_pragma_fp_contract:
     ProhibitAttributes(Attrs);
-    Diag(Tok, diag::err_pragma_fp_contract_scope);
+    Diag(Tok, diag::err_pragma_file_or_compound_scope) << "fp_contract";
     ConsumeAnnotationToken();
     return StmtError();
 
   case tok::annot_pragma_fp:
     ProhibitAttributes(Attrs);
-    Diag(Tok, diag::err_pragma_fp_scope);
+    Diag(Tok, diag::err_pragma_file_or_compound_scope) << "clang fp";
     ConsumeAnnotationToken();
     return StmtError();
 
@@ -368,6 +368,12 @@
     HandlePragmaFEnvAccess();
     return StmtEmpty();
 
+  case tok::annot_pragma_float_control:
+    ProhibitAttributes(Attrs);
+    Diag(Tok, diag::err_pragma_file_or_compound_scope) << "float_control";
+    ConsumeAnnotationToken();
+    return StmtError();
+
   case tok::annot_pragma_opencl_extension:
     ProhibitAttributes(Attrs);
     HandlePragmaOpenCLExtension();
@@ -936,6 +942,9 @@
     case tok::annot_pragma_fenv_access:
       HandlePragmaFEnvAccess();
       break;
+    case tok::annot_pragma_float_control:
+      HandlePragmaFloatControl();
+      break;
     case tok::annot_pragma_ms_pointers_to_members:
       HandlePragmaMSPointersToMembers();
       break;
Index: clang/lib/Parse/ParsePragma.cpp
===================================================================
--- clang/lib/Parse/ParsePragma.cpp
+++ clang/lib/Parse/ParsePragma.cpp
@@ -184,6 +184,16 @@
   Sema &Actions;
 };
 
+struct PragmaFloatControlHandler : public PragmaHandler {
+  PragmaFloatControlHandler(Sema &Actions)
+    : PragmaHandler("float_control"), Actions(Actions) {}
+  void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+                    Token &FirstToken) override;
+
+private:
+  Sema &Actions;
+};
+
 struct PragmaMSPointersToMembers : public PragmaHandler {
   explicit PragmaMSPointersToMembers() : PragmaHandler("pointers_to_members") {}
   void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
@@ -334,6 +344,9 @@
     PP.AddPragmaHandler(MSCommentHandler.get());
   }
 
+  FloatControlHandler =
+      std::make_unique<PragmaFloatControlHandler>(Actions);
+  PP.AddPragmaHandler(FloatControlHandler.get());
   if (getLangOpts().MicrosoftExt) {
     MSDetectMismatchHandler =
         std::make_unique<PragmaDetectMismatchHandler>(Actions);
@@ -438,6 +451,8 @@
   PP.RemovePragmaHandler("clang", PCSectionHandler.get());
   PCSectionHandler.reset();
 
+  PP.RemovePragmaHandler(FloatControlHandler.get());
+  FloatControlHandler.reset();
   if (getLangOpts().MicrosoftExt) {
     PP.RemovePragmaHandler(MSDetectMismatchHandler.get());
     MSDetectMismatchHandler.reset();
@@ -646,6 +661,18 @@
   ConsumeAnnotationToken();
 }
 
+void Parser::HandlePragmaFloatControl() {
+  assert(Tok.is(tok::annot_pragma_float_control));
+
+  uintptr_t Value = reinterpret_cast<uintptr_t>(Tok.getAnnotationValue());
+  Sema::PragmaMsStackAction Action =
+      static_cast<Sema::PragmaMsStackAction>((Value >> 16) & 0xFFFF);
+  PragmaFloatControlKind Kind =
+    PragmaFloatControlKind(Value & 0xFFFF);
+  SourceLocation PragmaLoc = ConsumeAnnotationToken();
+  Actions.ActOnPragmaFloatControl(PragmaLoc, Action, Kind);
+}
+
 void Parser::HandlePragmaFEnvAccess() {
   assert(Tok.is(tok::annot_pragma_fenv_access));
   tok::OnOffSwitch OOS =
@@ -2489,6 +2516,129 @@
   PP.EnterToken(AnnotTok, /*IsReinject*/ false);
 }
 
+/// Handle the \#pragma float_control extension.
+///
+/// The syntax is:
+/// \code
+///   #pragma float_control(keyword[, setting] [,push])
+/// \endcode
+/// Where 'keyword' and 'setting' are identifiers.
+// 'keyword' can be: precise, except, push, pop
+// 'setting' can be: on, off
+/// The optional arguments 'setting' and 'push' are supported only
+/// when the keyword is 'precise' or 'except'.
+void PragmaFloatControlHandler::HandlePragma(Preprocessor &PP,
+                                               PragmaIntroducer Introducer,
+                                               Token &Tok) {
+  Sema::PragmaMsStackAction Action = Sema::PSK_Set;
+  SourceLocation FloatControlLoc = Tok.getLocation();
+  PP.Lex(Tok);
+  if (Tok.isNot(tok::l_paren)) {
+    PP.Diag(FloatControlLoc, diag::err_expected) << tok::l_paren;
+    return;
+  }
+
+  // Read the identifier.
+  PP.Lex(Tok);
+  if (Tok.isNot(tok::identifier)) {
+    PP.Diag(Tok.getLocation(), diag::err_pragma_float_control_malformed);
+    return;
+  }
+
+  // Verify that this is one of the float control options.
+  IdentifierInfo *II = Tok.getIdentifierInfo();
+  PragmaFloatControlKind Kind =
+    llvm::StringSwitch<PragmaFloatControlKind>(
+      II->getName())
+    .Case("precise", PFC_Precise)
+    .Case("except",  PFC_Except)
+    .Case("push",    PFC_Push)
+    .Case("pop",     PFC_Pop)
+    .Default(PFC_Unknown);
+  PP.Lex(Tok);  // the identifier
+  if (Kind == PFC_Unknown) {
+    PP.Diag(Tok.getLocation(), diag::err_pragma_float_control_unknown_kind);
+    return;
+  } else if (Kind == PFC_Push ||
+             Kind == PFC_Pop) {
+    if (Tok.isNot(tok::r_paren)) {
+      PP.Diag(Tok.getLocation(), diag::err_pragma_float_control_malformed);
+      return;
+    }
+    PP.Lex(Tok);  // Eat the r_paren
+    Action = (Kind == PFC_Pop) ?  Sema::PSK_Pop : Sema::PSK_Push;
+  } else {
+    if (Tok.is(tok::r_paren))
+      // Selecting Precise or Except
+      PP.Lex(Tok); // the r_paren
+    else if (Tok.isNot(tok::comma)) {
+      PP.Diag(Tok.getLocation(), diag::err_pragma_float_control_malformed);
+      return;
+    } else {
+      PP.Lex(Tok); // ,
+      if (!Tok.isAnyIdentifier()) {
+        PP.Diag(Tok.getLocation(), diag::err_pragma_float_control_malformed);
+        return;
+      }
+      StringRef PushOnOff = Tok.getIdentifierInfo()->getName();
+      if (PushOnOff == "on")
+        // Kind is set correctly
+        ;
+      else if (PushOnOff == "off") {
+        if (Kind == PFC_Precise )
+          Kind = PFC_NoPrecise ;
+        if (Kind == PFC_Except )
+          Kind = PFC_NoExcept ;
+      } else if (PushOnOff == "push") {
+        Action = Sema::PSK_Push_Set;
+      } else {
+        PP.Diag(Tok.getLocation(), diag::err_pragma_float_control_malformed);
+        return;
+      }
+      PP.Lex(Tok); // the identifier
+      if (Tok.is(tok::comma)) {
+        PP.Lex(Tok); // ,
+        if (!Tok.isAnyIdentifier()) {
+          PP.Diag(Tok.getLocation(), diag::err_pragma_float_control_malformed);
+          return;
+        }
+        StringRef ExpectedPush = Tok.getIdentifierInfo()->getName();
+        if (ExpectedPush == "push") {
+          Action = Sema::PSK_Push_Set;
+        } else {
+          PP.Diag(Tok.getLocation(), diag::err_pragma_float_control_malformed);
+          return;
+        }
+        PP.Lex(Tok); // the push identifier
+      }
+      if (Tok.isNot(tok::r_paren)) {
+        PP.Diag(Tok.getLocation(), diag::err_pragma_float_control_malformed);
+        return;
+      }
+      PP.Lex(Tok); // the r_paren
+    }
+  }
+  SourceLocation EndLoc = Tok.getLocation();
+  if (Tok.isNot(tok::eod)) {
+    PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol)
+      << "float_control";
+    return;
+  }
+
+  // Note: there is no accomodation for PP callback for this pragma.
+
+  // Enter the annotation.
+  auto TokenArray = std::make_unique<Token[]>(1);
+  TokenArray[0].startToken();
+  TokenArray[0].setKind(tok::annot_pragma_float_control);
+  TokenArray[0].setLocation(FloatControlLoc);
+  TokenArray[0].setAnnotationEndLoc(EndLoc);
+  TokenArray[0].setAnnotationValue(reinterpret_cast<void *>(
+      static_cast<uintptr_t>((Action << 16) | (Kind & 0xFFFF))));
+  PP.EnterTokenStream(std::move(TokenArray), 1,
+                      /*DisableMacroExpansion=*/false, /*IsReinject=*/false);
+}
+
 /// Handle the Microsoft \#pragma detect_mismatch extension.
 ///
 /// The syntax is:
Index: clang/lib/Parse/ParseDeclCXX.cpp
===================================================================
--- clang/lib/Parse/ParseDeclCXX.cpp
+++ clang/lib/Parse/ParseDeclCXX.cpp
@@ -3361,6 +3361,14 @@
     // are complete and we can parse the delayed portions of method
     // declarations and the lexed inline method definitions, along with any
     // delayed attributes.
+
+    // Save the state of Sema.FPFeatures, and change the setting
+    // to the levels specified on the command line.  Previous level
+    // will be restored when the RAII object is destroyed.
+    Sema::FPFeaturesStateRAII SaveFPFeaturesState(Actions);
+    FPOptions fpOptions(getLangOpts());
+    Actions.FPFeatures.Restore(fpOptions.getInt());
+
     SourceLocation SavedPrevTokLocation = PrevTokLocation;
     ParseLexedPragmas(getCurrentClass());
     ParseLexedAttributes(getCurrentClass());
Index: clang/lib/Frontend/CompilerInvocation.cpp
===================================================================
--- clang/lib/Frontend/CompilerInvocation.cpp
+++ clang/lib/Frontend/CompilerInvocation.cpp
@@ -2475,6 +2475,7 @@
 static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK,
                           const TargetOptions &TargetOpts,
                           PreprocessorOptions &PPOpts,
+                          CodeGenOptions &CGOpts,
                           DiagnosticsEngine &Diags) {
   // FIXME: Cleanup per-file based stuff.
   LangStandard::Kind LangStd = LangStandard::lang_unspecified;
@@ -3187,6 +3188,15 @@
   Opts.UnsafeFPMath = Args.hasArg(OPT_menable_unsafe_fp_math) ||
                       Args.hasArg(OPT_cl_unsafe_math_optimizations) ||
                       Args.hasArg(OPT_cl_fast_relaxed_math);
+  Opts.AllowFPReassoc = Opts.FastMath || CGOpts.Reassociate;
+  Opts.NoHonorNaNs = Opts.FastMath || CGOpts.NoNaNsFPMath ||
+                     Opts.FiniteMathOnly;
+  Opts.NoHonorInfs = Opts.FastMath || CGOpts.NoInfsFPMath ||
+                     Opts.FiniteMathOnly;
+  Opts.NoSignedZero = Opts.FastMath || CGOpts.NoSignedZeros;
+  Opts.AllowRecip = Opts.FastMath || CGOpts.ReciprocalMath;
+  // Currently there's no clang option to enable this individually
+  Opts.ApproxFunc = Opts.FastMath;
 
   if (Arg *A = Args.getLastArg(OPT_ffp_contract)) {
     StringRef Val = A->getValue();
@@ -3607,7 +3617,7 @@
     // Other LangOpts are only initialized when the input is not AST or LLVM IR.
     // FIXME: Should we really be calling this for an Language::Asm input?
     ParseLangArgs(LangOpts, Args, DashX, Res.getTargetOpts(),
-                  Res.getPreprocessorOpts(), Diags);
+                  Res.getPreprocessorOpts(), Res.getCodeGenOpts(), Diags);
     if (Res.getFrontendOpts().ProgramAction == frontend::RewriteObjC)
       LangOpts.ObjCExceptions = 1;
     if (T.isOSDarwin() && DashX.isPreprocessed()) {
Index: clang/lib/CodeGen/CodeGenFunction.h
===================================================================
--- clang/lib/CodeGen/CodeGenFunction.h
+++ clang/lib/CodeGen/CodeGenFunction.h
@@ -4420,6 +4420,16 @@
 }
 
 }  // end namespace CodeGen
+
+// Map the LangOption for floating point rounding mode into
+// the corresponding enum in the IR.
+llvm::fp::RoundingMode ToConstrainedRoundingMD(
+  LangOptions::FPRoundingModeKind Kind);
+
+// Map the LangOption for floating point exception behavior into
+// the corresponding enum in the IR.
+llvm::fp::ExceptionBehavior ToConstrainedExceptMD(
+  LangOptions::FPExceptionModeKind Kind);
 }  // end namespace clang
 
 #endif
Index: clang/lib/CodeGen/CodeGenFunction.cpp
===================================================================
--- clang/lib/CodeGen/CodeGenFunction.cpp
+++ clang/lib/CodeGen/CodeGenFunction.cpp
@@ -117,7 +117,7 @@
 
 // Map the LangOption for rounding mode into
 // the corresponding enum in the IR.
-static llvm::fp::RoundingMode ToConstrainedRoundingMD(
+llvm::fp::RoundingMode clang::ToConstrainedRoundingMD(
   LangOptions::FPRoundingModeKind Kind) {
 
   switch (Kind) {
@@ -132,7 +132,7 @@
 
 // Map the LangOption for exception behavior into
 // the corresponding enum in the IR.
-static llvm::fp::ExceptionBehavior ToConstrainedExceptMD(
+llvm::fp::ExceptionBehavior clang::ToConstrainedExceptMD(
   LangOptions::FPExceptionModeKind Kind) {
 
   switch (Kind) {
@@ -149,14 +149,14 @@
   auto fpExceptionBehavior = ToConstrainedExceptMD(
                                getLangOpts().getFPExceptionMode());
 
+  Builder.setDefaultConstrainedRounding(fpRoundingMode);
+  Builder.setDefaultConstrainedExcept(fpExceptionBehavior);
   if (fpExceptionBehavior == llvm::fp::ebIgnore &&
       fpRoundingMode == llvm::fp::rmToNearest)
     // Constrained intrinsics are not used.
-    ;
+    Builder.setIsFPConstrained(false);
   else {
     Builder.setIsFPConstrained(true);
-    Builder.setDefaultConstrainedRounding(fpRoundingMode);
-    Builder.setDefaultConstrainedExcept(fpExceptionBehavior);
   }
 }
 
@@ -923,9 +923,11 @@
       if (FD->isMain())
         Fn->addFnAttr(llvm::Attribute::NoRecurse);
 
-  if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D))
+  if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D)) {
+    Builder.setIsFPConstrained(FD->usesFPIntrin());
     if (FD->usesFPIntrin())
       Fn->addFnAttr(llvm::Attribute::StrictFP);
+  }
 
   // If a custom alignment is used, force realigning to this alignment on
   // any main function which certainly will need it.
Index: clang/lib/CodeGen/CGExprScalar.cpp
===================================================================
--- clang/lib/CodeGen/CGExprScalar.cpp
+++ clang/lib/CodeGen/CGExprScalar.cpp
@@ -216,7 +216,14 @@
 /// Update the FastMathFlags of LLVM IR from the FPOptions in LangOptions.
 static void updateFastMathFlags(llvm::FastMathFlags &FMF,
                                 FPOptions FPFeatures) {
-  FMF.setAllowContract(FPFeatures.allowFPContractAcrossStatement());
+  FMF.setAllowReassoc(FPFeatures.allowReassoc());
+  FMF.setNoNaNs(FPFeatures.noNaNs());
+  FMF.setNoInfs(FPFeatures.noInfs());
+  FMF.setNoSignedZeros(FPFeatures.noSignedZeros());
+  FMF.setAllowReciprocal(FPFeatures.allowReciprocal());
+  FMF.setApproxFunc(FPFeatures.approxFunc());
+  FMF.setAllowContract(FPFeatures.allowFPContractAcrossStatement() ||
+                       FPFeatures.allowFPContractWithinStatement());
 }
 
 /// Propagate fast-math flags from \p Op to the instruction in \p V.
@@ -423,6 +430,36 @@
 
   Value *Visit(Expr *E) {
     ApplyDebugLocation DL(CGF, E);
+    if (BinaryOperator * BinOp = dyn_cast<BinaryOperator>(E)) {
+      //  Preserve the old values
+      llvm::IRBuilder<>::FastMathFlagGuard FMFG(Builder);
+      auto FPFeatures = BinOp->getFPFeatures();
+      auto NewRoundingBehavior = ToConstrainedRoundingMD(
+                                    FPFeatures.getRoundingMode());
+      Builder.setDefaultConstrainedRounding(NewRoundingBehavior);
+      auto NewExceptionBehavior = ToConstrainedExceptMD(
+                                    FPFeatures.getExceptionMode());
+      Builder.setDefaultConstrainedExcept(NewExceptionBehavior);
+      auto FMF = Builder.getFastMathFlags();
+      FMF.setAllowReassoc(FPFeatures.allowReassoc());
+      FMF.setNoNaNs(FPFeatures.noNaNs());
+      FMF.setNoInfs(FPFeatures.noInfs());
+      FMF.setNoSignedZeros(FPFeatures.noSignedZeros());
+      FMF.setAllowReciprocal(FPFeatures.allowReciprocal());
+      FMF.setApproxFunc(FPFeatures.approxFunc());
+      FMF.setAllowContract(FPFeatures.allowFPContractAcrossStatement() ||
+                       FPFeatures.allowFPContractWithinStatement());
+      Builder.setFastMathFlags(FMF);
+      assert((CGF.CurFuncDecl==nullptr ||
+             Builder.getIsFPConstrained() ||
+             isa<CXXConstructorDecl>(CGF.CurFuncDecl) ||
+             isa<CXXDestructorDecl>(CGF.CurFuncDecl) ||
+          (NewExceptionBehavior == llvm::fp::ebIgnore &&
+           NewRoundingBehavior == llvm::fp::rmToNearest)) &&
+          "FPConstrained should be enabled on entire function");
+
+      return StmtVisitor<ScalarExprEmitter, Value*>::Visit(E);
+    }
     return StmtVisitor<ScalarExprEmitter, Value*>::Visit(E);
   }
 
Index: clang/include/clang/Serialization/ASTWriter.h
===================================================================
--- clang/include/clang/Serialization/ASTWriter.h
+++ clang/include/clang/Serialization/ASTWriter.h
@@ -502,6 +502,7 @@
   void WriteMSStructPragmaOptions(Sema &SemaRef);
   void WriteMSPointersToMembersPragmaOptions(Sema &SemaRef);
   void WritePackPragmaOptions(Sema &SemaRef);
+  void WriteFloatControlPragmaOptions(Sema &SemaRef);
   void WriteModuleFileExtension(Sema &SemaRef,
                                 ModuleFileExtensionWriter &Writer);
 
Index: clang/include/clang/Serialization/ASTReader.h
===================================================================
--- clang/include/clang/Serialization/ASTReader.h
+++ clang/include/clang/Serialization/ASTReader.h
@@ -856,6 +856,18 @@
   int PragmaMSPointersToMembersState = -1;
   SourceLocation PointersToMembersPragmaLocation;
 
+  /// The pragma float_control state.
+  Optional<unsigned> FpPragmaCurrentValue;
+  SourceLocation FpPragmaCurrentLocation;
+  struct FpPragmaStackEntry {
+    unsigned Value;
+    SourceLocation Location;
+    SourceLocation PushLocation;
+    StringRef SlotLabel;
+  };
+  llvm::SmallVector<FpPragmaStackEntry, 2> FpPragmaStack;
+  llvm::SmallVector<std::string, 2> FpPragmaStrings;
+
   /// The pragma pack state.
   Optional<unsigned> PragmaPackCurrentValue;
   SourceLocation PragmaPackCurrentLocation;
Index: clang/include/clang/Serialization/ASTBitCodes.h
===================================================================
--- clang/include/clang/Serialization/ASTBitCodes.h
+++ clang/include/clang/Serialization/ASTBitCodes.h
@@ -650,7 +650,10 @@
       PP_CONDITIONAL_STACK = 62,
 
       /// A table of skipped ranges within the preprocessing record.
-      PPD_SKIPPED_RANGES = 63
+      PPD_SKIPPED_RANGES = 63,
+
+      /// Record code for \#pragma float_control options.
+      FLOAT_CONTROL_PRAGMA_OPTIONS = 64
     };
 
     /// Record types used within a source manager block.
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -552,6 +552,9 @@
   PragmaStack<StringLiteral *> ConstSegStack;
   PragmaStack<StringLiteral *> CodeSegStack;
 
+    // This stacks the current state of Sema.FPFeatures
+  PragmaStack<unsigned> FpPragmaStack;
+
   // RAII object to push / pop sentinel slots for all MS #pragma stacks.
   // Actions should be performed only if we enter / exit a C++ method body.
   class PragmaStackSentinelRAII {
@@ -9395,6 +9398,11 @@
   void ActOnPragmaDetectMismatch(SourceLocation Loc, StringRef Name,
                                  StringRef Value);
 
+  /// ActOnPragmaFloatControl - Call on well-formed \#pragma float_control
+  void ActOnPragmaFloatControl(SourceLocation Loc,
+                               PragmaMsStackAction Action,
+                               PragmaFloatControlKind Value);
+
   /// ActOnPragmaUnused - Called on well-formed '\#pragma unused'.
   void ActOnPragmaUnused(const Token &Identifier,
                          Scope *curScope,
Index: clang/include/clang/Parse/Parser.h
===================================================================
--- clang/include/clang/Parse/Parser.h
+++ clang/include/clang/Parse/Parser.h
@@ -178,6 +178,7 @@
   std::unique_ptr<PragmaHandler> PCSectionHandler;
   std::unique_ptr<PragmaHandler> MSCommentHandler;
   std::unique_ptr<PragmaHandler> MSDetectMismatchHandler;
+  std::unique_ptr<PragmaHandler> FloatControlHandler;
   std::unique_ptr<PragmaHandler> MSPointersToMembers;
   std::unique_ptr<PragmaHandler> MSVtorDisp;
   std::unique_ptr<PragmaHandler> MSInitSeg;
@@ -721,6 +722,10 @@
   /// #pragma STDC FENV_ACCESS...
   void HandlePragmaFEnvAccess();
 
+  /// Handle the annotation token produced for
+  /// #pragma float_control
+  void HandlePragmaFloatControl();
+
   /// \brief Handle the annotation token produced for
   /// #pragma clang fp ...
   void HandlePragmaFP();
Index: clang/include/clang/Basic/TokenKinds.def
===================================================================
--- clang/include/clang/Basic/TokenKinds.def
+++ clang/include/clang/Basic/TokenKinds.def
@@ -806,6 +806,11 @@
 // handles them.
 PRAGMA_ANNOTATION(pragma_fenv_access)
 
+// Annotation for #pragma float_control
+// The lexer produces these so that they only take effect when the parser
+// handles them.
+PRAGMA_ANNOTATION(pragma_float_control)
+
 // Annotation for #pragma pointers_to_members...
 // The lexer produces these so that they only take effect when the parser
 // handles them.
Index: clang/include/clang/Basic/PragmaKinds.h
===================================================================
--- clang/include/clang/Basic/PragmaKinds.h
+++ clang/include/clang/Basic/PragmaKinds.h
@@ -25,6 +25,16 @@
   PMSST_ON   // #pragms ms_struct on
 };
 
+enum PragmaFloatControlKind {
+  PFC_Unknown,
+  PFC_Precise,    // #pragma float_control(precise, [,on])
+  PFC_NoPrecise,  // #pragma float_control(precise, off)
+  PFC_Except,     // #pragma float_control(except [,on])
+  PFC_NoExcept,   // #pragma float_control(except, off)
+  PFC_Push,       // #pragma float_control(push)
+  PFC_Pop         // #pragma float_control(pop)
+};
+
 }
 
 #endif
Index: clang/include/clang/Basic/LangOptions.h
===================================================================
--- clang/include/clang/Basic/LangOptions.h
+++ clang/include/clang/Basic/LangOptions.h
@@ -359,25 +359,48 @@
   FPOptions() : fp_contract(LangOptions::FPC_Off),
                 fenv_access(LangOptions::FEA_Off),
                 rounding(LangOptions::FPR_ToNearest),
-                exceptions(LangOptions::FPE_Ignore)
-        {}
+                exceptions(LangOptions::FPE_Ignore),
+                allow_reassoc(0),
+                no_nans(0),
+                no_infs(0),
+                no_signed_zeros(0),
+                allow_reciprocal(0),
+                approx_func(0)
+                {}
 
   // Used for serializing.
   explicit FPOptions(unsigned I)
       : fp_contract(static_cast<LangOptions::FPContractModeKind>(I & 3)),
         fenv_access(static_cast<LangOptions::FEnvAccessModeKind>((I >> 2) & 1)),
         rounding(static_cast<LangOptions::FPRoundingModeKind>((I >> 3) & 7)),
-        exceptions(static_cast<LangOptions::FPExceptionModeKind>((I >> 6) & 3))
+        exceptions(static_cast<LangOptions::FPExceptionModeKind>((I >> 6) & 3)),
+        allow_reassoc((I>>8) & 1),
+        no_nans((I>>9) & 1),
+        no_infs((I>>10) & 1),
+        no_signed_zeros((I>>11) & 1),
+        allow_reciprocal((I>>12) & 1),
+        approx_func((I>>13) & 1)
         {}
 
   explicit FPOptions(const LangOptions &LangOpts)
       : fp_contract(LangOpts.getDefaultFPContractMode()),
         fenv_access(LangOptions::FEA_Off),
-        rounding(LangOptions::FPR_ToNearest),
-        exceptions(LangOptions::FPE_Ignore)
+        rounding(LangOpts.getFPRoundingMode()),
+        exceptions(LangOpts.getFPExceptionMode()),
+        allow_reassoc(LangOpts.FastMath || LangOpts.AllowFPReassoc),
+        no_nans(LangOpts.FastMath || LangOpts.NoHonorNaNs),
+        no_infs(LangOpts.FastMath || LangOpts.NoHonorInfs),
+        no_signed_zeros(LangOpts.FastMath || LangOpts.NoSignedZero),
+        allow_reciprocal(LangOpts.FastMath || LangOpts.AllowRecip),
+        approx_func(LangOpts.FastMath || LangOpts.ApproxFunc)
         {}
   // FIXME: Use getDefaultFEnvAccessMode() when available.
 
+  void setFastMath(bool B = true) {
+    allow_reassoc = no_nans = no_infs = no_signed_zeros = approx_func =
+      allow_reciprocal = B;
+  }
+
   bool allowFPContractWithinStatement() const {
     return fp_contract == LangOptions::FPC_On;
   }
@@ -404,6 +427,18 @@
     fenv_access = LangOptions::FEA_On;
   }
 
+  void setFPPreciseEnabled(bool Value) {
+    if (Value) {
+      /* Precise mode implies fp_contract=on and disables ffast-math */
+      setFastMath(false);
+      setAllowFPContractWithinStatement();
+    } else {
+      /* Precise mode implies fp_contract=fast and enables ffast-math */
+      setFastMath(true);
+      setAllowFPContractAcrossStatement();
+    }
+  }
+
   void setDisallowFEnvAccess() { fenv_access = LangOptions::FEA_Off; }
 
   LangOptions::FPRoundingModeKind getRoundingMode() const {
@@ -422,6 +457,34 @@
     exceptions = EM;
   }
 
+  /// Flag queries
+  bool allowReassoc() const    { return allow_reassoc; }
+  bool noNaNs() const          { return no_nans; }
+  bool noInfs() const          { return no_infs; }
+  bool noSignedZeros() const   { return no_signed_zeros; }
+  bool allowReciprocal() const { return allow_reciprocal; }
+  bool approxFunc() const      { return approx_func; }
+
+  /// Flag setters
+  void setAllowReassoc(bool B = true) {
+    allow_reassoc = B;
+  }
+  void setNoNaNs(bool B = true) {
+    no_nans = B;
+  }
+  void setNoInfs(bool B = true) {
+    no_infs = B;
+  }
+  void setNoSignedZeros(bool B = true) {
+    no_signed_zeros = B;
+  }
+  void setAllowReciprocal(bool B = true) {
+    allow_reciprocal = B;
+  }
+  void setApproxFunc(bool B = true) {
+    approx_func = B;
+  }
+
   bool isFPConstrained() const {
     return getRoundingMode() != LangOptions::FPR_ToNearest ||
            getExceptionMode() != LangOptions::FPE_Ignore ||
@@ -431,7 +494,24 @@
   /// Used to serialize this.
   unsigned getInt() const {
     return fp_contract | (fenv_access << 2) | (rounding << 3)
-        | (exceptions << 6);
+        | (exceptions << 6)
+        | (allow_reassoc << 8) | (no_nans << 9)
+        | (no_infs << 10) | (no_signed_zeros << 11)
+        | (allow_reciprocal << 12) | (approx_func << 13);
+  }
+
+  /// Used with getInt() to manage the float_control pragma stack.
+  void Restore(unsigned I) {
+    fp_contract = (static_cast<LangOptions::FPContractModeKind>(I & 3));
+    fenv_access = (static_cast<LangOptions::FEnvAccessModeKind>((I >> 2) & 1));
+    rounding = (static_cast<LangOptions::FPRoundingModeKind>((I >> 3) & 7));
+    exceptions = (static_cast<LangOptions::FPExceptionModeKind>((I >> 6) & 3));
+    allow_reassoc = ((I>>8) & 1);
+    no_nans = ((I>>9) & 1);
+    no_infs = ((I>>10) & 1);
+    no_signed_zeros = ((I>>11) & 1);
+    allow_reciprocal = ((I>>12) & 1);
+    approx_func = ((I>>13) & 1);
   }
 
 private:
@@ -442,6 +522,12 @@
   unsigned fenv_access : 1;
   unsigned rounding : 3;
   unsigned exceptions : 2;
+  unsigned allow_reassoc : 1;
+  unsigned no_nans : 1;
+  unsigned no_infs : 1;
+  unsigned no_signed_zeros : 1;
+  unsigned allow_reciprocal : 1;
+  unsigned approx_func : 1;
 };
 
 /// Describes the kind of translation unit being processed.
Index: clang/include/clang/Basic/LangOptions.def
===================================================================
--- clang/include/clang/Basic/LangOptions.def
+++ clang/include/clang/Basic/LangOptions.def
@@ -188,6 +188,12 @@
 COMPATIBLE_LANGOPT(FastMath          , 1, 0, "fast FP math optimizations, and __FAST_MATH__ predefined macro")
 COMPATIBLE_LANGOPT(FiniteMathOnly    , 1, 0, "__FINITE_MATH_ONLY__ predefined macro")
 COMPATIBLE_LANGOPT(UnsafeFPMath      , 1, 0, "Unsafe Floating Point Math")
+COMPATIBLE_LANGOPT(AllowFPReassoc      , 1, 0, "Permit Floating Point reassociation")
+COMPATIBLE_LANGOPT(NoHonorNaNs      , 1, 0, "Permit Floating Point optimization without regard to NaN")
+COMPATIBLE_LANGOPT(NoHonorInfs      , 1, 0, "Permit Floating Point optimization without regard to infinities")
+COMPATIBLE_LANGOPT(NoSignedZero      , 1, 0, "Permit Floating Point optimization without regard to signed zeros")
+COMPATIBLE_LANGOPT(AllowRecip        , 1, 0, "Permit Floating Point reciprocal")
+COMPATIBLE_LANGOPT(ApproxFunc        , 1, 0, "Permit Floating Point approximation")
 
 BENIGN_LANGOPT(ObjCGCBitmapPrint , 1, 0, "printing of GC's bitmap layout for __weak/__strong ivars")
 
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -854,6 +854,8 @@
   "specifying both a name and alignment to 'pop' is undefined">;
 def warn_pragma_pop_failed : Warning<"#pragma %0(pop, ...) failed: %1">,
   InGroup<IgnoredPragmas>;
+def err_pragma_fc_pp_scope : Error<
+  "'#pragma float_control push/pop' can only appear at file scope">;
 def warn_cxx_ms_struct :
   Warning<"ms_struct may not produce Microsoft-compatible layouts for classes "
           "with base classes or virtual functions">,
Index: clang/include/clang/Basic/DiagnosticParseKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticParseKinds.td
+++ clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1109,9 +1109,9 @@
   "'#pragma init_seg' is only supported when targeting a "
   "Microsoft environment">,
   InGroup<IgnoredPragmas>;
-// - #pragma fp_contract
-def err_pragma_fp_contract_scope : Error<
-  "'#pragma fp_contract' can only appear at file scope or at the start of a "
+// - #pragma restricted to file scope or start of compound statement
+def err_pragma_file_or_compound_scope : Error<
+  "'#pragma %0' can only appear at file scope or at the start of a "
   "compound statement">;
 // - #pragma stdc unknown
 def ext_stdc_pragma_ignored : ExtWarn<"unknown pragma in STDC namespace">,
@@ -1130,6 +1130,12 @@
 def err_pragma_detect_mismatch_malformed : Error<
   "pragma detect_mismatch is malformed; it requires two comma-separated "
   "string literals">;
+// - #pragma float_control
+def err_pragma_float_control_malformed : Error<
+  "pragma float_control is malformed; use 'float_control({push|pop})' or "
+  "'float_control({precise|except}, {on|off} [,push])'">;
+def err_pragma_float_control_unknown_kind : Error<
+  "unknown kind of pragma float_control">;
 // - #pragma pointers_to_members
 def err_pragma_pointers_to_members_unknown_kind : Error<
   "unexpected %0, expected to see one of %select{|'best_case', 'full_generality', }1"
@@ -1297,9 +1303,6 @@
 def err_pragma_fp_invalid_argument : Error<
   "unexpected argument '%0' to '#pragma clang fp %1'; "
   "expected 'on', 'fast' or 'off'">;
-def err_pragma_fp_scope : Error<
-  "'#pragma clang fp' can only appear at file scope or at the start of a "
-  "compound statement">;
 
 def err_pragma_invalid_keyword : Error<
   "invalid argument; expected 'enable'%select{|, 'full'}0%select{|, 'assume_safety'}1 or 'disable'">;
Index: clang/include/clang/AST/Stmt.h
===================================================================
--- clang/include/clang/AST/Stmt.h
+++ clang/include/clang/AST/Stmt.h
@@ -533,7 +533,7 @@
 
     /// This is only meaningful for operations on floating point
     /// types and 0 otherwise.
-    unsigned FPFeatures : 8;
+    unsigned FPFeatures : 14;
 
     SourceLocation OpLoc;
   };
@@ -604,7 +604,7 @@
     unsigned OperatorKind : 6;
 
     // Only meaningful for floating point types.
-    unsigned FPFeatures : 8;
+    unsigned FPFeatures : 14;
   };
 
   class CXXRewrittenBinaryOperatorBitfields {
@@ -1100,7 +1100,7 @@
   Stmt &operator=(Stmt &&) = delete;
 
   Stmt(StmtClass SC) {
-    static_assert(sizeof(*this) <= 8,
+    static_assert(sizeof(*this) <= 16,
                   "changing bitfields changed sizeof(Stmt)");
     static_assert(sizeof(*this) % alignof(void *) == 0,
                   "Insufficient alignment!");
Index: clang/docs/LanguageExtensions.rst
===================================================================
--- clang/docs/LanguageExtensions.rst
+++ clang/docs/LanguageExtensions.rst
@@ -3045,6 +3045,38 @@
 section of the code. This can be useful when fast contraction is otherwise
 enabled for the translation unit with the ``-ffp-contract=fast`` flag.
 
+The ``#pragma float_control`` pragma allows precise floating-point
+semantics and floating-point exception behavior to be specified
+for a section of the source code. This pragma can only appear at file scope or
+at the start of a compound statement (excluding comments). When using within a
+compound statement, the pragma is active within the scope of the compound
+statement.  This pragma is modeled after a Microsoft pragma with the
+same spelling and syntax.  For pragmas specified at file scope, a stack
+is supported so that the pragma float_control settings can be pushed or popped.
+
+When ``float_control(precise, on)`` is enabled, the section of code governed
+by the pragma behaves as though the command-line option ``ffp-model=precise``
+is enabled.  That is, fast-math is disabled and fp-contract=on (fused
+multiply add) is enabled.
+
+When ``float_control(except, on)`` is enabled, the section of code governed
+by the pragma behaves as though the command-line
+ ``ffp-exception-behavior=strict`` is enabled, ``float-control(precise, off)``
+selects ``ffp-exception-behavior=ignore``.
+
+The full syntax this pragma supports is
+``float_control(except|precise, on|off [, push])`` and
+``float_control(push|pop)``.
+The ``push`` and ``pop`` forms can only occur at file scope.
+
+.. code-block:: c++
+
+  for(...) {
+    // This block will be compiled with fno-fast-math and ffp-contract=on
+    #pragma float_control(precise, on)
+    a = b[i] * c[i] + e;
+  }
+
 Specifying an attribute for multiple declarations (#pragma clang attribute)
 ===========================================================================
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to