mibintc created this revision.
mibintc added reviewers: andrew.w.kaylor, kpn, rjmccall, sepavloff.
Herald added projects: clang, LLVM.
Herald added subscribers: llvm-commits, cfe-commits.

Intel would like to support #pragma float_control which allows control over 
precision and exception behavior at the source level. This pragma is supported 
by both the Microsoft compiler and the Intel compiler, and our customers have 
found it useful.  This message is to describe the pragma, provide the patch for 
review, and request feedback from the community.

As the pragma was originally defined in the Microsoft compiler, the pragma 
supports a stack of settings, so that you can use push and pop to save and 
restore the state. That functionality is already in clang and this patch 
piggy-backs on that support (reference PragmaStack and PragmaMsStackAction). 
The Microsoft compiler supports it only at file scope, but the Intel compiler, 
and this patch, supports the pragma at either file scope or at the beginning of 
a compound statement.  Clang currently provides support for pragma fp_contract 
which enables floating point contraction--also at file scope and compound 
statement, and uses FPFeatures on certain expression nodes to hold the pragma 
settings. This patch piggy-backs FPFeatures, adding fields to show the setting 
of "float_control(precise)" and "float_control(except)"

This patch includes an update to the pragma documentation, to summarize, using 
pragma float_control(precise) is like enabling ffp-model=precise for a region 
of the program. pragma float_control(except) is like enabling 
ffp-exception-behavior=strict/ignore for a region of the program.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D72841

Files:
  clang/docs/LanguageExtensions.rst
  clang/include/clang/AST/Decl.h
  clang/include/clang/AST/Stmt.h
  clang/include/clang/Basic/DiagnosticParseKinds.td
  clang/include/clang/Basic/LangOptions.h
  clang/include/clang/Basic/TokenKinds.def
  clang/include/clang/Parse/Parser.h
  clang/include/clang/Sema/Sema.h
  clang/lib/CodeGen/CGExprScalar.cpp
  clang/lib/CodeGen/CodeGenFunction.cpp
  clang/lib/CodeGen/CodeGenFunction.h
  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/test/CodeGen/fp-floatcontrol-class.cpp
  clang/test/CodeGen/fp-floatcontrol-pragma.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
@@ -299,10 +299,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 +316,9 @@
     ~FastMathFlagGuard() {
       Builder.FMF = FMF;
       Builder.DefaultFPMathTag = FPMathTag;
+      Builder.IsFPConstrained = IsFPConstrained;
+      Builder.DefaultConstrainedExcept = DefaultConstrainedExcept;
+      Builder.DefaultConstrainedRounding = DefaultConstrainedRounding;
     }
   };
 
Index: clang/test/CodeGen/fp-floatcontrol-pragma.cpp
===================================================================
--- /dev/null
+++ clang/test/CodeGen/fp-floatcontrol-pragma.cpp
@@ -0,0 +1,63 @@
+// RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -fsyntax-only -verify -DCHECK_ERROR %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 float
+//CHECK: fadd 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;
+}
+
+// There seems to be a bug in Actions.CurContext->isTranslationUnit()
+// unless this dummy class is used. FIXME
+class ResetTUScope;
+#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}}
+  return;
+}
+#endif
Index: clang/test/CodeGen/fp-floatcontrol-class.cpp
===================================================================
--- /dev/null
+++ clang/test/CodeGen/fp-floatcontrol-class.cpp
@@ -0,0 +1,26 @@
+// RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck %s
+// XFAIL: *
+
+// This get an assertion failure in Debug mode because the expression
+// occurs within a global_var_init_function. Those expressions are Sema'd
+// before the global_var_init_function is created, and therefore we don't
+// mark those function nodes as "usesPFIntrin". The assertion occurs in
+// ScalarExprEmitter: either Constrained mode is on, or the
+// expression requests non-constrained Rounding and ExceptionBehavior
+
+#pragma float_control(except, on)
+float y();
+class ON {
+float z = y() * 1;
+// CHECK-LABEL: define {{.*}} void @_ZN2ONC2Ev{{.*}}
+//CHECK: llvm.experimental.constrained.fmul{{.*}}
+};
+ON on;
+#pragma float_control( except, off)
+class OFF {
+float w = y() * 1;
+// CHECK-LABEL: define {{.*}} void @_ZN3OFFC2Ev{{.*}}
+//CHECK: fmul float
+};
+OFF off;
+
Index: clang/lib/Sema/SemaExpr.cpp
===================================================================
--- clang/lib/Sema/SemaExpr.cpp
+++ clang/lib/Sema/SemaExpr.cpp
@@ -13120,10 +13120,15 @@
     return ExprError();
 
   if (ResultTy->isRealFloatingType() &&
-      (getLangOpts().getFPRoundingMode() != LangOptions::FPR_ToNearest ||
+      ((FpPragmaStack.CurrentValue.HasPragma &&
+        FpPragmaStack.CurrentValue.IsExcept) ||
+       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)) {
+      // If the expression occurs inside an internal global_var_init_function
+      // then the FunctionDecl is not availble
+      // TBD
       F->setUsesFPIntrin(true);
     }
 
Index: clang/lib/Sema/SemaAttr.cpp
===================================================================
--- clang/lib/Sema/SemaAttr.cpp
+++ clang/lib/Sema/SemaAttr.cpp
@@ -407,6 +407,37 @@
   Consumer.HandleTopLevelDecl(DeclGroupRef(PDMD));
 }
 
+void Sema::ActOnPragmaFloatControl(SourceLocation Loc,
+                              PragmaMsStackAction Action,
+                              PragmaFloatControlDecl::FloatControlKind Value) {
+  auto NewValue = FpPragmaStack.CurrentValue;
+  NewValue.HasPragma = true;
+  switch(Value) {
+  default:
+    llvm_unreachable("invalid pragma float_control kind");
+  case PragmaFloatControlDecl::FC_Precise:
+    NewValue.IsPrecise = true;
+    break;
+  case PragmaFloatControlDecl::FC_NoPrecise:
+    NewValue.IsPrecise = false;
+    break;
+  case PragmaFloatControlDecl::FC_Except:
+    NewValue.IsExcept = true;
+    break;
+  case PragmaFloatControlDecl::FC_NoExcept:
+    NewValue.IsExcept = false;
+    break;
+  case PragmaFloatControlDecl::FC_Push:
+  case PragmaFloatControlDecl::FC_Pop:
+    break;
+  }
+  FpPragmaStack.Act(Loc, Action, StringRef(), NewValue);
+  NewValue = FpPragmaStack.CurrentValue;
+  FPFeatures.setFPPreciseEnabled(NewValue.IsPrecise);
+  FPFeatures.setFPExcept(NewValue.IsExcept ?
+              LangOptions::FPE_Strict : LangOptions::FPE_Ignore);
+}
+
 void Sema::ActOnPragmaMSPointersToMembers(
     LangOptions::PragmaMSPointersToMembersKind RepresentationMethod,
     SourceLocation PragmaLoc) {
Index: clang/lib/Sema/Sema.cpp
===================================================================
--- clang/lib/Sema/Sema.cpp
+++ clang/lib/Sema/Sema.cpp
@@ -140,6 +140,8 @@
       VtorDispStack(LangOpts.getVtorDispMode()), PackStack(0),
       DataSegStack(nullptr), BSSSegStack(nullptr), ConstSegStack(nullptr),
       CodeSegStack(nullptr), CurInitSeg(nullptr), VisContext(nullptr),
+      FpPragmaStack({false, false, pp.getLangOpts().getFPExceptionMode() ==
+                            LangOptions::FPE_Strict}),
       PragmaAttributeCurrentTargetDecl(nullptr),
       IsBuildingRecoveryCallExpr(false), Cleanup{}, LateTemplateParser(nullptr),
       LateTemplateParserCleanup(nullptr), OpaqueParser(nullptr), IdResolver(pp),
Index: clang/lib/Parse/Parser.cpp
===================================================================
--- clang/lib/Parse/Parser.cpp
+++ clang/lib/Parse/Parser.cpp
@@ -753,6 +753,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
@@ -368,6 +368,12 @@
     HandlePragmaFEnvAccess();
     return StmtEmpty();
 
+  case tok::annot_pragma_float_control:
+    ProhibitAttributes(Attrs);
+    Diag(Tok, diag::err_pragma_fc_scope);
+    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,
@@ -322,6 +332,9 @@
     PP.AddPragmaHandler(MSCommentHandler.get());
   }
 
+  FloatControlHandler =
+      std::make_unique<PragmaFloatControlHandler>(Actions);
+  PP.AddPragmaHandler(FloatControlHandler.get());
   if (getLangOpts().MicrosoftExt) {
     MSDetectMismatchHandler =
         std::make_unique<PragmaDetectMismatchHandler>(Actions);
@@ -420,6 +433,8 @@
   PP.RemovePragmaHandler("clang", PCSectionHandler.get());
   PCSectionHandler.reset();
 
+  PP.RemovePragmaHandler(FloatControlHandler.get());
+  FloatControlHandler.reset();
   if (getLangOpts().MicrosoftExt) {
     PP.RemovePragmaHandler(MSDetectMismatchHandler.get());
     MSDetectMismatchHandler.reset();
@@ -622,6 +637,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);
+  PragmaFloatControlDecl::FloatControlKind Kind =
+    PragmaFloatControlDecl::FloatControlKind(Value & 0xFFFF);
+  SourceLocation PragmaLoc = ConsumeAnnotationToken();
+  Actions.ActOnPragmaFloatControl(PragmaLoc, Action, Kind);
+}
+
 void Parser::HandlePragmaFEnvAccess() {
   assert(Tok.is(tok::annot_pragma_fenv_access));
   tok::OnOffSwitch OOS =
@@ -2465,6 +2492,138 @@
   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.
+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();
+  PragmaFloatControlDecl::FloatControlKind Kind =
+    llvm::StringSwitch<PragmaFloatControlDecl::FloatControlKind>(
+      II->getName())
+    .Case("precise", PragmaFloatControlDecl::FC_Precise)
+    .Case("except",  PragmaFloatControlDecl::FC_Except)
+    .Case("push",    PragmaFloatControlDecl::FC_Push)
+    .Case("pop",     PragmaFloatControlDecl::FC_Pop)
+    .Default(PragmaFloatControlDecl::FC_Unknown);
+  PP.Lex(Tok);  // the identifier
+  if (Kind == PragmaFloatControlDecl::FC_Unknown) {
+    PP.Diag(Tok.getLocation(), diag::err_pragma_float_control_unknown_kind);
+    return;
+  } else if (Kind == PragmaFloatControlDecl::FC_Push ||
+             Kind == PragmaFloatControlDecl::FC_Pop) {
+    if (!Actions.CurContext->isTranslationUnit()) {
+//FIXME this seems to be the wrong way to check file-scope
+//since the token immediately following a function definition
+//returns false, but the token immediately following a forward
+//class declaration returns true
+//Or is there a bug in the CurContext setting?
+//See xFAIL test
+      PP.Diag(Tok.getLocation(), diag::err_pragma_fc_pp_scope);
+      return;
+    }
+    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 == PragmaFloatControlDecl::FC_Pop) ?
+              Sema::PSK_Pop : Sema::PSK_Push;
+  } else {
+    if (Tok.is(tok::r_paren))
+      ; // Selecting Precise or Except
+    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 == PragmaFloatControlDecl::FC_Precise )
+          Kind = PragmaFloatControlDecl::FC_NoPrecise ;
+        if (Kind == PragmaFloatControlDecl::FC_Except )
+          Kind = PragmaFloatControlDecl::FC_NoExcept ;
+      } else if (PushOnOff == "push") {
+        if (!Actions.CurContext->isTranslationUnit()) {
+          PP.Diag(Tok.getLocation(), diag::err_pragma_fc_pp_scope);
+          return;
+        }
+        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") {
+          if (!Actions.CurContext->isTranslationUnit()) {
+            PP.Diag(Tok.getLocation(), diag::err_pragma_fc_pp_scope);
+            return;
+          }
+          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();
+
+  // Note: there is no accomodation for PP callback for this pragma.
+
+  // Enter the annotation.
+  Token AnnotTok;
+  AnnotTok.startToken();
+  AnnotTok.setKind(tok::annot_pragma_float_control);
+  AnnotTok.setLocation(FloatControlLoc);
+  AnnotTok.setAnnotationEndLoc(EndLoc);
+  AnnotTok.setAnnotationValue(reinterpret_cast<void *>(
+      static_cast<uintptr_t>((Action << 16) | (Kind & 0xFFFF))));
+      //static_cast<uintptr_t>((Action & 0xFFFF))));
+  PP.EnterToken(AnnotTok, /*IsReinject=*/false);
+}
+
 /// Handle the Microsoft \#pragma detect_mismatch extension.
 ///
 /// The syntax is:
Index: clang/lib/CodeGen/CodeGenFunction.h
===================================================================
--- clang/lib/CodeGen/CodeGenFunction.h
+++ clang/lib/CodeGen/CodeGenFunction.h
@@ -4417,6 +4417,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
@@ -108,7 +108,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) {
@@ -123,7 +123,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) {
@@ -140,14 +140,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);
   }
 }
 
@@ -1245,6 +1245,9 @@
   FunctionArgList Args;
   QualType ResTy = BuildFunctionArgList(GD, Args);
 
+  if (FD->usesFPIntrin())
+    Builder.setIsFPConstrained(true);
+
   // Check if we should generate debug info for this function.
   if (FD->hasAttr<NoDebugAttr>())
     DebugInfo = nullptr; // disable debug info indefinitely for this function
Index: clang/lib/CodeGen/CGExprScalar.cpp
===================================================================
--- clang/lib/CodeGen/CGExprScalar.cpp
+++ clang/lib/CodeGen/CGExprScalar.cpp
@@ -423,7 +423,28 @@
 
   Value *Visit(Expr *E) {
     ApplyDebugLocation DL(CGF, E);
-    return StmtVisitor<ScalarExprEmitter, Value*>::Visit(E);
+    if (BinaryOperator * BinOp = dyn_cast<BinaryOperator>(E)) {
+      //  Preserve the old values
+      llvm::IRBuilder<>::FastMathFlagGuard FMFG(Builder);
+
+      auto RoundingBehavior = Builder.getDefaultConstrainedRounding();
+      auto NewExceptionBehavior = ToConstrainedExceptMD(
+                                    BinOp->getFPFeatures().fpExcept());
+      Builder.setDefaultConstrainedExcept(NewExceptionBehavior);
+      if (BinOp->getFPFeatures().fpPreciseEnabled()) {
+        Builder.clearFastMathFlags();
+        assert(BinOp->getFPFeatures().allowFPContractWithinStatement() &&
+               "Expected FPFeatures to allow constract within statement");
+      }
+      assert(Builder.getIsFPConstrained() ||
+          (NewExceptionBehavior == llvm::fp::ebIgnore &&
+          RoundingBehavior == llvm::fp::rmToNearest) &&
+          "FPConstrained should be enabled on entire function");
+
+      return StmtVisitor<ScalarExprEmitter, Value*>::Visit(E);
+    } else {
+      return StmtVisitor<ScalarExprEmitter, Value*>::Visit(E);
+    }
   }
 
   Value *VisitStmt(Stmt *S) {
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -540,6 +540,23 @@
   PragmaStack<StringLiteral *> ConstSegStack;
   PragmaStack<StringLiteral *> CodeSegStack;
 
+  typedef struct {
+    bool HasPragma;
+    bool IsPrecise;
+    bool IsExcept;
+  } FpPragmaStateType;
+  unsigned getInt(FpPragmaStateType Value) const {
+    return (Value.HasPragma<<2) + (Value.IsPrecise << 1) + Value.IsExcept;
+  }
+  FpPragmaStateType fromInt(unsigned Value) const {
+    FpPragmaStateType Result;
+    Result.HasPragma = Value & 0B100;
+    Result.IsPrecise = Value & 0B10;
+    Result.IsExcept = Value & 0B01;
+    return Result;
+  }
+  PragmaStack<FpPragmaStateType> 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 {
@@ -9235,6 +9252,11 @@
   void ActOnPragmaDetectMismatch(SourceLocation Loc, StringRef Name,
                                  StringRef Value);
 
+  /// ActOnPragmaFloatControl - Call on well-formed \#pragma float_control
+  void ActOnPragmaFloatControl(SourceLocation Loc,
+                               PragmaMsStackAction Action,
+                               PragmaFloatControlDecl::FloatControlKind 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;
@@ -719,6 +720,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/LangOptions.h
===================================================================
--- clang/include/clang/Basic/LangOptions.h
+++ clang/include/clang/Basic/LangOptions.h
@@ -357,18 +357,24 @@
 class FPOptions {
 public:
   FPOptions() : fp_contract(LangOptions::FPC_Off),
-                fenv_access(LangOptions::FEA_Off) {}
+                fenv_access(LangOptions::FEA_Off),
+                fp_except(LangOptions::FPE_Ignore),
+                fp_precise(false) {}
 
   // Used for serializing.
   explicit FPOptions(unsigned I)
       : fp_contract(static_cast<LangOptions::FPContractModeKind>(I & 3)),
-        fenv_access(static_cast<LangOptions::FEnvAccessModeKind>((I >> 2) & 1))
-        {}
+        fenv_access(static_cast<LangOptions::FEnvAccessModeKind>((I >> 2) & 1)),
+        fp_except(static_cast<LangOptions::FPExceptionModeKind>((I >> 3) & 3)),
+        fp_precise(static_cast<bool>((I >> 5) & 1)) {}
 
   explicit FPOptions(const LangOptions &LangOpts)
       : fp_contract(LangOpts.getDefaultFPContractMode()),
-        fenv_access(LangOptions::FEA_Off) {}
+        fenv_access(LangOptions::FEA_Off),
   // FIXME: Use getDefaultFEnvAccessMode() when available.
+        fp_except(LangOpts.getFPExceptionMode()),
+        fp_precise(!LangOpts.OpenCL && !LangOpts.FastMath &&
+                 LangOpts.getDefaultFPContractMode() == LangOptions::FPC_On) {}
 
   bool allowFPContractWithinStatement() const {
     return fp_contract == LangOptions::FPC_On;
@@ -396,16 +402,37 @@
     fenv_access = LangOptions::FEA_On;
   }
 
+  void setFPPreciseEnabled(bool Value) {
+    fp_precise = Value;
+
+    /* Precise mode implies fp_contract and disables ffast-math */
+    if (fp_precise)
+      setAllowFPContractWithinStatement();
+    else
+      setDisallowFPContract();
+  }
+  bool fpPreciseEnabled() { return fp_precise; }
+
+  void setFPExcept(LangOptions::FPExceptionModeKind Value) { fp_except = Value;}
+  LangOptions::FPExceptionModeKind fpExcept() {
+    return static_cast<LangOptions::FPExceptionModeKind>(fp_except);
+  }
+
   void setDisallowFEnvAccess() { fenv_access = LangOptions::FEA_Off; }
 
   /// Used to serialize this.
-  unsigned getInt() const { return fp_contract | (fenv_access << 2); }
+  unsigned getInt() const {
+    return fp_contract | (fenv_access << 2) |
+           (fp_except << 3) | (fp_precise << 5);
+  };
 
 private:
   /// Adjust BinaryOperator::FPFeatures to match the total bit-field size
   /// of these two.
   unsigned fp_contract : 2;
   unsigned fenv_access : 1;
+  unsigned fp_except : 2;
+  unsigned fp_precise : 1;
 };
 
 /// Describes the kind of translation unit being processed.
Index: clang/include/clang/Basic/DiagnosticParseKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticParseKinds.td
+++ clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1101,6 +1101,11 @@
 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; it requires one or two comma-separated "
+  "arguments">;
+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"
@@ -1271,6 +1276,11 @@
 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_fc_scope : Error<
+  "'#pragma float_control' can only appear at file scope or at the start of a "
+  "compound statement">;
+def err_pragma_fc_pp_scope : Error<
+  "'#pragma float_control push/pop' can only appear at file scope">;
 
 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
@@ -530,7 +530,7 @@
 
     /// This is only meaningful for operations on floating point
     /// types and 0 otherwise.
-    unsigned FPFeatures : 3;
+    unsigned FPFeatures : 6;
 
     SourceLocation OpLoc;
   };
@@ -601,7 +601,7 @@
     unsigned OperatorKind : 6;
 
     // Only meaningful for floating point types.
-    unsigned FPFeatures : 3;
+    unsigned FPFeatures : 6;
   };
 
   class CXXRewrittenBinaryOperatorBitfields {
Index: clang/include/clang/AST/Decl.h
===================================================================
--- clang/include/clang/AST/Decl.h
+++ clang/include/clang/AST/Decl.h
@@ -176,6 +176,22 @@
   static bool classofKind(Kind K) { return K == PragmaDetectMismatch; }
 };
 
+/// Support for `#pragma float_control` line.
+class PragmaFloatControlDecl {
+
+public:
+  enum FloatControlKind {
+    FC_Unknown,
+    FC_Precise,
+    FC_NoPrecise,
+    FC_Except,
+    FC_NoExcept,
+    FC_Push,
+    FC_Pop
+  };
+
+};
+
 /// Declaration context for names declared as extern "C" in C++. This
 /// is neither the semantic nor lexical context for such declarations, but is
 /// used to check for conflicts with other extern "C" declarations. Example:
Index: clang/docs/LanguageExtensions.rst
===================================================================
--- clang/docs/LanguageExtensions.rst
+++ clang/docs/LanguageExtensions.rst
@@ -3028,6 +3028,32 @@
 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 floating-point precision 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
+multiple 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``.
+
+.. 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