[PATCH] Flag to enable IEEE-754 friendly FP optimizations

Sergey Dmitrouk via llvm-commits llvm-commits at lists.llvm.org
Thu Oct 1 06:14:31 PDT 2015


Hello Hal,

On Tue, Sep 29, 2015 at 03:18:46PM -0700, Hal Finkel wrote:
> > > Did this stuff ever land?
> >
> > No, it was relatively close, but an issue with reordering became
> > visible and changed the disposition.
>
> Is there a reason that we should not land the constant-folding patches
> first, and then move on to the problems of preventing reordering?

Not really, I'll be happy to land the constant-folding part.  Attached
rebased and updated version, assuming we don't need Phabricator for
this round.  Main changes are use of FastMathFlags in arguments, comment and
IRBuilder updates, also removed accidental changes for Mul (not FMul).

> > That reordering issue is quite hard to address, the latest update I
> > sent is here:
> >
> > http://lists.llvm.org/pipermail/llvm-commits/Week-of-Mon-20150914/299491.html
>
> Looks like you're waiting for a reply from me on this; I'll do that...

Thanks, I actually do have new instructions now (FADD_W_CHAIN, etc.),
had to add them to work around conflicts.  TableGen definitions use node
types for selection and using the same node more than once (to provide
versions with and without side effects) causes conflicts.

Regards,
Sergey
-------------- next part --------------
>From db76e97d1557978f407821e0c5f3d43522e584db Mon Sep 17 00:00:00 2001
From: Sergey Dmitrouk <sdmitrouk at accesssoftek.com>
Date: Thu, 13 Nov 2014 16:54:23 +0200
Subject: [PATCH 01/13] Add flags and command-line switches for FPEnv

* Two flags for TargetOptions.
* Three command-line switches to enable flags separately or at the same
  time.
---
 include/llvm/CodeGen/CommandFlags.h | 19 +++++++++++++++++++
 include/llvm/Target/TargetOptions.h | 17 ++++++++++++++++-
 2 files changed, 35 insertions(+), 1 deletion(-)

diff --git a/include/llvm/CodeGen/CommandFlags.h b/include/llvm/CodeGen/CommandFlags.h
index c97d186..a62018b 100644
--- a/include/llvm/CodeGen/CommandFlags.h
+++ b/include/llvm/CodeGen/CommandFlags.h
@@ -130,6 +130,21 @@ EnableHonorSignDependentRoundingFPMath("enable-sign-dependent-rounding-fp-math",
       cl::desc("Force codegen to assume rounding mode can change dynamically"),
       cl::init(false));
 
+cl::opt<bool>
+EnableFPEnvAccessFPMath("enable-fpenv-access-fp-math",
+    cl::desc("Disable FP math optimizations that affect FP environment access"),
+    cl::init(false));
+
+cl::opt<bool>
+EnableExceptAccessFPMath("enable-except-access-fp-math",
+            cl::desc("Disable FP math optimizations that affect FP exceptions"),
+            cl::init(false));
+
+cl::opt<bool>
+EnableRoundAccessFPMath("enable-round-access-fp-math",
+              cl::desc("Disable FP math optimizations that affect FP rounding"),
+              cl::init(false));
+
 cl::opt<llvm::FloatABI::ABIType>
 FloatABIForCalls("float-abi",
                  cl::desc("Choose float ABI type"),
@@ -259,6 +274,10 @@ static inline TargetOptions InitTargetOptionsFromCodeGenFlags() {
   Options.NoNaNsFPMath = EnableNoNaNsFPMath;
   Options.HonorSignDependentRoundingFPMathOption =
       EnableHonorSignDependentRoundingFPMath;
+  Options.AllowFPExceptAccess = EnableExceptAccessFPMath |
+      EnableFPEnvAccessFPMath;
+  Options.AllowFPRoundAccess = EnableRoundAccessFPMath |
+      EnableFPEnvAccessFPMath;
   if (FloatABIForCalls != FloatABI::Default)
     Options.FloatABIType = FloatABIForCalls;
   Options.NoZerosInBSS = DontPlaceZerosInBSS;
diff --git a/include/llvm/Target/TargetOptions.h b/include/llvm/Target/TargetOptions.h
index bd696f3..3e233f8 100644
--- a/include/llvm/Target/TargetOptions.h
+++ b/include/llvm/Target/TargetOptions.h
@@ -63,7 +63,8 @@ namespace llvm {
     TargetOptions()
         : PrintMachineCode(false),
           LessPreciseFPMADOption(false), UnsafeFPMath(false),
-          NoInfsFPMath(false), NoNaNsFPMath(false),
+          NoInfsFPMath(false), NoNaNsFPMath(false), AllowFPExceptAccess(false),
+          AllowFPRoundAccess(false),
           HonorSignDependentRoundingFPMathOption(false),
           NoZerosInBSS(false),
           GuaranteedTailCallOpt(false),
@@ -114,6 +115,20 @@ namespace llvm {
     /// assume the FP arithmetic arguments and results are never NaNs.
     unsigned NoNaNsFPMath : 1;
 
+    /// AllowFPExceptAccess - This flag is enabled when the
+    /// -enable-except-access-fp-math or -enable-fpenv-access-fp-math flag is
+    /// specified on the command line.  When this flag is off (the default), the
+    /// code generator is allowed to assume the exceptions part of
+    /// floating-point environment is not accessed by the program.
+    unsigned AllowFPExceptAccess : 1;
+
+    /// AllowFPRoundAccess - This flag is enabled when the
+    /// -enable-round-access-fp-math or -enable-fpenv-access-fp-math flag is
+    /// specified on the command line. When this flag is off (the default), the
+    /// code generator is allowed to assume the rounding part of floating-point
+    /// environment is not accessed by the program.
+    unsigned AllowFPRoundAccess : 1;
+
     /// HonorSignDependentRoundingFPMath - This returns true when the
     /// -enable-sign-dependent-rounding-fp-math is specified.  If this returns
     /// false (the default), the code generator is allowed to assume that the
-- 
2.3.5

-------------- next part --------------
>From 51106a6ff97518686a43c2ba29ce81e0de2b490e Mon Sep 17 00:00:00 2001
From: Sergey Dmitrouk <sdmitrouk at accesssoftek.com>
Date: Thu, 13 Nov 2014 16:06:06 +0200
Subject: [PATCH 02/13] Add FPEnv access flags to fast-math flags

Will be used to disable optimizations related to floating-point
operations which would affect runtime semantics (e.g. raising
floating-point exceptions).
---
 docs/LangRef.rst                     | 10 +++++++++-
 include/llvm/IR/Instruction.h        | 16 ++++++++++++++++
 include/llvm/IR/Operator.h           | 30 +++++++++++++++++++++++++++++-
 lib/AsmParser/LLLexer.cpp            |  2 ++
 lib/AsmParser/LLParser.h             | 12 +++++++-----
 lib/AsmParser/LLToken.h              |  2 ++
 lib/Bitcode/Reader/BitcodeReader.cpp |  4 ++++
 lib/Bitcode/Writer/BitcodeWriter.cpp |  4 ++++
 lib/IR/AsmWriter.cpp                 |  4 ++++
 lib/IR/Instruction.cpp               | 28 ++++++++++++++++++++++++++++
 test/Assembler/fast-math-flags.ll    |  8 ++++++++
 11 files changed, 113 insertions(+), 7 deletions(-)

diff --git a/docs/LangRef.rst b/docs/LangRef.rst
index bc4df05..d96c0d8 100644
--- a/docs/LangRef.rst
+++ b/docs/LangRef.rst
@@ -1916,10 +1916,18 @@ be set to enable otherwise unsafe floating point operations
    Allow Reciprocal - Allow optimizations to use the reciprocal of an
    argument rather than perform division.
 
+``kexc``
+   Keep Exceptions - Forbid optimizations to affect floating-point exceptions
+   that can be thrown at runtime.
+
+``kround``
+   Keep Rounding - Optimizations may assume round-to-nearest (ties even)
+   rounding mode unless this flag is specified.
+
 ``fast``
    Fast - Allow algebraically equivalent transformations that may
    dramatically change results in floating point (e.g. reassociate). This
-   flag implies all the others.
+   flag implies all the others except for the last two.
 
 .. _uselistorder:
 
diff --git a/include/llvm/IR/Instruction.h b/include/llvm/IR/Instruction.h
index 000e854..f1e258a 100644
--- a/include/llvm/IR/Instruction.h
+++ b/include/llvm/IR/Instruction.h
@@ -256,6 +256,16 @@ public:
   /// this flag.
   void setHasAllowReciprocal(bool B);
 
+  /// Set or clear the keep-exceptions flag on this instruction, which must be
+  /// an operator which supports this flag. See LangRef.html for the meaning of
+  /// this flag.
+  void setHasKeepExceptions(bool B);
+
+  /// Set or clear the keep-rounding flag on this instruction, which must be an
+  /// operator which supports this flag. See LangRef.html for the meaning of
+  /// this flag.
+  void setHasKeepRounding(bool B);
+
   /// Convenience function for setting multiple fast-math flags on this
   /// instruction, which must be an operator which supports these flags. See
   /// LangRef.html for the meaning of these flags.
@@ -281,6 +291,12 @@ public:
   /// Determine whether the allow-reciprocal flag is set.
   bool hasAllowReciprocal() const;
 
+  /// Determine whether the keep-exceptions flag is set.
+  bool hasKeepExceptions() const;
+
+  /// Determine whether the keep-rounding flag is set.
+  bool hasKeepRounding() const;
+
   /// Convenience function for getting all the fast-math flags, which must be an
   /// operator which supports these flags. See LangRef.html for the meaning of
   /// these flags.
diff --git a/include/llvm/IR/Operator.h b/include/llvm/IR/Operator.h
index 372b254..0f4d07c 100644
--- a/include/llvm/IR/Operator.h
+++ b/include/llvm/IR/Operator.h
@@ -173,7 +173,9 @@ public:
     NoNaNs          = (1 << 1),
     NoInfs          = (1 << 2),
     NoSignedZeros   = (1 << 3),
-    AllowReciprocal = (1 << 4)
+    AllowReciprocal = (1 << 4),
+    KeepExceptions  = (1 << 5),
+    KeepRounding    = (1 << 6)
   };
 
   FastMathFlags() : Flags(0)
@@ -191,6 +193,8 @@ public:
   bool noSignedZeros() const   { return 0 != (Flags & NoSignedZeros); }
   bool allowReciprocal() const { return 0 != (Flags & AllowReciprocal); }
   bool unsafeAlgebra() const   { return 0 != (Flags & UnsafeAlgebra); }
+  bool keepExceptions() const  { return 0 != (Flags & KeepExceptions); }
+  bool keepRounding()const     { return 0 != (Flags & KeepRounding); }
 
   /// Flag setters
   void setNoNaNs()          { Flags |= NoNaNs; }
@@ -204,6 +208,8 @@ public:
     setNoSignedZeros();
     setAllowReciprocal();
   }
+  void setKeepExceptions() { Flags |= KeepExceptions; }
+  void setKeepRounding()   { Flags |= KeepRounding; }
 
   void operator&=(const FastMathFlags &OtherFlags) {
     Flags &= OtherFlags.Flags;
@@ -250,6 +256,16 @@ private:
       (SubclassOptionalData & ~FastMathFlags::AllowReciprocal) |
       (B * FastMathFlags::AllowReciprocal);
   }
+  void setHasKeepExceptions(bool B) {
+    SubclassOptionalData =
+      (SubclassOptionalData & ~FastMathFlags::KeepExceptions) |
+      (B * FastMathFlags::KeepExceptions);
+  }
+  void setHasKeepRounding(bool B) {
+    SubclassOptionalData =
+      (SubclassOptionalData & ~FastMathFlags::KeepRounding) |
+      (B * FastMathFlags::KeepRounding);
+  }
 
   /// Convenience function for setting multiple fast-math flags.
   /// FMF is a mask of the bits to set.
@@ -294,6 +310,18 @@ public:
     return (SubclassOptionalData & FastMathFlags::AllowReciprocal) != 0;
   }
 
+  /// Test whether this operation should not be optimized if its runtime
+  /// semantics regarding floating-point exceptions can't be preserved.
+  bool hasKeepExceptions() const {
+    return (SubclassOptionalData & FastMathFlags::KeepExceptions) != 0;
+  }
+
+  /// Test whether this operation should not be optimized if its runtime
+  /// semantics regarding floating-point rounding modes can't be preserved.
+  bool hasKeepRounding() const {
+    return (SubclassOptionalData & FastMathFlags::KeepRounding) != 0;
+  }
+
   /// Convenience function for getting all the fast-math flags
   FastMathFlags getFastMathFlags() const {
     return FastMathFlags(SubclassOptionalData);
diff --git a/lib/AsmParser/LLLexer.cpp b/lib/AsmParser/LLLexer.cpp
index 2eb5f0b..a46884b 100644
--- a/lib/AsmParser/LLLexer.cpp
+++ b/lib/AsmParser/LLLexer.cpp
@@ -546,6 +546,8 @@ lltok::Kind LLLexer::LexIdentifier() {
   KEYWORD(ninf);
   KEYWORD(nsz);
   KEYWORD(arcp);
+  KEYWORD(kexc);
+  KEYWORD(kround);
   KEYWORD(fast);
   KEYWORD(nuw);
   KEYWORD(nsw);
diff --git a/lib/AsmParser/LLParser.h b/lib/AsmParser/LLParser.h
index 24ff8e7..d5f8e57 100644
--- a/lib/AsmParser/LLParser.h
+++ b/lib/AsmParser/LLParser.h
@@ -193,11 +193,13 @@ namespace llvm {
       FastMathFlags FMF;
       while (true)
         switch (Lex.getKind()) {
-        case lltok::kw_fast: FMF.setUnsafeAlgebra();   Lex.Lex(); continue;
-        case lltok::kw_nnan: FMF.setNoNaNs();          Lex.Lex(); continue;
-        case lltok::kw_ninf: FMF.setNoInfs();          Lex.Lex(); continue;
-        case lltok::kw_nsz:  FMF.setNoSignedZeros();   Lex.Lex(); continue;
-        case lltok::kw_arcp: FMF.setAllowReciprocal(); Lex.Lex(); continue;
+        case lltok::kw_fast:   FMF.setUnsafeAlgebra();   Lex.Lex(); continue;
+        case lltok::kw_nnan:   FMF.setNoNaNs();          Lex.Lex(); continue;
+        case lltok::kw_ninf:   FMF.setNoInfs();          Lex.Lex(); continue;
+        case lltok::kw_nsz:    FMF.setNoSignedZeros();   Lex.Lex(); continue;
+        case lltok::kw_arcp:   FMF.setAllowReciprocal(); Lex.Lex(); continue;
+        case lltok::kw_kexc:   FMF.setKeepExceptions();  Lex.Lex(); continue;
+        case lltok::kw_kround: FMF.setKeepRounding();    Lex.Lex(); continue;
         default: return FMF;
         }
       return FMF;
diff --git a/lib/AsmParser/LLToken.h b/lib/AsmParser/LLToken.h
index 06b5f9b..f1f95e2 100644
--- a/lib/AsmParser/LLToken.h
+++ b/lib/AsmParser/LLToken.h
@@ -67,6 +67,8 @@ namespace lltok {
     kw_ninf,
     kw_nsz,
     kw_arcp,
+    kw_kexc,
+    kw_kround,
     kw_fast,
     kw_nuw,
     kw_nsw,
diff --git a/lib/Bitcode/Reader/BitcodeReader.cpp b/lib/Bitcode/Reader/BitcodeReader.cpp
index 165ab6e..e004f34 100644
--- a/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -728,6 +728,10 @@ static FastMathFlags getDecodedFastMathFlags(unsigned Val) {
     FMF.setNoSignedZeros();
   if (0 != (Val & FastMathFlags::AllowReciprocal))
     FMF.setAllowReciprocal();
+  if (0 != (Val & FastMathFlags::KeepExceptions))
+    FMF.setKeepExceptions();
+  if (0 != (Val & FastMathFlags::KeepRounding))
+    FMF.setKeepRounding();
   return FMF;
 }
 
diff --git a/lib/Bitcode/Writer/BitcodeWriter.cpp b/lib/Bitcode/Writer/BitcodeWriter.cpp
index f645572..11de8b7 100644
--- a/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -798,6 +798,10 @@ static uint64_t GetOptimizationFlags(const Value *V) {
       Flags |= FastMathFlags::NoSignedZeros;
     if (FPMO->hasAllowReciprocal())
       Flags |= FastMathFlags::AllowReciprocal;
+    if (FPMO->hasKeepExceptions())
+      Flags |= FastMathFlags::KeepExceptions;
+    if (FPMO->hasKeepRounding())
+      Flags |= FastMathFlags::KeepRounding;
   }
 
   return Flags;
diff --git a/lib/IR/AsmWriter.cpp b/lib/IR/AsmWriter.cpp
index b4d7a66..693a353 100644
--- a/lib/IR/AsmWriter.cpp
+++ b/lib/IR/AsmWriter.cpp
@@ -1067,6 +1067,10 @@ static void WriteOptimizationInfo(raw_ostream &Out, const User *U) {
         Out << " nsz";
       if (FPO->hasAllowReciprocal())
         Out << " arcp";
+      if (FPO->hasKeepExceptions())
+        Out << " kexc";
+      if (FPO->hasKeepRounding())
+        Out << " kround";
     }
   }
 
diff --git a/lib/IR/Instruction.cpp b/lib/IR/Instruction.cpp
index 91ac83c..9808a2e 100644
--- a/lib/IR/Instruction.cpp
+++ b/lib/IR/Instruction.cpp
@@ -129,6 +129,22 @@ void Instruction::setHasAllowReciprocal(bool B) {
   cast<FPMathOperator>(this)->setHasAllowReciprocal(B);
 }
 
+/// Set or clear the keep-exceptions flag on this instruction, which must be an
+/// operator which supports this flag. See LangRef.html for the meaning of this
+/// flag.
+void Instruction::setHasKeepExceptions(bool B) {
+  assert(isa<FPMathOperator>(this) && "setting fast-math flag on invalid op");
+  cast<FPMathOperator>(this)->setHasKeepExceptions(B);
+}
+
+/// Set or clear the keep-rounding flag on this instruction, which must be an
+/// operator which supports this flag. See LangRef.html for the meaning of this
+/// flag.
+void Instruction::setHasKeepRounding(bool B) {
+  assert(isa<FPMathOperator>(this) && "setting fast-math flag on invalid op");
+  cast<FPMathOperator>(this)->setHasKeepRounding(B);
+}
+
 /// Convenience function for setting all the fast-math flags on this
 /// instruction, which must be an operator which supports these flags. See
 /// LangRef.html for the meaning of these flats.
@@ -172,6 +188,18 @@ bool Instruction::hasAllowReciprocal() const {
   return cast<FPMathOperator>(this)->hasAllowReciprocal();
 }
 
+/// Determine whether the keep-exceptions flag is set.
+bool Instruction::hasKeepExceptions() const {
+  assert(isa<FPMathOperator>(this) && "getting fast-math flag on invalid op");
+  return cast<FPMathOperator>(this)->hasKeepExceptions();
+}
+
+/// Determine whether the keep-rounding flag is set.
+bool Instruction::hasKeepRounding() const {
+  assert(isa<FPMathOperator>(this) && "getting fast-math flag on invalid op");
+  return cast<FPMathOperator>(this)->hasKeepRounding();
+}
+
 /// Convenience function for getting all the fast-math flags, which must be an
 /// operator which supports these flags. See LangRef.html for the meaning of
 /// these flags.
diff --git a/test/Assembler/fast-math-flags.ll b/test/Assembler/fast-math-flags.ll
index f0d3ecc..4958c40 100644
--- a/test/Assembler/fast-math-flags.ll
+++ b/test/Assembler/fast-math-flags.ll
@@ -138,6 +138,14 @@ entry:
   %e = frem nnan nsz float %x, %y
 ; CHECK:  %e_vec = frem nnan <3 x float> %vec, %vec
   %e_vec = frem nnan <3 x float> %vec, %vec
+; CHECK:  %f = fmul fast float %x, %y
+  %f = fmul kexc kround fast float %x, %y
+; CHECK:  %f_vec = frem fast <3 x float> %vec, %vec
+  %f_vec = frem kexc kround fast <3 x float> %vec, %vec
+; CHECK:  %g = fmul kexc kround float %x, %y
+  %g = fmul kexc kround float %x, %y
+; CHECK:  %g_vec = frem kexc kround <3 x float> %vec, %vec
+  %g_vec = frem kexc kround <3 x float> %vec, %vec
 ; CHECK:  ret  float %e
   ret  float %e
 }
-- 
2.3.5

-------------- next part --------------
>From 7ca9e405af820e3a9dfbcdcda86e5d194dbc7eb0 Mon Sep 17 00:00:00 2001
From: Sergey Dmitrouk <sdmitrouk at accesssoftek.com>
Date: Thu, 13 Nov 2014 20:40:40 +0200
Subject: [PATCH 03/13] Consider FPEnv access in SelectionDAG

* Disable optimizations that erase floating-point exceptions at runtime
  if application is allowed to access exception mask.

* Disable optimizations if application is allowed to access rounding and
  result was rounded.
---
 include/llvm/CodeGen/SelectionDAG.h              |   5 +
 include/llvm/CodeGen/SelectionDAGNodes.h         |  19 ++-
 lib/CodeGen/SelectionDAG/SelectionDAG.cpp        |  36 ++++--
 lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp |   2 +
 test/CodeGen/Generic/selectiondag-fenv.ll        | 140 +++++++++++++++++++++++
 5 files changed, 188 insertions(+), 14 deletions(-)
 create mode 100644 test/CodeGen/Generic/selectiondag-fenv.ll

diff --git a/include/llvm/CodeGen/SelectionDAG.h b/include/llvm/CodeGen/SelectionDAG.h
index f23f010..7ca990f 100644
--- a/include/llvm/CodeGen/SelectionDAG.h
+++ b/include/llvm/CodeGen/SelectionDAG.h
@@ -1294,6 +1294,11 @@ private:
   SDNode *FindNodeOrInsertPos(const FoldingSetNodeID &ID, DebugLoc DL,
                               void *&InsertPos);
 
+  /// Checks whether it's safe to replace a floating-point operation with its
+  /// result.  UnsafeOps is bit mask of unacceptable exceptions.
+  bool isSafeToOptimizeFPOp(const SDNodeFlags *Flags, APFloat::opStatus s,
+                            APFloat::opStatus UnsafeOps) const;
+
   /// List of non-single value types.
   FoldingSet<SDVTListNode> VTListMap;
 
diff --git a/include/llvm/CodeGen/SelectionDAGNodes.h b/include/llvm/CodeGen/SelectionDAGNodes.h
index 12f9c8c..53e0c72 100644
--- a/include/llvm/CodeGen/SelectionDAGNodes.h
+++ b/include/llvm/CodeGen/SelectionDAGNodes.h
@@ -333,7 +333,9 @@ private:
   bool NoInfs : 1;
   bool NoSignedZeros : 1;
   bool AllowReciprocal : 1;
-  
+  bool KeepExceptions : 1;
+  bool KeepRounding : 1;
+
 public:
   /// Default constructor turns off all optimization flags.
   SDNodeFlags() {
@@ -345,8 +347,10 @@ public:
     NoInfs = false;
     NoSignedZeros = false;
     AllowReciprocal = false;
+    KeepExceptions = false;
+    KeepRounding = false;
   }
-  
+
   // These are mutators for each flag.
   void setNoUnsignedWrap(bool b) { NoUnsignedWrap = b; }
   void setNoSignedWrap(bool b) { NoSignedWrap = b; }
@@ -356,7 +360,9 @@ public:
   void setNoInfs(bool b) { NoInfs = b; }
   void setNoSignedZeros(bool b) { NoSignedZeros = b; }
   void setAllowReciprocal(bool b) { AllowReciprocal = b; }
-  
+  void setKeepExceptions(bool b) { KeepExceptions = b; }
+  void setKeepRounding(bool b) { KeepRounding = b; }
+
   // These are accessors for each flag.
   bool hasNoUnsignedWrap() const { return NoUnsignedWrap; }
   bool hasNoSignedWrap() const { return NoSignedWrap; }
@@ -366,13 +372,16 @@ public:
   bool hasNoInfs() const { return NoInfs; }
   bool hasNoSignedZeros() const { return NoSignedZeros; }
   bool hasAllowReciprocal() const { return AllowReciprocal; }
-  
+  bool hasKeepExceptions() const { return KeepExceptions; }
+  bool hasKeepRounding() const { return KeepRounding; }
+
   /// Return a raw encoding of the flags.
   /// This function should only be used to add data to the NodeID value.
   unsigned getRawFlags() const {
     return (NoUnsignedWrap << 0) | (NoSignedWrap << 1) | (Exact << 2) |
     (UnsafeAlgebra << 3) | (NoNaNs << 4) | (NoInfs << 5) |
-    (NoSignedZeros << 6) | (AllowReciprocal << 7);
+    (NoSignedZeros << 6) | (AllowReciprocal << 7) |
+    (KeepExceptions << 8) | (KeepRounding << 9);
   }
 };
 
diff --git a/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
index a968e96..eab0a24 100644
--- a/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -3703,7 +3703,6 @@ SDValue SelectionDAG::getNode(unsigned Opcode, SDLoc DL, EVT VT, SDValue N1,
     return SV;
 
   // Constant fold FP operations.
-  bool HasFPExceptions = TLI->hasFloatingPointExceptions();
   ConstantFPSDNode *N1CFP = dyn_cast<ConstantFPSDNode>(N1);
   ConstantFPSDNode *N2CFP = dyn_cast<ConstantFPSDNode>(N2);
   if (N1CFP) {
@@ -3717,30 +3716,32 @@ SDValue SelectionDAG::getNode(unsigned Opcode, SDLoc DL, EVT VT, SDValue N1,
       switch (Opcode) {
       case ISD::FADD:
         s = V1.add(V2, APFloat::rmNearestTiesToEven);
-        if (!HasFPExceptions || s != APFloat::opInvalidOp)
+        if (isSafeToOptimizeFPOp(Flags, s, APFloat::opInvalidOp))
           return getConstantFP(V1, DL, VT);
         break;
       case ISD::FSUB:
         s = V1.subtract(V2, APFloat::rmNearestTiesToEven);
-        if (!HasFPExceptions || s!=APFloat::opInvalidOp)
+        if (isSafeToOptimizeFPOp(Flags, s, APFloat::opInvalidOp))
           return getConstantFP(V1, DL, VT);
         break;
       case ISD::FMUL:
         s = V1.multiply(V2, APFloat::rmNearestTiesToEven);
-        if (!HasFPExceptions || s!=APFloat::opInvalidOp)
+        if (isSafeToOptimizeFPOp(Flags, s, APFloat::opInvalidOp))
           return getConstantFP(V1, DL, VT);
         break;
       case ISD::FDIV:
         s = V1.divide(V2, APFloat::rmNearestTiesToEven);
-        if (!HasFPExceptions || (s!=APFloat::opInvalidOp &&
-                                 s!=APFloat::opDivByZero)) {
+        if (isSafeToOptimizeFPOp(Flags, s,
+                                 (APFloat::opStatus)(APFloat::opInvalidOp |
+                                                     APFloat::opDivByZero))) {
           return getConstantFP(V1, DL, VT);
         }
         break;
-      case ISD::FREM :
+      case ISD::FREM:
         s = V1.mod(V2);
-        if (!HasFPExceptions || (s!=APFloat::opInvalidOp &&
-                                 s!=APFloat::opDivByZero)) {
+        if (isSafeToOptimizeFPOp(Flags, s,
+                                 (APFloat::opStatus)(APFloat::opInvalidOp |
+                                                     APFloat::opDivByZero))) {
           return getConstantFP(V1, DL, VT);
         }
         break;
@@ -3860,6 +3861,23 @@ SDValue SelectionDAG::getNode(unsigned Opcode, SDLoc DL, EVT VT, SDValue N1,
   return SDValue(N, 0);
 }
 
+bool SelectionDAG::isSafeToOptimizeFPOp(const SDNodeFlags *Flags,
+                                        APFloat::opStatus s,
+                                        APFloat::opStatus UnsafeOps) const {
+  bool hasKeepRounding = Flags ? Flags->hasKeepRounding() : false;
+  bool hasKeepExceptions = Flags ? Flags->hasKeepExceptions() : false;
+
+  if ((getTarget().Options.AllowFPRoundAccess || hasKeepRounding) &&
+      (s & APFloat::opInexact))
+    return false;
+
+  if (!TLI->hasFloatingPointExceptions())
+    return true;
+  if (getTarget().Options.AllowFPExceptAccess || hasKeepExceptions)
+    return s == APFloat::opOK;
+  return (s & UnsafeOps) == APFloat::opOK;
+}
+
 SDValue SelectionDAG::getNode(unsigned Opcode, SDLoc DL, EVT VT,
                               SDValue N1, SDValue N2, SDValue N3) {
   // Perform various simplifications.
diff --git a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index 8b0ef1b..e2265bf 100644
--- a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -2254,6 +2254,8 @@ void SelectionDAGBuilder::visitBinary(const User &I, unsigned OpCode) {
     Flags.setNoNaNs(FMF.noNaNs());
     Flags.setNoSignedZeros(FMF.noSignedZeros());
     Flags.setUnsafeAlgebra(FMF.unsafeAlgebra());
+    Flags.setKeepExceptions(FMF.keepExceptions());
+    Flags.setKeepRounding(FMF.keepRounding());
   }
   SDValue BinNodeValue = DAG.getNode(OpCode, getCurSDLoc(), Op1.getValueType(),
                                      Op1, Op2, &Flags);
diff --git a/test/CodeGen/Generic/selectiondag-fenv.ll b/test/CodeGen/Generic/selectiondag-fenv.ll
new file mode 100644
index 0000000..ed2e3f6
--- /dev/null
+++ b/test/CodeGen/Generic/selectiondag-fenv.ll
@@ -0,0 +1,140 @@
+; RUN: llc < %s -print-machineinstrs=expand-isel-pseudos -o /dev/null -enable-except-access-fp-math 2>&1 | FileCheck %s -check-prefix=CHECK -check-prefix=EXCEPT
+; RUN: llc < %s -print-machineinstrs=expand-isel-pseudos -o /dev/null -enable-round-access-fp-math 2>&1 | FileCheck %s -check-prefix=CHECK -check-prefix=ROUND
+
+; SelectionDAG should not fold floating-point operations that have constant
+; expression as their operands when FEnv access is enabled and such folding
+; leads to differences in observable effects at runtime.
+
+define double @do-not-fold-fadd-that-can-trap() {
+; CHECK-LABEL: do-not-fold-fadd-that-can-trap:
+; CHECK: cp#0: 1.000000e+308
+entry:
+  %val = fadd double 1.000000e+308, 1.000000e+308
+  ret double %val
+}
+
+define double @do-not-fold-fsub-that-can-trap() {
+; CHECK-LABEL: do-not-fold-fsub-that-can-trap:
+; CHECK: cp#0: 1.000000e-308
+; CHECK: cp#1: 1.000000e+308
+entry:
+  %val = fsub double 1.000000e-308, 1.000000e+308
+  ret double %val
+}
+
+define double @do-not-fold-fmul-that-can-trap() {
+; CHECK-LABEL: do-not-fold-fmul-that-can-trap:
+; CHECK: cp#0: 1.000000e+300
+entry:
+  %val = fmul double 1.000000e+300, 1.000000e+300
+  ret double %val
+}
+
+define double @do-not-fold-fdiv-that-can-trap() {
+; CHECK-LABEL: do-not-fold-fdiv-that-can-trap:
+; CHECK: cp#0: 1.000000e+300
+; CHECK: cp#1: 1.000000e-300
+entry:
+  %val = fdiv double 1.000000e+300, 1.000000e-300
+  ret double %val
+}
+
+define double @do-not-fold-frem-that-can-trap() {
+; CHECK-LABEL: do-not-fold-frem-that-can-trap:
+; CHECK: cp#0: 1.0
+entry:
+  %val = frem double 1.0, 0.0
+  ret double %val
+}
+
+define double @do-not-fold-fadd-because-of-rounding() {
+; CHECK-LABEL: do-not-fold-fadd-because-of-rounding:
+; EXCEPT: cp#0: 1.010000e-01
+; EXCEPT: cp#1: 9.300000e-01
+; ROUND: cp#0: 1.010000e-01
+; ROUND: cp#1: 9.300000e-01
+entry:
+  %val = fadd double 0.101, 0.93
+  ret double %val
+}
+
+define double @do-not-fold-fsub-because-of-rounding() {
+; CHECK-LABEL: do-not-fold-fsub-because-of-rounding:
+; EXCEPT: cp#0: 1.010000e-01
+; EXCEPT: cp#1: 9.300000e-01
+; ROUND: cp#0: 1.010000e-01
+; ROUND: cp#1: 9.300000e-01
+entry:
+  %val = fsub double 0.101, 0.93
+  ret double %val
+}
+
+define double @do-not-fold-fmul-because-of-rounding() {
+; CHECK-LABEL: do-not-fold-fmul-because-of-rounding:
+; EXCEPT: cp#0: 1.010000e-01
+; EXCEPT: cp#1: 9.300000e-01
+; ROUND: cp#0: 1.010000e-01
+; ROUND: cp#1: 9.300000e-01
+entry:
+  %val = fmul double 0.101, 0.93
+  ret double %val
+}
+
+define double @do-not-fold-fdiv-because-of-rounding() {
+; CHECK-LABEL: do-not-fold-fdiv-because-of-rounding:
+; EXCEPT: cp#0: 1.010000e-01
+; EXCEPT: cp#1: 9.300000e-01
+; ROUND: cp#0: 1.010000e-01
+; ROUND: cp#1: 9.300000e-01
+entry:
+  %val = fdiv double 0.101, 0.93
+  ret double %val
+}
+
+; "frem" instruction falls into the same category, but none of tried test-cases
+; could raised inexact exception.
+
+define double @fold-fadd-without-rounding() {
+; CHECK-LABEL: fold-fadd-without-rounding:
+; EXCEPT: cp#0: 2.0
+; ROUND: cp#0: 2.0
+entry:
+  %val = fadd double 1.0, 1.0
+  ret double %val
+}
+
+define double @fold-fsub-without-rounding() {
+; CHECK-LABEL: fold-fsub-without-rounding:
+; EXCEPT: cp#0: 1.0
+; ROUND: cp#0: 1.0
+entry:
+  %val = fsub double 3.0, 2.0
+  ret double %val
+}
+
+define double @fold-fmul-without-rounding() {
+; CHECK-LABEL: fold-fmul-without-rounding:
+; EXCEPT: cp#0: 6.0
+; ROUND: cp#0: 6.0
+entry:
+  %val = fmul double 2.0, 3.0
+  ret double %val
+}
+
+define double @fold-fdiv-without-rounding() {
+; CHECK-LABEL: fold-fdiv-without-rounding:
+; EXCEPT: cp#0: 2.0
+; ROUND: cp#0: 2.0
+entry:
+  %val = fdiv double 6.0, 3.0
+  ret double %val
+}
+
+define double @fold-frem-without-rounding() {
+; CHECK-LABEL: fold-frem-without-rounding:
+; EXCEPT: cp#0: 1.0
+; ROUND: cp#0: 1.0
+entry:
+  %val = frem double 7.0, 3.0
+  ret double %val
+}
-- 
2.3.5

-------------- next part --------------
>From 095eda1ce520f260a4c227b6ff6a00b7ad99b2d4 Mon Sep 17 00:00:00 2001
From: Sergey Dmitrouk <sdmitrouk at accesssoftek.com>
Date: Mon, 17 Nov 2014 23:10:29 +0200
Subject: [PATCH 04/13] Skip constant folding to preserve FPEnv

Consider floating point environment flags when constant folding
expressions.
---
 include/llvm/Analysis/ValueTracking.h |  9 +++--
 include/llvm/IR/Constants.h           | 10 +++++-
 lib/Analysis/ValueTracking.cpp        | 25 +++++++++++++-
 lib/IR/ConstantFold.cpp               | 51 +++++++++++++++++++--------
 lib/IR/ConstantFold.h                 |  3 +-
 lib/IR/Constants.cpp                  | 65 +++++++++++++++++++++++++++++++++--
 6 files changed, 140 insertions(+), 23 deletions(-)

diff --git a/include/llvm/Analysis/ValueTracking.h b/include/llvm/Analysis/ValueTracking.h
index 53b2e61..1738cf0 100644
--- a/include/llvm/Analysis/ValueTracking.h
+++ b/include/llvm/Analysis/ValueTracking.h
@@ -247,8 +247,9 @@ namespace llvm {
                                           const TargetLibraryInfo *TLI = nullptr);
 
   /// isSafeToSpeculativelyExecute - Return true if the instruction does not
-  /// have any effects besides calculating the result and does not have
-  /// undefined behavior.
+  /// have any effects besides calculating the result (in a way that doesn't
+  /// lose floating-point exceptions or precision when requested via
+  /// corresponding flags) and does not have undefined behavior.
   ///
   /// This method never returns true for an instruction that returns true for
   /// mayHaveSideEffects; however, this method also does some other checks in
@@ -273,7 +274,9 @@ namespace llvm {
   bool isSafeToSpeculativelyExecute(const Value *V,
                                     const Instruction *CtxI = nullptr,
                                     const DominatorTree *DT = nullptr,
-                                    const TargetLibraryInfo *TLI = nullptr);
+                                    const TargetLibraryInfo *TLI = nullptr,
+                                    bool KeepExceptions = false,
+                                    bool KeepRounding = false);
 
   /// Returns true if the result or effects of the given instructions \p I
   /// depend on or influence global memory.
diff --git a/include/llvm/IR/Constants.h b/include/llvm/IR/Constants.h
index 11ca800..a7edf6c 100644
--- a/include/llvm/IR/Constants.h
+++ b/include/llvm/IR/Constants.h
@@ -1069,7 +1069,9 @@ public:
   ///
   /// \param OnlyIfReducedTy see \a getWithOperands() docs.
   static Constant *get(unsigned Opcode, Constant *C1, Constant *C2,
-                       unsigned Flags = 0, Type *OnlyIfReducedTy = nullptr);
+                       unsigned Flags = 0, Type *OnlyIfReducedTy = nullptr,
+                       bool KeepExceptions = false,
+                       bool KeepRounding = false);
 
   /// \brief Return an ICmp or FCmp comparison operator constant expression.
   ///
@@ -1193,6 +1195,12 @@ public:
     return V->getValueID() == ConstantExprVal;
   }
 
+  /// Evaluates floating point operation to examine how it could affect
+  /// floating-environment at run-time if it's not folded, does nothing for
+  /// non-FP operations.  Returns false for unsupported values, otherwise true
+  /// is returned and S is assigned status of the operation.
+  static bool getFPOpExceptions(const Value *V, APFloat::opStatus &S);
+
 private:
   // Shadow Value::setValueSubclassData with a private forwarding method so that
   // subclasses cannot accidentally use it.
diff --git a/lib/Analysis/ValueTracking.cpp b/lib/Analysis/ValueTracking.cpp
index f0b04b7..f4e96e2 100644
--- a/lib/Analysis/ValueTracking.cpp
+++ b/lib/Analysis/ValueTracking.cpp
@@ -3133,11 +3133,18 @@ bool llvm::isDereferenceablePointer(const Value *V, const DataLayout &DL,
 bool llvm::isSafeToSpeculativelyExecute(const Value *V,
                                         const Instruction *CtxI,
                                         const DominatorTree *DT,
-                                        const TargetLibraryInfo *TLI) {
+                                        const TargetLibraryInfo *TLI,
+                                        bool KeepExceptions,
+                                        bool KeepRounding) {
   const Operator *Inst = dyn_cast<Operator>(V);
   if (!Inst)
     return false;
 
+  if (const FPMathOperator *MathOp = dyn_cast<FPMathOperator>(Inst)) {
+      KeepExceptions |= MathOp->hasKeepExceptions();
+      KeepRounding |= MathOp->hasKeepRounding();
+  }
+
   for (unsigned i = 0, e = Inst->getNumOperands(); i != e; ++i)
     if (Constant *C = dyn_cast<Constant>(Inst->getOperand(i)))
       if (C->canTrap())
@@ -3146,6 +3153,22 @@ bool llvm::isSafeToSpeculativelyExecute(const Value *V,
   switch (Inst->getOpcode()) {
   default:
     return true;
+  case Instruction::FMul:
+  case Instruction::FAdd:
+  case Instruction::FSub:
+  case Instruction::FDiv:
+  case Instruction::FRem:
+    if (KeepExceptions || KeepRounding) {
+      APFloat::opStatus S;
+      if (!ConstantExpr::getFPOpExceptions(Inst, S))
+        return false;
+
+      if (KeepExceptions && S != APFloat::opOK)
+        return false;
+      if (KeepRounding && (S & APFloat::opInexact))
+        return false;
+    }
+    return true;
   case Instruction::UDiv:
   case Instruction::URem: {
     // x / y is undefined if y == 0.
diff --git a/lib/IR/ConstantFold.cpp b/lib/IR/ConstantFold.cpp
index 68ddf09..721c96d 100644
--- a/lib/IR/ConstantFold.cpp
+++ b/lib/IR/ConstantFold.cpp
@@ -915,7 +915,9 @@ Constant *llvm::ConstantFoldInsertValueInstruction(Constant *Agg,
 
 
 Constant *llvm::ConstantFoldBinaryInstruction(unsigned Opcode,
-                                              Constant *C1, Constant *C2) {
+                                              Constant *C1, Constant *C2,
+                                              bool KeepExceptions,
+                                              bool KeepRounding) {
   // Handle UndefValue up front.
   if (isa<UndefValue>(C1) || isa<UndefValue>(C2)) {
     switch (Opcode) {
@@ -1099,7 +1101,8 @@ Constant *llvm::ConstantFoldBinaryInstruction(unsigned Opcode,
   } else if (isa<ConstantInt>(C1)) {
     // If C1 is a ConstantInt and C2 is not, swap the operands.
     if (Instruction::isCommutative(Opcode))
-      return ConstantExpr::get(Opcode, C2, C1);
+      return ConstantExpr::get(Opcode, C2, C1, 0, nullptr, KeepExceptions,
+                               KeepRounding);
   }
 
   // At this point we know neither constant is an UndefValue.
@@ -1171,23 +1174,39 @@ Constant *llvm::ConstantFoldBinaryInstruction(unsigned Opcode,
       APFloat C1V = CFP1->getValueAPF();
       APFloat C2V = CFP2->getValueAPF();
       APFloat C3V = C1V;  // copy for modification
+      APFloat::opStatus S;
       switch (Opcode) {
-      default:                   
+      default:
         break;
       case Instruction::FAdd:
-        (void)C3V.add(C2V, APFloat::rmNearestTiesToEven);
+        S = C3V.add(C2V, APFloat::rmNearestTiesToEven);
+        if ((KeepExceptions && S != APFloat::opOK) ||
+            (KeepRounding && (S & APFloat::opInexact)))
+          break;
         return ConstantFP::get(C1->getContext(), C3V);
       case Instruction::FSub:
-        (void)C3V.subtract(C2V, APFloat::rmNearestTiesToEven);
+        S = C3V.subtract(C2V, APFloat::rmNearestTiesToEven);
+        if ((KeepExceptions && S != APFloat::opOK) ||
+            (KeepRounding && (S & APFloat::opInexact)))
+          break;
         return ConstantFP::get(C1->getContext(), C3V);
       case Instruction::FMul:
-        (void)C3V.multiply(C2V, APFloat::rmNearestTiesToEven);
+        S = C3V.multiply(C2V, APFloat::rmNearestTiesToEven);
+        if ((KeepExceptions && S != APFloat::opOK) ||
+            (KeepRounding && (S & APFloat::opInexact)))
+          break;
         return ConstantFP::get(C1->getContext(), C3V);
       case Instruction::FDiv:
-        (void)C3V.divide(C2V, APFloat::rmNearestTiesToEven);
+        S = C3V.divide(C2V, APFloat::rmNearestTiesToEven);
+        if ((KeepExceptions && S != APFloat::opOK) ||
+            (KeepRounding && (S & APFloat::opInexact)))
+          break;
         return ConstantFP::get(C1->getContext(), C3V);
       case Instruction::FRem:
-        (void)C3V.mod(C2V);
+        S = C3V.mod(C2V);
+        if ((KeepExceptions && S != APFloat::opOK) ||
+            (KeepRounding && (S & APFloat::opInexact)))
+          break;
         return ConstantFP::get(C1->getContext(), C3V);
       }
     }
@@ -1200,10 +1219,11 @@ Constant *llvm::ConstantFoldBinaryInstruction(unsigned Opcode,
         ConstantExpr::getExtractElement(C1, ConstantInt::get(Ty, i));
       Constant *RHS =
         ConstantExpr::getExtractElement(C2, ConstantInt::get(Ty, i));
-      
-      Result.push_back(ConstantExpr::get(Opcode, LHS, RHS));
+
+      Result.push_back(ConstantExpr::get(Opcode, LHS, RHS, 0, nullptr,
+                                         KeepExceptions, KeepRounding));
     }
-    
+
     return ConstantVector::get(Result);
   }
 
@@ -1215,15 +1235,18 @@ Constant *llvm::ConstantFoldBinaryInstruction(unsigned Opcode,
     // Given ((a + b) + c), if (b + c) folds to something interesting, return
     // (a + (b + c)).
     if (Instruction::isAssociative(Opcode) && CE1->getOpcode() == Opcode) {
-      Constant *T = ConstantExpr::get(Opcode, CE1->getOperand(1), C2);
+      Constant *T = ConstantExpr::get(Opcode, CE1->getOperand(1), C2, 0,
+                                      nullptr, KeepExceptions, KeepRounding);
       if (!isa<ConstantExpr>(T) || cast<ConstantExpr>(T)->getOpcode() != Opcode)
-        return ConstantExpr::get(Opcode, CE1->getOperand(0), T);
+        return ConstantExpr::get(Opcode, CE1->getOperand(0), T, 0, nullptr,
+                                 KeepExceptions, KeepRounding);
     }
   } else if (isa<ConstantExpr>(C2)) {
     // If C2 is a constant expr and C1 isn't, flop them around and fold the
     // other way if possible.
     if (Instruction::isCommutative(Opcode))
-      return ConstantFoldBinaryInstruction(Opcode, C2, C1);
+      return ConstantFoldBinaryInstruction(Opcode, C2, C1, KeepExceptions,
+                                           KeepRounding);
   }
 
   // i1 can be simplified in many cases.
diff --git a/lib/IR/ConstantFold.h b/lib/IR/ConstantFold.h
index 42a9c6b..8e35aa0 100644
--- a/lib/IR/ConstantFold.h
+++ b/lib/IR/ConstantFold.h
@@ -44,7 +44,8 @@ namespace llvm {
   Constant *ConstantFoldInsertValueInstruction(Constant *Agg, Constant *Val,
                                                ArrayRef<unsigned> Idxs);
   Constant *ConstantFoldBinaryInstruction(unsigned Opcode, Constant *V1,
-                                          Constant *V2);
+                                          Constant *V2, bool KeepFPExceptions,
+                                          bool KeepFPRounding);
   Constant *ConstantFoldCompareInstruction(unsigned short predicate, 
                                            Constant *C1, Constant *C2);
   Constant *ConstantFoldGetElementPtr(Constant *C, bool inBounds,
diff --git a/lib/IR/Constants.cpp b/lib/IR/Constants.cpp
index 798ea24..c52c185 100644
--- a/lib/IR/Constants.cpp
+++ b/lib/IR/Constants.cpp
@@ -327,8 +327,9 @@ static bool canTrapImpl(const Constant *C,
   // ConstantExpr traps if any operands can trap.
   for (unsigned i = 0, e = C->getNumOperands(); i != e; ++i) {
     if (ConstantExpr *Op = dyn_cast<ConstantExpr>(CE->getOperand(i))) {
-      if (NonTrappingOps.insert(Op).second && canTrapImpl(Op, NonTrappingOps))
+      if (NonTrappingOps.insert(Op).second && canTrapImpl(Op, NonTrappingOps)) {
         return true;
+      }
     }
   }
 
@@ -1869,7 +1870,8 @@ Constant *ConstantExpr::getAddrSpaceCast(Constant *C, Type *DstTy,
 }
 
 Constant *ConstantExpr::get(unsigned Opcode, Constant *C1, Constant *C2,
-                            unsigned Flags, Type *OnlyIfReducedTy) {
+                            unsigned Flags, Type *OnlyIfReducedTy,
+                            bool KeepExceptions, bool KeepRounding) {
   // Check the operands for consistency first.
   assert(Opcode >= Instruction::BinaryOpsBegin &&
          Opcode <  Instruction::BinaryOpsEnd   &&
@@ -1935,8 +1937,11 @@ Constant *ConstantExpr::get(unsigned Opcode, Constant *C1, Constant *C2,
   }
 #endif
 
-  if (Constant *FC = ConstantFoldBinaryInstruction(Opcode, C1, C2))
+  if (Constant *FC = ConstantFoldBinaryInstruction(Opcode, C1, C2,
+                                                   KeepExceptions,
+                                                   KeepRounding)) {
     return FC;          // Fold a few common cases.
+  }
 
   if (OnlyIfReducedTy == C1->getType())
     return nullptr;
@@ -3091,3 +3096,57 @@ Instruction *ConstantExpr::getAsInstruction() {
     return BO;
   }
 }
+
+bool ConstantExpr::getFPOpExceptions(const Value *V, APFloat::opStatus &S) {
+  unsigned Opcode;
+  const User *U;
+  if (const Operator *Op = dyn_cast<Operator>(V)) {
+    U = Op;
+    Opcode = Op->getOpcode();
+  } if (const ConstantExpr *C = dyn_cast<ConstantExpr>(V)) {
+    U = C;
+    Opcode = C->getOpcode();
+  } else {
+    return false;
+  }
+
+  switch (Opcode) {
+  default:
+    return true;
+  case Instruction::FMul:
+  case Instruction::FAdd:
+  case Instruction::FSub:
+  case Instruction::FDiv:
+  case Instruction::FRem: {
+    ConstantFP *LHSC = dyn_cast<ConstantFP>(U->getOperand(0));
+    ConstantFP *RHSC = dyn_cast<ConstantFP>(U->getOperand(1));
+
+    if (!LHSC || !RHSC) {
+      return false;
+    }
+
+    APFloat LHS = LHSC->getValueAPF();
+    APFloat RHS = LHSC->getValueAPF();
+
+    switch (Opcode) {
+    case Instruction::FMul:
+      S = LHS.multiply(RHS, APFloat::rmNearestTiesToEven);
+      break;
+    case Instruction::FAdd:
+      S = LHS.add(RHS, APFloat::rmNearestTiesToEven);
+      break;
+    case Instruction::FSub:
+      S = LHS.subtract(RHS, APFloat::rmNearestTiesToEven);
+      break;
+    case Instruction::FDiv:
+      S = LHS.divide(RHS, APFloat::rmNearestTiesToEven);
+      break;
+    case Instruction::FRem:
+      S = LHS.mod(RHS);
+      break;
+    }
+
+    return true;
+  }
+  }
+}
-- 
2.3.5

-------------- next part --------------
>From 837235df9ef6c477296113c369e764cd109d7795 Mon Sep 17 00:00:00 2001
From: Sergey Dmitrouk <sdmitrouk at accesssoftek.com>
Date: Wed, 26 Nov 2014 15:22:55 +0200
Subject: [PATCH 05/13] Teach IR builder and folders about new flags

FastMathFlags were moved to a separate header to make it possible
to use in IR/Constants.h, which otherwise created circular dependency
between IR/Constants.h and IR/Operator.h.
---
 include/llvm/Analysis/ConstantFolding.h |   8 +-
 include/llvm/Analysis/TargetFolder.h    |  26 ++++---
 include/llvm/IR/ConstantFolder.h        |  31 +++++---
 include/llvm/IR/Constants.h             |  19 +++--
 include/llvm/IR/FastMathFlags.h         |  78 +++++++++++++++++++
 include/llvm/IR/IRBuilder.h             |  10 +--
 include/llvm/IR/Operator.h              |  57 +-------------
 lib/Analysis/ConstantFolding.cpp        |  32 +++++---
 lib/IR/ConstantFold.cpp                 |  36 ++++-----
 lib/IR/ConstantFold.h                   |   4 +-
 lib/IR/Constants.cpp                    |  28 ++++---
 test/Other/fpenv-constant-fold.ll       | 130 ++++++++++++++++++++++++++++++++
 12 files changed, 320 insertions(+), 139 deletions(-)
 create mode 100644 include/llvm/IR/FastMathFlags.h
 create mode 100644 test/Other/fpenv-constant-fold.ll

diff --git a/include/llvm/Analysis/ConstantFolding.h b/include/llvm/Analysis/ConstantFolding.h
index e8185b3..4efa646 100644
--- a/include/llvm/Analysis/ConstantFolding.h
+++ b/include/llvm/Analysis/ConstantFolding.h
@@ -20,6 +20,8 @@
 #ifndef LLVM_ANALYSIS_CONSTANTFOLDING_H
 #define LLVM_ANALYSIS_CONSTANTFOLDING_H
 
+#include "llvm/IR/FastMathFlags.h"
+
 namespace llvm {
   class Constant;
   class ConstantExpr;
@@ -44,7 +46,8 @@ namespace llvm {
 /// result is returned, if not, null is returned.
   Constant *
   ConstantFoldConstantExpression(const ConstantExpr *CE, const DataLayout &DL,
-                                 const TargetLibraryInfo *TLI = nullptr);
+                                 const TargetLibraryInfo *TLI = nullptr,
+                                 FastMathFlags FMF = FastMathFlags());
 
 /// ConstantFoldInstOperands - Attempt to constant fold an instruction with the
 /// specified operands.  If successful, the constant result is returned, if not,
@@ -55,7 +58,8 @@ namespace llvm {
   Constant *ConstantFoldInstOperands(unsigned Opcode, Type *DestTy,
                                      ArrayRef<Constant *> Ops,
                                      const DataLayout &DL,
-                                     const TargetLibraryInfo *TLI = nullptr);
+                                     const TargetLibraryInfo *TLI = nullptr,
+                                     FastMathFlags FMF = FastMathFlags());
 
 /// ConstantFoldCompareInstOperands - Attempt to constant fold a compare
 /// instruction (icmp/fcmp) with the specified operands.  If it fails, it
diff --git a/include/llvm/Analysis/TargetFolder.h b/include/llvm/Analysis/TargetFolder.h
index 12bf9fe..befdff5 100644
--- a/include/llvm/Analysis/TargetFolder.h
+++ b/include/llvm/Analysis/TargetFolder.h
@@ -22,6 +22,7 @@
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/Analysis/ConstantFolding.h"
 #include "llvm/IR/Constants.h"
+#include "llvm/IR/FastMathFlags.h"
 #include "llvm/IR/InstrTypes.h"
 
 namespace llvm {
@@ -51,22 +52,25 @@ public:
                       bool HasNUW = false, bool HasNSW = false) const {
     return Fold(ConstantExpr::getAdd(LHS, RHS, HasNUW, HasNSW));
   }
-  Constant *CreateFAdd(Constant *LHS, Constant *RHS) const {
-    return Fold(ConstantExpr::getFAdd(LHS, RHS));
+  Constant *CreateFAdd(Constant *LHS, Constant *RHS,
+                       FastMathFlags FMF = FastMathFlags()) const {
+    return Fold(ConstantExpr::getFAdd(LHS, RHS, FMF));
   }
   Constant *CreateSub(Constant *LHS, Constant *RHS,
                       bool HasNUW = false, bool HasNSW = false) const {
     return Fold(ConstantExpr::getSub(LHS, RHS, HasNUW, HasNSW));
   }
-  Constant *CreateFSub(Constant *LHS, Constant *RHS) const {
-    return Fold(ConstantExpr::getFSub(LHS, RHS));
+  Constant *CreateFSub(Constant *LHS, Constant *RHS,
+                       FastMathFlags FMF = FastMathFlags()) const {
+    return Fold(ConstantExpr::getFSub(LHS, RHS, FMF));
   }
   Constant *CreateMul(Constant *LHS, Constant *RHS,
                       bool HasNUW = false, bool HasNSW = false) const {
     return Fold(ConstantExpr::getMul(LHS, RHS, HasNUW, HasNSW));
   }
-  Constant *CreateFMul(Constant *LHS, Constant *RHS) const {
-    return Fold(ConstantExpr::getFMul(LHS, RHS));
+  Constant *CreateFMul(Constant *LHS, Constant *RHS,
+                       FastMathFlags FMF = FastMathFlags()) const {
+    return Fold(ConstantExpr::getFMul(LHS, RHS, FMF));
   }
   Constant *CreateUDiv(Constant *LHS, Constant *RHS, bool isExact = false)const{
     return Fold(ConstantExpr::getUDiv(LHS, RHS, isExact));
@@ -74,8 +78,9 @@ public:
   Constant *CreateSDiv(Constant *LHS, Constant *RHS, bool isExact = false)const{
     return Fold(ConstantExpr::getSDiv(LHS, RHS, isExact));
   }
-  Constant *CreateFDiv(Constant *LHS, Constant *RHS) const {
-    return Fold(ConstantExpr::getFDiv(LHS, RHS));
+  Constant *CreateFDiv(Constant *LHS, Constant *RHS,
+                       FastMathFlags FMF = FastMathFlags()) const {
+    return Fold(ConstantExpr::getFDiv(LHS, RHS, FMF));
   }
   Constant *CreateURem(Constant *LHS, Constant *RHS) const {
     return Fold(ConstantExpr::getURem(LHS, RHS));
@@ -83,8 +88,9 @@ public:
   Constant *CreateSRem(Constant *LHS, Constant *RHS) const {
     return Fold(ConstantExpr::getSRem(LHS, RHS));
   }
-  Constant *CreateFRem(Constant *LHS, Constant *RHS) const {
-    return Fold(ConstantExpr::getFRem(LHS, RHS));
+  Constant *CreateFRem(Constant *LHS, Constant *RHS,
+                       FastMathFlags FMF = FastMathFlags()) const {
+    return Fold(ConstantExpr::getFRem(LHS, RHS, FMF));
   }
   Constant *CreateShl(Constant *LHS, Constant *RHS,
                       bool HasNUW = false, bool HasNSW = false) const {
diff --git a/include/llvm/IR/ConstantFolder.h b/include/llvm/IR/ConstantFolder.h
index fb6ca3b..60909ea 100644
--- a/include/llvm/IR/ConstantFolder.h
+++ b/include/llvm/IR/ConstantFolder.h
@@ -18,6 +18,7 @@
 #define LLVM_IR_CONSTANTFOLDER_H
 
 #include "llvm/IR/Constants.h"
+#include "llvm/IR/FastMathFlags.h"
 #include "llvm/IR/InstrTypes.h"
 
 namespace llvm {
@@ -35,22 +36,25 @@ public:
                       bool HasNUW = false, bool HasNSW = false) const {
     return ConstantExpr::getAdd(LHS, RHS, HasNUW, HasNSW);
   }
-  Constant *CreateFAdd(Constant *LHS, Constant *RHS) const {
-    return ConstantExpr::getFAdd(LHS, RHS);
+  Constant *CreateFAdd(Constant *LHS, Constant *RHS,
+                       FastMathFlags FMF = FastMathFlags()) const {
+    return ConstantExpr::getFAdd(LHS, RHS, FMF);
   }
   Constant *CreateSub(Constant *LHS, Constant *RHS,
                       bool HasNUW = false, bool HasNSW = false) const {
     return ConstantExpr::getSub(LHS, RHS, HasNUW, HasNSW);
   }
-  Constant *CreateFSub(Constant *LHS, Constant *RHS) const {
-    return ConstantExpr::getFSub(LHS, RHS);
+  Constant *CreateFSub(Constant *LHS, Constant *RHS,
+                       FastMathFlags FMF = FastMathFlags()) const {
+    return ConstantExpr::getFSub(LHS, RHS, FMF);
   }
   Constant *CreateMul(Constant *LHS, Constant *RHS,
                       bool HasNUW = false, bool HasNSW = false) const {
     return ConstantExpr::getMul(LHS, RHS, HasNUW, HasNSW);
   }
-  Constant *CreateFMul(Constant *LHS, Constant *RHS) const {
-    return ConstantExpr::getFMul(LHS, RHS);
+  Constant *CreateFMul(Constant *LHS, Constant *RHS,
+                       FastMathFlags FMF = FastMathFlags()) const {
+    return ConstantExpr::getFMul(LHS, RHS, FMF);
   }
   Constant *CreateUDiv(Constant *LHS, Constant *RHS,
                        bool isExact = false) const {
@@ -60,8 +64,9 @@ public:
                        bool isExact = false) const {
     return ConstantExpr::getSDiv(LHS, RHS, isExact);
   }
-  Constant *CreateFDiv(Constant *LHS, Constant *RHS) const {
-    return ConstantExpr::getFDiv(LHS, RHS);
+  Constant *CreateFDiv(Constant *LHS, Constant *RHS,
+                       FastMathFlags FMF = FastMathFlags()) const {
+    return ConstantExpr::getFDiv(LHS, RHS, FMF);
   }
   Constant *CreateURem(Constant *LHS, Constant *RHS) const {
     return ConstantExpr::getURem(LHS, RHS);
@@ -69,8 +74,9 @@ public:
   Constant *CreateSRem(Constant *LHS, Constant *RHS) const {
     return ConstantExpr::getSRem(LHS, RHS);
   }
-  Constant *CreateFRem(Constant *LHS, Constant *RHS) const {
-    return ConstantExpr::getFRem(LHS, RHS);
+  Constant *CreateFRem(Constant *LHS, Constant *RHS,
+                       FastMathFlags FMF = FastMathFlags()) const {
+    return ConstantExpr::getFRem(LHS, RHS, FMF);
   }
   Constant *CreateShl(Constant *LHS, Constant *RHS,
                       bool HasNUW = false, bool HasNSW = false) const {
@@ -95,8 +101,9 @@ public:
   }
 
   Constant *CreateBinOp(Instruction::BinaryOps Opc,
-                        Constant *LHS, Constant *RHS) const {
-    return ConstantExpr::get(Opc, LHS, RHS);
+                        Constant *LHS, Constant *RHS,
+                        FastMathFlags FMF = FastMathFlags()) const {
+    return ConstantExpr::get(Opc, LHS, RHS, 0, nullptr, FMF);
   }
 
   //===--------------------------------------------------------------------===//
diff --git a/include/llvm/IR/Constants.h b/include/llvm/IR/Constants.h
index a7edf6c..432fa6c 100644
--- a/include/llvm/IR/Constants.h
+++ b/include/llvm/IR/Constants.h
@@ -26,6 +26,7 @@
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/IR/Constant.h"
 #include "llvm/IR/DerivedTypes.h"
+#include "llvm/IR/FastMathFlags.h"
 #include "llvm/IR/OperandTraits.h"
 
 namespace llvm {
@@ -894,19 +895,24 @@ public:
   static Constant *getNot(Constant *C);
   static Constant *getAdd(Constant *C1, Constant *C2,
                           bool HasNUW = false, bool HasNSW = false);
-  static Constant *getFAdd(Constant *C1, Constant *C2);
+  static Constant *getFAdd(Constant *C1, Constant *C2,
+                           FastMathFlags FMF = FastMathFlags());
   static Constant *getSub(Constant *C1, Constant *C2,
                           bool HasNUW = false, bool HasNSW = false);
-  static Constant *getFSub(Constant *C1, Constant *C2);
+  static Constant *getFSub(Constant *C1, Constant *C2,
+                           FastMathFlags FMF = FastMathFlags());
   static Constant *getMul(Constant *C1, Constant *C2,
                           bool HasNUW = false, bool HasNSW = false);
-  static Constant *getFMul(Constant *C1, Constant *C2);
+  static Constant *getFMul(Constant *C1, Constant *C2,
+                           FastMathFlags FMF = FastMathFlags());
   static Constant *getUDiv(Constant *C1, Constant *C2, bool isExact = false);
   static Constant *getSDiv(Constant *C1, Constant *C2, bool isExact = false);
-  static Constant *getFDiv(Constant *C1, Constant *C2);
+  static Constant *getFDiv(Constant *C1, Constant *C2,
+                           FastMathFlags FMF = FastMathFlags());
   static Constant *getURem(Constant *C1, Constant *C2);
   static Constant *getSRem(Constant *C1, Constant *C2);
-  static Constant *getFRem(Constant *C1, Constant *C2);
+  static Constant *getFRem(Constant *C1, Constant *C2,
+                           FastMathFlags FMF = FastMathFlags());
   static Constant *getAnd(Constant *C1, Constant *C2);
   static Constant *getOr(Constant *C1, Constant *C2);
   static Constant *getXor(Constant *C1, Constant *C2);
@@ -1070,8 +1076,7 @@ public:
   /// \param OnlyIfReducedTy see \a getWithOperands() docs.
   static Constant *get(unsigned Opcode, Constant *C1, Constant *C2,
                        unsigned Flags = 0, Type *OnlyIfReducedTy = nullptr,
-                       bool KeepExceptions = false,
-                       bool KeepRounding = false);
+                       FastMathFlags FMF = FastMathFlags());
 
   /// \brief Return an ICmp or FCmp comparison operator constant expression.
   ///
diff --git a/include/llvm/IR/FastMathFlags.h b/include/llvm/IR/FastMathFlags.h
new file mode 100644
index 0000000..b74a78c
--- /dev/null
+++ b/include/llvm/IR/FastMathFlags.h
@@ -0,0 +1,78 @@
+//===- FastMathFlags.h - Fast math flags structure definition ---*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file provides convenience struct for specifying and reasoning about
+// fast-math flags.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_IR_FASTMATHFLAGS_H
+#define LLVM_IR_FASTMATHFLAGS_H
+
+namespace llvm {
+
+/// Convenience struct for specifying and reasoning about fast-math flags.
+class FastMathFlags {
+private:
+  friend class FPMathOperator;
+  unsigned Flags;
+  FastMathFlags(unsigned F) : Flags(F) { }
+
+public:
+  enum {
+    UnsafeAlgebra   = (1 << 0),
+    NoNaNs          = (1 << 1),
+    NoInfs          = (1 << 2),
+    NoSignedZeros   = (1 << 3),
+    AllowReciprocal = (1 << 4),
+    KeepExceptions  = (1 << 5),
+    KeepRounding    = (1 << 6)
+  };
+
+  FastMathFlags() : Flags(0)
+  { }
+
+  /// Whether any flag is set
+  bool any() const { return Flags != 0; }
+
+  /// Set all the flags to false
+  void clear() { Flags = 0; }
+
+  /// Flag queries
+  bool noNaNs() const          { return 0 != (Flags & NoNaNs); }
+  bool noInfs() const          { return 0 != (Flags & NoInfs); }
+  bool noSignedZeros() const   { return 0 != (Flags & NoSignedZeros); }
+  bool allowReciprocal() const { return 0 != (Flags & AllowReciprocal); }
+  bool unsafeAlgebra() const   { return 0 != (Flags & UnsafeAlgebra); }
+  bool keepExceptions() const  { return 0 != (Flags & KeepExceptions); }
+  bool keepRounding()const     { return 0 != (Flags & KeepRounding); }
+
+  /// Flag setters
+  void setNoNaNs()          { Flags |= NoNaNs; }
+  void setNoInfs()          { Flags |= NoInfs; }
+  void setNoSignedZeros()   { Flags |= NoSignedZeros; }
+  void setAllowReciprocal() { Flags |= AllowReciprocal; }
+  void setUnsafeAlgebra() {
+    Flags |= UnsafeAlgebra;
+    setNoNaNs();
+    setNoInfs();
+    setNoSignedZeros();
+    setAllowReciprocal();
+  }
+  void setKeepExceptions() { Flags |= KeepExceptions; }
+  void setKeepRounding()   { Flags |= KeepRounding; }
+
+  void operator&=(const FastMathFlags &OtherFlags) {
+    Flags &= OtherFlags.Flags;
+  }
+};
+
+} // End llvm namespace
+
+#endif
diff --git a/include/llvm/IR/IRBuilder.h b/include/llvm/IR/IRBuilder.h
index f021283..e7556e8 100644
--- a/include/llvm/IR/IRBuilder.h
+++ b/include/llvm/IR/IRBuilder.h
@@ -759,7 +759,7 @@ public:
                     MDNode *FPMathTag = nullptr) {
     if (Constant *LC = dyn_cast<Constant>(LHS))
       if (Constant *RC = dyn_cast<Constant>(RHS))
-        return Insert(Folder.CreateFAdd(LC, RC), Name);
+        return Insert(Folder.CreateFAdd(LC, RC, FMF), Name);
     return Insert(AddFPMathAttributes(BinaryOperator::CreateFAdd(LHS, RHS),
                                       FPMathTag, FMF), Name);
   }
@@ -781,7 +781,7 @@ public:
                     MDNode *FPMathTag = nullptr) {
     if (Constant *LC = dyn_cast<Constant>(LHS))
       if (Constant *RC = dyn_cast<Constant>(RHS))
-        return Insert(Folder.CreateFSub(LC, RC), Name);
+        return Insert(Folder.CreateFSub(LC, RC, FMF), Name);
     return Insert(AddFPMathAttributes(BinaryOperator::CreateFSub(LHS, RHS),
                                       FPMathTag, FMF), Name);
   }
@@ -803,7 +803,7 @@ public:
                     MDNode *FPMathTag = nullptr) {
     if (Constant *LC = dyn_cast<Constant>(LHS))
       if (Constant *RC = dyn_cast<Constant>(RHS))
-        return Insert(Folder.CreateFMul(LC, RC), Name);
+        return Insert(Folder.CreateFMul(LC, RC, FMF), Name);
     return Insert(AddFPMathAttributes(BinaryOperator::CreateFMul(LHS, RHS),
                                       FPMathTag, FMF), Name);
   }
@@ -835,7 +835,7 @@ public:
                     MDNode *FPMathTag = nullptr) {
     if (Constant *LC = dyn_cast<Constant>(LHS))
       if (Constant *RC = dyn_cast<Constant>(RHS))
-        return Insert(Folder.CreateFDiv(LC, RC), Name);
+        return Insert(Folder.CreateFDiv(LC, RC, FMF), Name);
     return Insert(AddFPMathAttributes(BinaryOperator::CreateFDiv(LHS, RHS),
                                       FPMathTag, FMF), Name);
   }
@@ -855,7 +855,7 @@ public:
                     MDNode *FPMathTag = nullptr) {
     if (Constant *LC = dyn_cast<Constant>(LHS))
       if (Constant *RC = dyn_cast<Constant>(RHS))
-        return Insert(Folder.CreateFRem(LC, RC), Name);
+        return Insert(Folder.CreateFRem(LC, RC, FMF), Name);
     return Insert(AddFPMathAttributes(BinaryOperator::CreateFRem(LHS, RHS),
                                       FPMathTag, FMF), Name);
   }
diff --git a/include/llvm/IR/Operator.h b/include/llvm/IR/Operator.h
index 0f4d07c..301db0c 100644
--- a/include/llvm/IR/Operator.h
+++ b/include/llvm/IR/Operator.h
@@ -18,6 +18,7 @@
 #include "llvm/IR/Constants.h"
 #include "llvm/IR/DataLayout.h"
 #include "llvm/IR/DerivedTypes.h"
+#include "llvm/IR/FastMathFlags.h"
 #include "llvm/IR/Instruction.h"
 #include "llvm/IR/Type.h"
 
@@ -160,62 +161,6 @@ public:
   }
 };
 
-/// Convenience struct for specifying and reasoning about fast-math flags.
-class FastMathFlags {
-private:
-  friend class FPMathOperator;
-  unsigned Flags;
-  FastMathFlags(unsigned F) : Flags(F) { }
-
-public:
-  enum {
-    UnsafeAlgebra   = (1 << 0),
-    NoNaNs          = (1 << 1),
-    NoInfs          = (1 << 2),
-    NoSignedZeros   = (1 << 3),
-    AllowReciprocal = (1 << 4),
-    KeepExceptions  = (1 << 5),
-    KeepRounding    = (1 << 6)
-  };
-
-  FastMathFlags() : Flags(0)
-  { }
-
-  /// Whether any flag is set
-  bool any() const { return Flags != 0; }
-
-  /// Set all the flags to false
-  void clear() { Flags = 0; }
-
-  /// Flag queries
-  bool noNaNs() const          { return 0 != (Flags & NoNaNs); }
-  bool noInfs() const          { return 0 != (Flags & NoInfs); }
-  bool noSignedZeros() const   { return 0 != (Flags & NoSignedZeros); }
-  bool allowReciprocal() const { return 0 != (Flags & AllowReciprocal); }
-  bool unsafeAlgebra() const   { return 0 != (Flags & UnsafeAlgebra); }
-  bool keepExceptions() const  { return 0 != (Flags & KeepExceptions); }
-  bool keepRounding()const     { return 0 != (Flags & KeepRounding); }
-
-  /// Flag setters
-  void setNoNaNs()          { Flags |= NoNaNs; }
-  void setNoInfs()          { Flags |= NoInfs; }
-  void setNoSignedZeros()   { Flags |= NoSignedZeros; }
-  void setAllowReciprocal() { Flags |= AllowReciprocal; }
-  void setUnsafeAlgebra() {
-    Flags |= UnsafeAlgebra;
-    setNoNaNs();
-    setNoInfs();
-    setNoSignedZeros();
-    setAllowReciprocal();
-  }
-  void setKeepExceptions() { Flags |= KeepExceptions; }
-  void setKeepRounding()   { Flags |= KeepRounding; }
-
-  void operator&=(const FastMathFlags &OtherFlags) {
-    Flags &= OtherFlags.Flags;
-  }
-};
-
 
 /// Utility class for floating point operations which can have
 /// information about relaxed accuracy requirements attached to them.
diff --git a/lib/Analysis/ConstantFolding.cpp b/lib/Analysis/ConstantFolding.cpp
index ccb5663..e17dbed 100644
--- a/lib/Analysis/ConstantFolding.cpp
+++ b/lib/Analysis/ConstantFolding.cpp
@@ -894,6 +894,12 @@ static Constant *SymbolicallyEvaluateGEP(Type *SrcTy, ArrayRef<Constant *> Ops,
 /// and stores, which have no constant expression form.
 Constant *llvm::ConstantFoldInstruction(Instruction *I, const DataLayout &DL,
                                         const TargetLibraryInfo *TLI) {
+  FastMathFlags FMF;
+
+  if (isa<FPMathOperator>(I)) {
+    FMF = I->getFastMathFlags();
+  }
+
   // Handle PHI nodes quickly here...
   if (PHINode *PN = dyn_cast<PHINode>(I)) {
     Constant *CommonValue = nullptr;
@@ -911,7 +917,7 @@ Constant *llvm::ConstantFoldInstruction(Instruction *I, const DataLayout &DL,
         return nullptr;
       // Fold the PHI's operands.
       if (ConstantExpr *NewC = dyn_cast<ConstantExpr>(C))
-        C = ConstantFoldConstantExpression(NewC, DL, TLI);
+        C = ConstantFoldConstantExpression(NewC, DL, TLI, FMF);
       // If the incoming value is a different constant to
       // the one we saw previously, then give up.
       if (CommonValue && C != CommonValue)
@@ -934,7 +940,7 @@ Constant *llvm::ConstantFoldInstruction(Instruction *I, const DataLayout &DL,
 
     // Fold the Instruction's operands.
     if (ConstantExpr *NewCE = dyn_cast<ConstantExpr>(Op))
-      Op = ConstantFoldConstantExpression(NewCE, DL, TLI);
+      Op = ConstantFoldConstantExpression(NewCE, DL, TLI, FMF);
 
     Ops.push_back(Op);
   }
@@ -959,13 +965,15 @@ Constant *llvm::ConstantFoldInstruction(Instruction *I, const DataLayout &DL,
                                     EVI->getIndices());
   }
 
-  return ConstantFoldInstOperands(I->getOpcode(), I->getType(), Ops, DL, TLI);
+  return ConstantFoldInstOperands(I->getOpcode(), I->getType(), Ops, DL, TLI,
+                                  FMF);
 }
 
 static Constant *
 ConstantFoldConstantExpressionImpl(const ConstantExpr *CE, const DataLayout &DL,
                                    const TargetLibraryInfo *TLI,
-                                   SmallPtrSetImpl<ConstantExpr *> &FoldedOps) {
+                                   SmallPtrSetImpl<ConstantExpr *> &FoldedOps,
+                                   FastMathFlags FMF) {
   SmallVector<Constant *, 8> Ops;
   for (User::const_op_iterator i = CE->op_begin(), e = CE->op_end(); i != e;
        ++i) {
@@ -974,7 +982,8 @@ ConstantFoldConstantExpressionImpl(const ConstantExpr *CE, const DataLayout &DL,
     // a ConstantExpr, we don't have to process it again.
     if (ConstantExpr *NewCE = dyn_cast<ConstantExpr>(NewC)) {
       if (FoldedOps.insert(NewCE).second)
-        NewC = ConstantFoldConstantExpressionImpl(NewCE, DL, TLI, FoldedOps);
+        NewC = ConstantFoldConstantExpressionImpl(NewCE, DL, TLI, FoldedOps,
+                                                  FMF);
     }
     Ops.push_back(NewC);
   }
@@ -982,7 +991,8 @@ ConstantFoldConstantExpressionImpl(const ConstantExpr *CE, const DataLayout &DL,
   if (CE->isCompare())
     return ConstantFoldCompareInstOperands(CE->getPredicate(), Ops[0], Ops[1],
                                            DL, TLI);
-  return ConstantFoldInstOperands(CE->getOpcode(), CE->getType(), Ops, DL, TLI);
+  return ConstantFoldInstOperands(CE->getOpcode(), CE->getType(), Ops, DL, TLI,
+                                  FMF);
 }
 
 /// Attempt to fold the constant expression
@@ -990,9 +1000,10 @@ ConstantFoldConstantExpressionImpl(const ConstantExpr *CE, const DataLayout &DL,
 /// result is returned, if not, null is returned.
 Constant *llvm::ConstantFoldConstantExpression(const ConstantExpr *CE,
                                                const DataLayout &DL,
-                                               const TargetLibraryInfo *TLI) {
+                                               const TargetLibraryInfo *TLI,
+                                               FastMathFlags FMF) {
   SmallPtrSet<ConstantExpr *, 4> FoldedOps;
-  return ConstantFoldConstantExpressionImpl(CE, DL, TLI, FoldedOps);
+  return ConstantFoldConstantExpressionImpl(CE, DL, TLI, FoldedOps, FMF);
 }
 
 /// Attempt to constant fold an instruction with the
@@ -1008,7 +1019,8 @@ Constant *llvm::ConstantFoldConstantExpression(const ConstantExpr *CE,
 Constant *llvm::ConstantFoldInstOperands(unsigned Opcode, Type *DestTy,
                                          ArrayRef<Constant *> Ops,
                                          const DataLayout &DL,
-                                         const TargetLibraryInfo *TLI) {
+                                         const TargetLibraryInfo *TLI,
+                                         FastMathFlags FMF) {
   // Handle easy binops first.
   if (Instruction::isBinaryOp(Opcode)) {
     if (isa<ConstantExpr>(Ops[0]) || isa<ConstantExpr>(Ops[1])) {
@@ -1016,7 +1028,7 @@ Constant *llvm::ConstantFoldInstOperands(unsigned Opcode, Type *DestTy,
         return C;
     }
 
-    return ConstantExpr::get(Opcode, Ops[0], Ops[1]);
+    return ConstantExpr::get(Opcode, Ops[0], Ops[1], 0, nullptr, FMF);
   }
 
   switch (Opcode) {
diff --git a/lib/IR/ConstantFold.cpp b/lib/IR/ConstantFold.cpp
index 721c96d..c33a7f4 100644
--- a/lib/IR/ConstantFold.cpp
+++ b/lib/IR/ConstantFold.cpp
@@ -916,8 +916,7 @@ Constant *llvm::ConstantFoldInsertValueInstruction(Constant *Agg,
 
 Constant *llvm::ConstantFoldBinaryInstruction(unsigned Opcode,
                                               Constant *C1, Constant *C2,
-                                              bool KeepExceptions,
-                                              bool KeepRounding) {
+                                              FastMathFlags FMF) {
   // Handle UndefValue up front.
   if (isa<UndefValue>(C1) || isa<UndefValue>(C2)) {
     switch (Opcode) {
@@ -1101,8 +1100,7 @@ Constant *llvm::ConstantFoldBinaryInstruction(unsigned Opcode,
   } else if (isa<ConstantInt>(C1)) {
     // If C1 is a ConstantInt and C2 is not, swap the operands.
     if (Instruction::isCommutative(Opcode))
-      return ConstantExpr::get(Opcode, C2, C1, 0, nullptr, KeepExceptions,
-                               KeepRounding);
+      return ConstantExpr::get(Opcode, C2, C1, 0, nullptr, FMF);
   }
 
   // At this point we know neither constant is an UndefValue.
@@ -1180,32 +1178,32 @@ Constant *llvm::ConstantFoldBinaryInstruction(unsigned Opcode,
         break;
       case Instruction::FAdd:
         S = C3V.add(C2V, APFloat::rmNearestTiesToEven);
-        if ((KeepExceptions && S != APFloat::opOK) ||
-            (KeepRounding && (S & APFloat::opInexact)))
+        if ((FMF.keepExceptions() && S != APFloat::opOK) ||
+            (FMF.keepRounding() && (S & APFloat::opInexact)))
           break;
         return ConstantFP::get(C1->getContext(), C3V);
       case Instruction::FSub:
         S = C3V.subtract(C2V, APFloat::rmNearestTiesToEven);
-        if ((KeepExceptions && S != APFloat::opOK) ||
-            (KeepRounding && (S & APFloat::opInexact)))
+        if ((FMF.keepExceptions() && S != APFloat::opOK) ||
+            (FMF.keepRounding() && (S & APFloat::opInexact)))
           break;
         return ConstantFP::get(C1->getContext(), C3V);
       case Instruction::FMul:
         S = C3V.multiply(C2V, APFloat::rmNearestTiesToEven);
-        if ((KeepExceptions && S != APFloat::opOK) ||
-            (KeepRounding && (S & APFloat::opInexact)))
+        if ((FMF.keepExceptions() && S != APFloat::opOK) ||
+            (FMF.keepRounding() && (S & APFloat::opInexact)))
           break;
         return ConstantFP::get(C1->getContext(), C3V);
       case Instruction::FDiv:
         S = C3V.divide(C2V, APFloat::rmNearestTiesToEven);
-        if ((KeepExceptions && S != APFloat::opOK) ||
-            (KeepRounding && (S & APFloat::opInexact)))
+        if ((FMF.keepExceptions() && S != APFloat::opOK) ||
+            (FMF.keepRounding() && (S & APFloat::opInexact)))
           break;
         return ConstantFP::get(C1->getContext(), C3V);
       case Instruction::FRem:
         S = C3V.mod(C2V);
-        if ((KeepExceptions && S != APFloat::opOK) ||
-            (KeepRounding && (S & APFloat::opInexact)))
+        if ((FMF.keepExceptions() && S != APFloat::opOK) ||
+            (FMF.keepRounding() && (S & APFloat::opInexact)))
           break;
         return ConstantFP::get(C1->getContext(), C3V);
       }
@@ -1220,8 +1218,7 @@ Constant *llvm::ConstantFoldBinaryInstruction(unsigned Opcode,
       Constant *RHS =
         ConstantExpr::getExtractElement(C2, ConstantInt::get(Ty, i));
 
-      Result.push_back(ConstantExpr::get(Opcode, LHS, RHS, 0, nullptr,
-                                         KeepExceptions, KeepRounding));
+      Result.push_back(ConstantExpr::get(Opcode, LHS, RHS, 0, nullptr, FMF));
     }
 
     return ConstantVector::get(Result);
@@ -1236,17 +1233,16 @@ Constant *llvm::ConstantFoldBinaryInstruction(unsigned Opcode,
     // (a + (b + c)).
     if (Instruction::isAssociative(Opcode) && CE1->getOpcode() == Opcode) {
       Constant *T = ConstantExpr::get(Opcode, CE1->getOperand(1), C2, 0,
-                                      nullptr, KeepExceptions, KeepRounding);
+                                      nullptr, FMF);
       if (!isa<ConstantExpr>(T) || cast<ConstantExpr>(T)->getOpcode() != Opcode)
         return ConstantExpr::get(Opcode, CE1->getOperand(0), T, 0, nullptr,
-                                 KeepExceptions, KeepRounding);
+                                 FMF);
     }
   } else if (isa<ConstantExpr>(C2)) {
     // If C2 is a constant expr and C1 isn't, flop them around and fold the
     // other way if possible.
     if (Instruction::isCommutative(Opcode))
-      return ConstantFoldBinaryInstruction(Opcode, C2, C1, KeepExceptions,
-                                           KeepRounding);
+      return ConstantFoldBinaryInstruction(Opcode, C2, C1, FMF);
   }
 
   // i1 can be simplified in many cases.
diff --git a/lib/IR/ConstantFold.h b/lib/IR/ConstantFold.h
index 8e35aa0..a0dd5b1 100644
--- a/lib/IR/ConstantFold.h
+++ b/lib/IR/ConstantFold.h
@@ -24,6 +24,7 @@
 namespace llvm {
   class Value;
   class Constant;
+  class FastMathFlags;
   class Type;
 
   // Constant fold various types of instruction...
@@ -44,8 +45,7 @@ namespace llvm {
   Constant *ConstantFoldInsertValueInstruction(Constant *Agg, Constant *Val,
                                                ArrayRef<unsigned> Idxs);
   Constant *ConstantFoldBinaryInstruction(unsigned Opcode, Constant *V1,
-                                          Constant *V2, bool KeepFPExceptions,
-                                          bool KeepFPRounding);
+                                          Constant *V2, FastMathFlags FMF);
   Constant *ConstantFoldCompareInstruction(unsigned short predicate, 
                                            Constant *C1, Constant *C2);
   Constant *ConstantFoldGetElementPtr(Constant *C, bool inBounds,
diff --git a/lib/IR/Constants.cpp b/lib/IR/Constants.cpp
index c52c185..9e40f8c 100644
--- a/lib/IR/Constants.cpp
+++ b/lib/IR/Constants.cpp
@@ -1871,7 +1871,7 @@ Constant *ConstantExpr::getAddrSpaceCast(Constant *C, Type *DstTy,
 
 Constant *ConstantExpr::get(unsigned Opcode, Constant *C1, Constant *C2,
                             unsigned Flags, Type *OnlyIfReducedTy,
-                            bool KeepExceptions, bool KeepRounding) {
+                            FastMathFlags FMF) {
   // Check the operands for consistency first.
   assert(Opcode >= Instruction::BinaryOpsBegin &&
          Opcode <  Instruction::BinaryOpsEnd   &&
@@ -1937,9 +1937,7 @@ Constant *ConstantExpr::get(unsigned Opcode, Constant *C1, Constant *C2,
   }
 #endif
 
-  if (Constant *FC = ConstantFoldBinaryInstruction(Opcode, C1, C2,
-                                                   KeepExceptions,
-                                                   KeepRounding)) {
+  if (Constant *FC = ConstantFoldBinaryInstruction(Opcode, C1, C2, FMF)) {
     return FC;          // Fold a few common cases.
   }
 
@@ -2270,8 +2268,8 @@ Constant *ConstantExpr::getAdd(Constant *C1, Constant *C2,
   return get(Instruction::Add, C1, C2, Flags);
 }
 
-Constant *ConstantExpr::getFAdd(Constant *C1, Constant *C2) {
-  return get(Instruction::FAdd, C1, C2);
+Constant *ConstantExpr::getFAdd(Constant *C1, Constant *C2, FastMathFlags FMF) {
+  return get(Instruction::FAdd, C1, C2, 0, nullptr, FMF);
 }
 
 Constant *ConstantExpr::getSub(Constant *C1, Constant *C2,
@@ -2281,19 +2279,19 @@ Constant *ConstantExpr::getSub(Constant *C1, Constant *C2,
   return get(Instruction::Sub, C1, C2, Flags);
 }
 
-Constant *ConstantExpr::getFSub(Constant *C1, Constant *C2) {
-  return get(Instruction::FSub, C1, C2);
+Constant *ConstantExpr::getFSub(Constant *C1, Constant *C2, FastMathFlags FMF) {
+  return get(Instruction::FSub, C1, C2, 0, nullptr, FMF);
 }
 
 Constant *ConstantExpr::getMul(Constant *C1, Constant *C2,
                                bool HasNUW, bool HasNSW) {
   unsigned Flags = (HasNUW ? OverflowingBinaryOperator::NoUnsignedWrap : 0) |
                    (HasNSW ? OverflowingBinaryOperator::NoSignedWrap   : 0);
-  return get(Instruction::Mul, C1, C2, Flags);
+  return get(Instruction::Mul, C1, C2, Flags, nullptr);
 }
 
-Constant *ConstantExpr::getFMul(Constant *C1, Constant *C2) {
-  return get(Instruction::FMul, C1, C2);
+Constant *ConstantExpr::getFMul(Constant *C1, Constant *C2, FastMathFlags FMF) {
+  return get(Instruction::FMul, C1, C2, 0, nullptr, FMF);
 }
 
 Constant *ConstantExpr::getUDiv(Constant *C1, Constant *C2, bool isExact) {
@@ -2306,8 +2304,8 @@ Constant *ConstantExpr::getSDiv(Constant *C1, Constant *C2, bool isExact) {
              isExact ? PossiblyExactOperator::IsExact : 0);
 }
 
-Constant *ConstantExpr::getFDiv(Constant *C1, Constant *C2) {
-  return get(Instruction::FDiv, C1, C2);
+Constant *ConstantExpr::getFDiv(Constant *C1, Constant *C2, FastMathFlags FMF) {
+  return get(Instruction::FDiv, C1, C2, 0, nullptr, FMF);
 }
 
 Constant *ConstantExpr::getURem(Constant *C1, Constant *C2) {
@@ -2318,8 +2316,8 @@ Constant *ConstantExpr::getSRem(Constant *C1, Constant *C2) {
   return get(Instruction::SRem, C1, C2);
 }
 
-Constant *ConstantExpr::getFRem(Constant *C1, Constant *C2) {
-  return get(Instruction::FRem, C1, C2);
+Constant *ConstantExpr::getFRem(Constant *C1, Constant *C2, FastMathFlags FMF) {
+  return get(Instruction::FRem, C1, C2, 0, nullptr, FMF);
 }
 
 Constant *ConstantExpr::getAnd(Constant *C1, Constant *C2) {
diff --git a/test/Other/fpenv-constant-fold.ll b/test/Other/fpenv-constant-fold.ll
new file mode 100644
index 0000000..01dbfa0
--- /dev/null
+++ b/test/Other/fpenv-constant-fold.ll
@@ -0,0 +1,130 @@
+; InstCombine is used just for its calls to constant folder.
+; RUN: opt -S -instcombine -o - < %s | FileCheck %s
+
+; Target independent constant folder should not fold floating-point operations
+; that have constant expression as their operands when FEnv access is requested
+; and such folding leads to differences in observable effects at runtime.
+
+define double @do-not-fold-fadd-that-can-trap() {
+; CHECK-LABEL: @do-not-fold-fadd-that-can-trap
+; CHECK: fadd
+entry:
+  %val = fadd kexc double 1.000000e+308, 1.000000e+308
+  ret double %val
+}
+
+define double @do-not-fold-fsub-that-can-trap() {
+; CHECK-LABEL: @do-not-fold-fsub-that-can-trap
+; CHECK: fsub
+entry:
+  %val = fsub kexc double 1.000000e-308, 1.000000e+308
+  ret double %val
+}
+
+define double @do-not-fold-fmul-that-can-trap() {
+; CHECK-LABEL: @do-not-fold-fmul-that-can-trap
+; CHECK: fmul
+entry:
+  %val = fmul kexc double 1.000000e+300, 1.000000e+300
+  ret double %val
+}
+
+define double @do-not-fold-fdiv-that-can-trap() {
+; CHECK-LABEL: @do-not-fold-fdiv-that-can-trap
+; CHECK: fdiv
+entry:
+  %val = fdiv kexc double 1.000000e+300, 1.000000e-300
+  ret double %val
+}
+
+define double @do-not-fold-frem-that-can-trap() {
+; CHECK-LABEL: @do-not-fold-frem-that-can-trap
+; CHECK: frem
+entry:
+  %val = frem kexc double 1.000000e+300, 1.000000e-300
+  ret double %val
+}
+
+define double @do-not-fold-fadd-because-of-rounding() {
+; CHECK-LABEL: @do-not-fold-fadd-because-of-rounding
+; CHECK: fadd
+entry:
+  %val = fadd kround double 0.101, 0.93
+  ret double %val
+}
+
+define double @do-not-fold-fsub-because-of-rounding() {
+; CHECK-LABEL: @do-not-fold-fsub-because-of-rounding
+; CHECK: fsub
+entry:
+  %val = fsub kround double 0.101, 0.93
+  ret double %val
+}
+
+define double @do-not-fold-fmul-because-of-rounding() {
+; CHECK-LABEL: @do-not-fold-fmul-because-of-rounding
+; CHECK: fmul
+entry:
+  %val = fmul kround double 0.101, 0.93
+  ret double %val
+}
+
+define double @do-not-fold-fdiv-because-of-rounding() {
+; CHECK-LABEL: @do-not-fold-fdiv-because-of-rounding
+; CHECK: fdiv
+entry:
+  %val = fdiv kround double 0.101, 0.93
+  ret double %val
+}
+
+; "frem" instruction falls into the same category, but none of tried test-cases
+; could raised inexact exception.
+
+define double @fold-fadd-that-cant-trap() {
+; CHECK-LABEL: @fold-fadd-that-cant-trap
+; CHECK-NOT: fadd
+entry:
+  %val = fadd kexc kround double 1.0, 1.0
+  ret double %val
+}
+
+define double @fold-fsub-that-cant-trap() {
+; CHECK-LABEL: @fold-fsub-that-cant-trap
+; CHECK-NOT: fsub
+entry:
+  %val = fsub kexc kround double 1.0, 1.0
+  ret double %val
+}
+
+define double @fold-fmul-that-cant-trap() {
+; CHECK-LABEL: @fold-fmul-that-cant-trap
+; CHECK-NOT: fmul
+entry:
+  %val = fmul kexc kround double 1.0, 1.0
+  ret double %val
+}
+
+define double @fold-fdiv-that-cant-trap() {
+; CHECK-LABEL: @fold-fdiv-that-cant-trap
+; CHECK-NOT: fdiv
+entry:
+  %val = fdiv kexc kround double 1.0, 1.0
+  ret double %val
+}
+
+define double @fold-frem-that-cant-trap() {
+; CHECK-LABEL: @fold-frem-that-cant-trap
+; CHECK-NOT: frem
+entry:
+  %val = frem kexc kround double 1.0, 1.0
+  ret double %val
+}
+
+define double @correcly-handle-nested-expressions() {
+; CHECK-LABEL: @correcly-handle-nested-expressions
+; CHECK: fmul
+entry:
+  %val1 = fmul kexc double 1.000000e+300, 1.000000e+300
+  %val2 = fdiv kexc double %val1, 1.000000e+300
+  ret double %val2
+}
-- 
2.3.5

-------------- next part --------------
>From 3d4f471cd90fb091b1d9c119dc59943510d362ff Mon Sep 17 00:00:00 2001
From: Sergey Dmitrouk <sdmitrouk at accesssoftek.com>
Date: Wed, 26 Nov 2014 19:50:50 +0200
Subject: [PATCH 06/13] Do not fold constants on reading in IR asm/bitcode

---
 lib/AsmParser/LLParser.cpp              |  8 +++++++-
 lib/Bitcode/Reader/BitcodeReader.cpp    |  5 ++++-
 test/Assembler/do-not-fold-fp-consts.ll | 36 +++++++++++++++++++++++++++++++++
 3 files changed, 47 insertions(+), 2 deletions(-)
 create mode 100644 test/Assembler/do-not-fold-fp-consts.ll

diff --git a/lib/AsmParser/LLParser.cpp b/lib/AsmParser/LLParser.cpp
index 02b4b75..4c7e48f 100644
--- a/lib/AsmParser/LLParser.cpp
+++ b/lib/AsmParser/LLParser.cpp
@@ -3004,11 +3004,17 @@ bool LLParser::ParseValID(ValID &ID, PerFunctionState *PFS) {
       break;
     default: llvm_unreachable("Unknown binary operator!");
     }
+
     unsigned Flags = 0;
     if (NUW)   Flags |= OverflowingBinaryOperator::NoUnsignedWrap;
     if (NSW)   Flags |= OverflowingBinaryOperator::NoSignedWrap;
     if (Exact) Flags |= PossiblyExactOperator::IsExact;
-    Constant *C = ConstantExpr::get(Opc, Val0, Val1, Flags);
+
+    FastMathFlags FMF;
+    FMF.setKeepExceptions();
+    FMF.setKeepRounding();
+
+    Constant *C = ConstantExpr::get(Opc, Val0, Val1, Flags, nullptr, FMF);
     ID.ConstantVal = C;
     ID.Kind = ValID::t_Constant;
     return false;
diff --git a/lib/Bitcode/Reader/BitcodeReader.cpp b/lib/Bitcode/Reader/BitcodeReader.cpp
index e004f34..89369a4 100644
--- a/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -2547,7 +2547,10 @@ std::error_code BitcodeReader::parseConstants() {
               Flags |= SDivOperator::IsExact;
           }
         }
-        V = ConstantExpr::get(Opc, LHS, RHS, Flags);
+        FastMathFlags FMF;
+        FMF.setKeepExceptions();
+        FMF.setKeepRounding();
+        V = ConstantExpr::get(Opc, LHS, RHS, Flags, nullptr, FMF);
       }
       break;
     }
diff --git a/test/Assembler/do-not-fold-fp-consts.ll b/test/Assembler/do-not-fold-fp-consts.ll
new file mode 100644
index 0000000..6b2ddcb
--- /dev/null
+++ b/test/Assembler/do-not-fold-fp-consts.ll
@@ -0,0 +1,36 @@
+; RUN: llvm-as < %s | llvm-dis | FileCheck %s
+
+define double @do-not-fold-fadd() {
+; CHECK-LABEL: @do-not-fold-fadd
+; CHECK: fadd
+entry:
+  ret double fadd (double 1.000000e+308, double 1.000000e+308)
+}
+
+define double @do-not-fold-fsub() {
+; CHECK-LABEL: @do-not-fold-fsub
+; CHECK: fsub
+entry:
+  ret double fsub (double 1.000000e-308, double 1.000000e+308)
+}
+
+define double @do-not-fold-fmul() {
+; CHECK-LABEL: @do-not-fold-fmul
+; CHECK: fmul
+entry:
+  ret double fmul (double 1.000000e+300, double 1.000000e+300)
+}
+
+define double @do-not-fold-fdiv() {
+; CHECK-LABEL: @do-not-fold-fdiv
+; CHECK: fdiv
+entry:
+  ret double fdiv (double 1.000000e+300, double 1.000000e-300)
+}
+
+define double @do-not-fold-frem() {
+; CHECK-LABEL: @do-not-fold-frem
+; CHECK: frem
+entry:
+  ret double frem (double 1.000000e+300, double 1.000000e-300)
+}
-- 
2.3.5

-------------- next part --------------
>From 42e2b55c8acc1c047af793455288d690a5231931 Mon Sep 17 00:00:00 2001
From: Sergey Dmitrouk <sdmitrouk at accesssoftek.com>
Date: Fri, 28 Nov 2014 12:58:45 +0200
Subject: [PATCH 07/13] Prevent undesired folding by InstSimplify

---
 lib/Analysis/InstructionSimplify.cpp      |  6 ++--
 test/Transforms/InstSimplify/fast-math.ll | 48 +++++++++++++++++++++++++++++++
 2 files changed, 51 insertions(+), 3 deletions(-)

diff --git a/lib/Analysis/InstructionSimplify.cpp b/lib/Analysis/InstructionSimplify.cpp
index c8a13c7..fd06ad7 100644
--- a/lib/Analysis/InstructionSimplify.cpp
+++ b/lib/Analysis/InstructionSimplify.cpp
@@ -791,7 +791,7 @@ static Value *SimplifyFAddInst(Value *Op0, Value *Op1, FastMathFlags FMF,
     if (Constant *CRHS = dyn_cast<Constant>(Op1)) {
       Constant *Ops[] = { CLHS, CRHS };
       return ConstantFoldInstOperands(Instruction::FAdd, CLHS->getType(),
-                                      Ops, Q.DL, Q.TLI);
+                                      Ops, Q.DL, Q.TLI, FMF);
     }
 
     // Canonicalize the constant to the RHS.
@@ -833,7 +833,7 @@ static Value *SimplifyFSubInst(Value *Op0, Value *Op1, FastMathFlags FMF,
     if (Constant *CRHS = dyn_cast<Constant>(Op1)) {
       Constant *Ops[] = { CLHS, CRHS };
       return ConstantFoldInstOperands(Instruction::FSub, CLHS->getType(),
-                                      Ops, Q.DL, Q.TLI);
+                                      Ops, Q.DL, Q.TLI, FMF);
     }
   }
 
@@ -871,7 +871,7 @@ static Value *SimplifyFMulInst(Value *Op0, Value *Op1,
     if (Constant *CRHS = dyn_cast<Constant>(Op1)) {
       Constant *Ops[] = { CLHS, CRHS };
       return ConstantFoldInstOperands(Instruction::FMul, CLHS->getType(),
-                                      Ops, Q.DL, Q.TLI);
+                                      Ops, Q.DL, Q.TLI, FMF);
     }
 
     // Canonicalize the constant to the RHS.
diff --git a/test/Transforms/InstSimplify/fast-math.ll b/test/Transforms/InstSimplify/fast-math.ll
index 90532fa..ba26a45 100644
--- a/test/Transforms/InstSimplify/fast-math.ll
+++ b/test/Transforms/InstSimplify/fast-math.ll
@@ -171,3 +171,51 @@ define float @fdiv_neg_swapped2(float %f) {
 ; CHECK-LABEL: fdiv_neg_swapped2
 ; CHECK: ret float -1.000000e+00
 }
+
+; CHECK-LABEL: @nofold_fadd_kexc(
+define double @nofold_fadd_kexc() {
+; CHECK: fadd
+entry:
+  %val = fadd kexc double 1.000000e+308, 1.000000e+308
+  ret double %val
+}
+
+; CHECK-LABEL: @nofold_fsub_kexc(
+define double @nofold_fsub_kexc() {
+; CHECK: fsub
+entry:
+  %val = fsub kexc double 1.000000e-308, 1.000000e+308
+  ret double %val
+}
+
+; CHECK-LABEL: @nofold_fmul_kexc(
+define double @nofold_fmul_kexc() {
+; CHECK: fmul
+entry:
+  %val = fmul kexc double 1.000000e+300, 1.000000e+300
+  ret double %val
+}
+
+; CHECK-LABEL: @nofold_fadd_kround(
+define double @nofold_fadd_kround() {
+; CHECK: fadd
+entry:
+  %val = fadd kround double 1.000000e+308, 1.000000e+308
+  ret double %val
+}
+
+; CHECK-LABEL: @nofold_fsub_kround(
+define double @nofold_fsub_kround() {
+; CHECK: fsub
+entry:
+  %val = fsub kround double 1.000000e-308, 1.000000e+308
+  ret double %val
+}
+
+; CHECK-LABEL: @nofold_fmul_kround(
+define double @nofold_fmul_kround() {
+; CHECK: fmul
+entry:
+  %val = fmul kround double 1.000000e+300, 1.000000e+300
+  ret double %val
+}
-- 
2.3.5

-------------- next part --------------
>From 2646364257850cc1f2aa4f2e7d41d044dda3a4b6 Mon Sep 17 00:00:00 2001
From: Sergey Dmitrouk <sdmitrouk at accesssoftek.com>
Date: Fri, 28 Nov 2014 15:53:44 +0200
Subject: [PATCH 08/13] Do not simplify expressions with FPEnv access

ConstantExpression can't store these flags, which can lead to
going against them.
---
 include/llvm/IR/Constants.h       |  3 ++-
 lib/Analysis/ConstantFolding.cpp  |  3 ++-
 lib/IR/Constants.cpp              |  8 +++++++-
 test/Transforms/EarlyCSE/fpenv.ll | 24 ++++++++++++++++++++++++
 4 files changed, 35 insertions(+), 3 deletions(-)
 create mode 100644 test/Transforms/EarlyCSE/fpenv.ll

diff --git a/include/llvm/IR/Constants.h b/include/llvm/IR/Constants.h
index 432fa6c..940985b 100644
--- a/include/llvm/IR/Constants.h
+++ b/include/llvm/IR/Constants.h
@@ -1076,7 +1076,8 @@ public:
   /// \param OnlyIfReducedTy see \a getWithOperands() docs.
   static Constant *get(unsigned Opcode, Constant *C1, Constant *C2,
                        unsigned Flags = 0, Type *OnlyIfReducedTy = nullptr,
-                       FastMathFlags FMF = FastMathFlags());
+                       FastMathFlags FMF = FastMathFlags(),
+                       bool Strict = false);
 
   /// \brief Return an ICmp or FCmp comparison operator constant expression.
   ///
diff --git a/lib/Analysis/ConstantFolding.cpp b/lib/Analysis/ConstantFolding.cpp
index e17dbed..89f3c96 100644
--- a/lib/Analysis/ConstantFolding.cpp
+++ b/lib/Analysis/ConstantFolding.cpp
@@ -1028,7 +1028,8 @@ Constant *llvm::ConstantFoldInstOperands(unsigned Opcode, Type *DestTy,
         return C;
     }
 
-    return ConstantExpr::get(Opcode, Ops[0], Ops[1], 0, nullptr, FMF);
+    return ConstantExpr::get(Opcode, Ops[0], Ops[1], 0, nullptr,
+                             FMF, /* Strict = */ true);
   }
 
   switch (Opcode) {
diff --git a/lib/IR/Constants.cpp b/lib/IR/Constants.cpp
index 9e40f8c..abd9507 100644
--- a/lib/IR/Constants.cpp
+++ b/lib/IR/Constants.cpp
@@ -1871,7 +1871,8 @@ Constant *ConstantExpr::getAddrSpaceCast(Constant *C, Type *DstTy,
 
 Constant *ConstantExpr::get(unsigned Opcode, Constant *C1, Constant *C2,
                             unsigned Flags, Type *OnlyIfReducedTy,
-                            FastMathFlags FMF) {
+                            FastMathFlags FMF,
+                            bool Strict) {
   // Check the operands for consistency first.
   assert(Opcode >= Instruction::BinaryOpsBegin &&
          Opcode <  Instruction::BinaryOpsEnd   &&
@@ -1944,6 +1945,11 @@ Constant *ConstantExpr::get(unsigned Opcode, Constant *C1, Constant *C2,
   if (OnlyIfReducedTy == C1->getType())
     return nullptr;
 
+  // ConstantExpression can't store these flags, which can lead to going against
+  // them.
+  if (Strict && (FMF.keepExceptions() || FMF.keepRounding()))
+    return nullptr;
+
   Constant *ArgVec[] = { C1, C2 };
   ConstantExprKeyType Key(Opcode, ArgVec, 0, Flags);
 
diff --git a/test/Transforms/EarlyCSE/fpenv.ll b/test/Transforms/EarlyCSE/fpenv.ll
new file mode 100644
index 0000000..aba632c
--- /dev/null
+++ b/test/Transforms/EarlyCSE/fpenv.ll
@@ -0,0 +1,24 @@
+; %val.true and %val.false should not be moved inside phi-node as constant
+; expressions as such transformation looses fast-math flags.
+; RUN: opt < %s -S -early-cse | FileCheck %s
+
+define double @do-not-turn-into-constexpr(double %x) {
+; CHECK-LABEL: @do-not-turn-into-constexpr(
+; CHECK: %val.true = fmul
+; CHECK: %val.false = fmul
+entry:
+  %cmp = fcmp oeq double %x, 0.000000e+00
+  br i1 %cmp, label %cond.true, label %cond.false
+
+cond.true:                                        ; preds = %entry
+  %val.true = fmul kexc double 1.000000e+300, 1.000000e+300
+  br label %cond.end
+
+cond.false:                                       ; preds = %entry
+  %val.false = fmul kexc double 1.000000e-300, 1.000000e-300
+  br label %cond.end
+
+cond.end:                                         ; preds = %cond.false, %cond.true
+  %cond = phi double [ %val.true, %cond.true ], [ %val.false, %cond.false ]
+  ret double %cond
+}
-- 
2.3.5

-------------- next part --------------
>From 084a71af4013d22186d46d4393c3c8814c921273 Mon Sep 17 00:00:00 2001
From: Sergey Dmitrouk <sdmitrouk at accesssoftek.com>
Date: Fri, 12 Dec 2014 16:35:01 +0200
Subject: [PATCH 09/13] Make Strict flag available for more clients

---
 include/llvm/Analysis/TargetFolder.h | 25 +++++++++++++++----------
 include/llvm/IR/ConstantFolder.h     | 25 +++++++++++++++----------
 include/llvm/IR/Constants.h          | 15 ++++++++++-----
 lib/IR/Constants.cpp                 | 25 +++++++++++++++----------
 4 files changed, 55 insertions(+), 35 deletions(-)

diff --git a/include/llvm/Analysis/TargetFolder.h b/include/llvm/Analysis/TargetFolder.h
index befdff5..291ad59 100644
--- a/include/llvm/Analysis/TargetFolder.h
+++ b/include/llvm/Analysis/TargetFolder.h
@@ -53,24 +53,27 @@ public:
     return Fold(ConstantExpr::getAdd(LHS, RHS, HasNUW, HasNSW));
   }
   Constant *CreateFAdd(Constant *LHS, Constant *RHS,
-                       FastMathFlags FMF = FastMathFlags()) const {
-    return Fold(ConstantExpr::getFAdd(LHS, RHS, FMF));
+                       FastMathFlags FMF = FastMathFlags(),
+                       bool Strict = false) const {
+    return Fold(ConstantExpr::getFAdd(LHS, RHS, FMF, Strict));
   }
   Constant *CreateSub(Constant *LHS, Constant *RHS,
                       bool HasNUW = false, bool HasNSW = false) const {
     return Fold(ConstantExpr::getSub(LHS, RHS, HasNUW, HasNSW));
   }
   Constant *CreateFSub(Constant *LHS, Constant *RHS,
-                       FastMathFlags FMF = FastMathFlags()) const {
-    return Fold(ConstantExpr::getFSub(LHS, RHS, FMF));
+                       FastMathFlags FMF = FastMathFlags(),
+                       bool Strict = false) const {
+    return Fold(ConstantExpr::getFSub(LHS, RHS, FMF, Strict));
   }
   Constant *CreateMul(Constant *LHS, Constant *RHS,
                       bool HasNUW = false, bool HasNSW = false) const {
     return Fold(ConstantExpr::getMul(LHS, RHS, HasNUW, HasNSW));
   }
   Constant *CreateFMul(Constant *LHS, Constant *RHS,
-                       FastMathFlags FMF = FastMathFlags()) const {
-    return Fold(ConstantExpr::getFMul(LHS, RHS, FMF));
+                       FastMathFlags FMF = FastMathFlags(),
+                       bool Strict = false) const {
+    return Fold(ConstantExpr::getFMul(LHS, RHS, FMF, Strict));
   }
   Constant *CreateUDiv(Constant *LHS, Constant *RHS, bool isExact = false)const{
     return Fold(ConstantExpr::getUDiv(LHS, RHS, isExact));
@@ -79,8 +82,9 @@ public:
     return Fold(ConstantExpr::getSDiv(LHS, RHS, isExact));
   }
   Constant *CreateFDiv(Constant *LHS, Constant *RHS,
-                       FastMathFlags FMF = FastMathFlags()) const {
-    return Fold(ConstantExpr::getFDiv(LHS, RHS, FMF));
+                       FastMathFlags FMF = FastMathFlags(),
+                       bool Strict = false) const {
+    return Fold(ConstantExpr::getFDiv(LHS, RHS, FMF, Strict));
   }
   Constant *CreateURem(Constant *LHS, Constant *RHS) const {
     return Fold(ConstantExpr::getURem(LHS, RHS));
@@ -89,8 +93,9 @@ public:
     return Fold(ConstantExpr::getSRem(LHS, RHS));
   }
   Constant *CreateFRem(Constant *LHS, Constant *RHS,
-                       FastMathFlags FMF = FastMathFlags()) const {
-    return Fold(ConstantExpr::getFRem(LHS, RHS, FMF));
+                       FastMathFlags FMF = FastMathFlags(),
+                       bool Strict = false) const {
+    return Fold(ConstantExpr::getFRem(LHS, RHS, FMF, Strict));
   }
   Constant *CreateShl(Constant *LHS, Constant *RHS,
                       bool HasNUW = false, bool HasNSW = false) const {
diff --git a/include/llvm/IR/ConstantFolder.h b/include/llvm/IR/ConstantFolder.h
index 60909ea..a10e4a0 100644
--- a/include/llvm/IR/ConstantFolder.h
+++ b/include/llvm/IR/ConstantFolder.h
@@ -37,24 +37,27 @@ public:
     return ConstantExpr::getAdd(LHS, RHS, HasNUW, HasNSW);
   }
   Constant *CreateFAdd(Constant *LHS, Constant *RHS,
-                       FastMathFlags FMF = FastMathFlags()) const {
-    return ConstantExpr::getFAdd(LHS, RHS, FMF);
+                       FastMathFlags FMF = FastMathFlags(),
+                       bool Strict = false) const {
+    return ConstantExpr::getFAdd(LHS, RHS, FMF, Strict);
   }
   Constant *CreateSub(Constant *LHS, Constant *RHS,
                       bool HasNUW = false, bool HasNSW = false) const {
     return ConstantExpr::getSub(LHS, RHS, HasNUW, HasNSW);
   }
   Constant *CreateFSub(Constant *LHS, Constant *RHS,
-                       FastMathFlags FMF = FastMathFlags()) const {
-    return ConstantExpr::getFSub(LHS, RHS, FMF);
+                       FastMathFlags FMF = FastMathFlags(),
+                       bool Strict = false) const {
+    return ConstantExpr::getFSub(LHS, RHS, FMF, Strict);
   }
   Constant *CreateMul(Constant *LHS, Constant *RHS,
                       bool HasNUW = false, bool HasNSW = false) const {
     return ConstantExpr::getMul(LHS, RHS, HasNUW, HasNSW);
   }
   Constant *CreateFMul(Constant *LHS, Constant *RHS,
-                       FastMathFlags FMF = FastMathFlags()) const {
-    return ConstantExpr::getFMul(LHS, RHS, FMF);
+                       FastMathFlags FMF = FastMathFlags(),
+                       bool Strict = false) const {
+    return ConstantExpr::getFMul(LHS, RHS, FMF, Strict);
   }
   Constant *CreateUDiv(Constant *LHS, Constant *RHS,
                        bool isExact = false) const {
@@ -65,8 +68,9 @@ public:
     return ConstantExpr::getSDiv(LHS, RHS, isExact);
   }
   Constant *CreateFDiv(Constant *LHS, Constant *RHS,
-                       FastMathFlags FMF = FastMathFlags()) const {
-    return ConstantExpr::getFDiv(LHS, RHS, FMF);
+                       FastMathFlags FMF = FastMathFlags(),
+                       bool Strict = false) const {
+    return ConstantExpr::getFDiv(LHS, RHS, FMF, Strict);
   }
   Constant *CreateURem(Constant *LHS, Constant *RHS) const {
     return ConstantExpr::getURem(LHS, RHS);
@@ -75,8 +79,9 @@ public:
     return ConstantExpr::getSRem(LHS, RHS);
   }
   Constant *CreateFRem(Constant *LHS, Constant *RHS,
-                       FastMathFlags FMF = FastMathFlags()) const {
-    return ConstantExpr::getFRem(LHS, RHS, FMF);
+                       FastMathFlags FMF = FastMathFlags(),
+                       bool Strict = false) const {
+    return ConstantExpr::getFRem(LHS, RHS, FMF, Strict);
   }
   Constant *CreateShl(Constant *LHS, Constant *RHS,
                       bool HasNUW = false, bool HasNSW = false) const {
diff --git a/include/llvm/IR/Constants.h b/include/llvm/IR/Constants.h
index 940985b..22c777c 100644
--- a/include/llvm/IR/Constants.h
+++ b/include/llvm/IR/Constants.h
@@ -896,23 +896,28 @@ public:
   static Constant *getAdd(Constant *C1, Constant *C2,
                           bool HasNUW = false, bool HasNSW = false);
   static Constant *getFAdd(Constant *C1, Constant *C2,
-                           FastMathFlags FMF = FastMathFlags());
+                           FastMathFlags FMF = FastMathFlags(),
+                           bool Strict = false);
   static Constant *getSub(Constant *C1, Constant *C2,
                           bool HasNUW = false, bool HasNSW = false);
   static Constant *getFSub(Constant *C1, Constant *C2,
-                           FastMathFlags FMF = FastMathFlags());
+                           FastMathFlags FMF = FastMathFlags(),
+                           bool Strict = false);
   static Constant *getMul(Constant *C1, Constant *C2,
                           bool HasNUW = false, bool HasNSW = false);
   static Constant *getFMul(Constant *C1, Constant *C2,
-                           FastMathFlags FMF = FastMathFlags());
+                           FastMathFlags FMF = FastMathFlags(),
+                           bool Strict = false);
   static Constant *getUDiv(Constant *C1, Constant *C2, bool isExact = false);
   static Constant *getSDiv(Constant *C1, Constant *C2, bool isExact = false);
   static Constant *getFDiv(Constant *C1, Constant *C2,
-                           FastMathFlags FMF = FastMathFlags());
+                           FastMathFlags FMF = FastMathFlags(),
+                           bool Strict = false);
   static Constant *getURem(Constant *C1, Constant *C2);
   static Constant *getSRem(Constant *C1, Constant *C2);
   static Constant *getFRem(Constant *C1, Constant *C2,
-                           FastMathFlags FMF = FastMathFlags());
+                           FastMathFlags FMF = FastMathFlags(),
+                           bool Strict = false);
   static Constant *getAnd(Constant *C1, Constant *C2);
   static Constant *getOr(Constant *C1, Constant *C2);
   static Constant *getXor(Constant *C1, Constant *C2);
diff --git a/lib/IR/Constants.cpp b/lib/IR/Constants.cpp
index abd9507..d9cc648 100644
--- a/lib/IR/Constants.cpp
+++ b/lib/IR/Constants.cpp
@@ -2274,8 +2274,9 @@ Constant *ConstantExpr::getAdd(Constant *C1, Constant *C2,
   return get(Instruction::Add, C1, C2, Flags);
 }
 
-Constant *ConstantExpr::getFAdd(Constant *C1, Constant *C2, FastMathFlags FMF) {
-  return get(Instruction::FAdd, C1, C2, 0, nullptr, FMF);
+Constant *ConstantExpr::getFAdd(Constant *C1, Constant *C2,
+                                FastMathFlags FMF, bool Strict) {
+  return get(Instruction::FAdd, C1, C2, 0, nullptr, FMF, Strict);
 }
 
 Constant *ConstantExpr::getSub(Constant *C1, Constant *C2,
@@ -2285,8 +2286,9 @@ Constant *ConstantExpr::getSub(Constant *C1, Constant *C2,
   return get(Instruction::Sub, C1, C2, Flags);
 }
 
-Constant *ConstantExpr::getFSub(Constant *C1, Constant *C2, FastMathFlags FMF) {
-  return get(Instruction::FSub, C1, C2, 0, nullptr, FMF);
+Constant *ConstantExpr::getFSub(Constant *C1, Constant *C2,
+                                FastMathFlags FMF, bool Strict) {
+  return get(Instruction::FSub, C1, C2, 0, nullptr, FMF, Strict);
 }
 
 Constant *ConstantExpr::getMul(Constant *C1, Constant *C2,
@@ -2296,8 +2298,9 @@ Constant *ConstantExpr::getMul(Constant *C1, Constant *C2,
   return get(Instruction::Mul, C1, C2, Flags, nullptr);
 }
 
-Constant *ConstantExpr::getFMul(Constant *C1, Constant *C2, FastMathFlags FMF) {
-  return get(Instruction::FMul, C1, C2, 0, nullptr, FMF);
+Constant *ConstantExpr::getFMul(Constant *C1, Constant *C2,
+                                FastMathFlags FMF, bool Strict) {
+  return get(Instruction::FMul, C1, C2, 0, nullptr, FMF, Strict);
 }
 
 Constant *ConstantExpr::getUDiv(Constant *C1, Constant *C2, bool isExact) {
@@ -2310,8 +2313,9 @@ Constant *ConstantExpr::getSDiv(Constant *C1, Constant *C2, bool isExact) {
              isExact ? PossiblyExactOperator::IsExact : 0);
 }
 
-Constant *ConstantExpr::getFDiv(Constant *C1, Constant *C2, FastMathFlags FMF) {
-  return get(Instruction::FDiv, C1, C2, 0, nullptr, FMF);
+Constant *ConstantExpr::getFDiv(Constant *C1, Constant *C2,
+                                FastMathFlags FMF, bool Strict) {
+  return get(Instruction::FDiv, C1, C2, 0, nullptr, FMF, Strict);
 }
 
 Constant *ConstantExpr::getURem(Constant *C1, Constant *C2) {
@@ -2322,8 +2326,9 @@ Constant *ConstantExpr::getSRem(Constant *C1, Constant *C2) {
   return get(Instruction::SRem, C1, C2);
 }
 
-Constant *ConstantExpr::getFRem(Constant *C1, Constant *C2, FastMathFlags FMF) {
-  return get(Instruction::FRem, C1, C2, 0, nullptr, FMF);
+Constant *ConstantExpr::getFRem(Constant *C1, Constant *C2,
+                                FastMathFlags FMF, bool Strict) {
+  return get(Instruction::FRem, C1, C2, 0, nullptr, FMF, Strict);
 }
 
 Constant *ConstantExpr::getAnd(Constant *C1, Constant *C2) {
-- 
2.3.5

-------------- next part --------------
>From c15ee5dd31bd115858b4344d61d4745312be85b0 Mon Sep 17 00:00:00 2001
From: Sergey Dmitrouk <sdmitrouk at accesssoftek.com>
Date: Fri, 12 Dec 2014 16:40:08 +0200
Subject: [PATCH 10/13] Use Strict in IRBuilder

Try to create constant expression, but skip it in case of conflicts
with FPEnv access flags.
---
 include/llvm/IR/IRBuilder.h    | 15 +++++++++-----
 unittests/IR/IRBuilderTest.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 56 insertions(+), 5 deletions(-)

diff --git a/include/llvm/IR/IRBuilder.h b/include/llvm/IR/IRBuilder.h
index e7556e8..1a2c317 100644
--- a/include/llvm/IR/IRBuilder.h
+++ b/include/llvm/IR/IRBuilder.h
@@ -759,7 +759,8 @@ public:
                     MDNode *FPMathTag = nullptr) {
     if (Constant *LC = dyn_cast<Constant>(LHS))
       if (Constant *RC = dyn_cast<Constant>(RHS))
-        return Insert(Folder.CreateFAdd(LC, RC, FMF), Name);
+        if (Constant *C = Insert(Folder.CreateFAdd(LC, RC, FMF, true), Name))
+          return C;
     return Insert(AddFPMathAttributes(BinaryOperator::CreateFAdd(LHS, RHS),
                                       FPMathTag, FMF), Name);
   }
@@ -781,7 +782,8 @@ public:
                     MDNode *FPMathTag = nullptr) {
     if (Constant *LC = dyn_cast<Constant>(LHS))
       if (Constant *RC = dyn_cast<Constant>(RHS))
-        return Insert(Folder.CreateFSub(LC, RC, FMF), Name);
+        if (Constant *C = Insert(Folder.CreateFSub(LC, RC, FMF, true), Name))
+          return C;
     return Insert(AddFPMathAttributes(BinaryOperator::CreateFSub(LHS, RHS),
                                       FPMathTag, FMF), Name);
   }
@@ -803,7 +805,8 @@ public:
                     MDNode *FPMathTag = nullptr) {
     if (Constant *LC = dyn_cast<Constant>(LHS))
       if (Constant *RC = dyn_cast<Constant>(RHS))
-        return Insert(Folder.CreateFMul(LC, RC, FMF), Name);
+        if (Constant *C = Insert(Folder.CreateFMul(LC, RC, FMF, true), Name))
+          return C;
     return Insert(AddFPMathAttributes(BinaryOperator::CreateFMul(LHS, RHS),
                                       FPMathTag, FMF), Name);
   }
@@ -835,7 +838,8 @@ public:
                     MDNode *FPMathTag = nullptr) {
     if (Constant *LC = dyn_cast<Constant>(LHS))
       if (Constant *RC = dyn_cast<Constant>(RHS))
-        return Insert(Folder.CreateFDiv(LC, RC, FMF), Name);
+        if (Constant *C = Insert(Folder.CreateFDiv(LC, RC, FMF, true), Name))
+          return C;
     return Insert(AddFPMathAttributes(BinaryOperator::CreateFDiv(LHS, RHS),
                                       FPMathTag, FMF), Name);
   }
@@ -855,7 +859,8 @@ public:
                     MDNode *FPMathTag = nullptr) {
     if (Constant *LC = dyn_cast<Constant>(LHS))
       if (Constant *RC = dyn_cast<Constant>(RHS))
-        return Insert(Folder.CreateFRem(LC, RC, FMF), Name);
+        if (Constant *C = Insert(Folder.CreateFRem(LC, RC, FMF, true), Name))
+          return C;
     return Insert(AddFPMathAttributes(BinaryOperator::CreateFRem(LHS, RHS),
                                       FPMathTag, FMF), Name);
   }
diff --git a/unittests/IR/IRBuilderTest.cpp b/unittests/IR/IRBuilderTest.cpp
index 093cbbf..c9d987a 100644
--- a/unittests/IR/IRBuilderTest.cpp
+++ b/unittests/IR/IRBuilderTest.cpp
@@ -221,6 +221,52 @@ TEST_F(IRBuilderTest, FastMathFlags) {
 
 }
 
+TEST_F(IRBuilderTest, FPEnvFlags) {
+  IRBuilder<> Builder(BB);
+  Value *F;
+
+  Constant *Huge = ConstantFP::get(Type::getDoubleTy(Ctx), 1.000000e+308);
+  Constant *Big = ConstantFP::get(Type::getDoubleTy(Ctx), 1.000000e+300);
+  Constant *Small = ConstantFP::get(Type::getDoubleTy(Ctx), 1.000000e-300);
+  Constant *Tiny = ConstantFP::get(Type::getDoubleTy(Ctx), 1.000000e-308);
+  Constant *Zero = ConstantFP::get(Type::getDoubleTy(Ctx), 0.0);
+
+  FastMathFlags KeepExceptions;
+  KeepExceptions.setKeepExceptions();
+  Builder.SetFastMathFlags(KeepExceptions);
+
+  F = Builder.CreateFAdd(Huge, Huge);
+  EXPECT_FALSE(isa<Constant>(F));
+
+  F = Builder.CreateFSub(Tiny, Huge);
+  EXPECT_FALSE(isa<Constant>(F));
+
+  F = Builder.CreateFMul(Big, Big);
+  EXPECT_FALSE(isa<Constant>(F));
+
+  F = Builder.CreateFDiv(Big, Small);
+  EXPECT_FALSE(isa<Constant>(F));
+
+  F = Builder.CreateFDiv(Big, Zero);
+  EXPECT_FALSE(isa<Constant>(F));
+
+  F = Builder.CreateFRem(Big, Zero);
+  EXPECT_FALSE(isa<Constant>(F));
+
+  FastMathFlags KeepRounding;
+  KeepRounding.setKeepRounding();
+  Builder.SetFastMathFlags(KeepRounding);
+
+  F = Builder.CreateFAdd(Huge, Huge);
+  EXPECT_FALSE(isa<Constant>(F));
+
+  F = Builder.CreateFSub(Tiny, Huge);
+  EXPECT_FALSE(isa<Constant>(F));
+
+  F = Builder.CreateFMul(Huge, Huge);
+  EXPECT_FALSE(isa<Constant>(F));
+}
+
 TEST_F(IRBuilderTest, WrapFlags) {
   IRBuilder<true, NoFolder> Builder(BB);
 
-- 
2.3.5

-------------- next part --------------
>From 1912f24f1e1778f0a2150e768f783193ef5297a2 Mon Sep 17 00:00:00 2001
From: Sergey Dmitrouk <sdmitrouk at accesssoftek.com>
Date: Mon, 15 Dec 2014 14:18:03 +0200
Subject: [PATCH 11/13] Don't convert fpops to constexprs in SCCP

---
 lib/Transforms/Scalar/SCCP.cpp                     | 16 +++++++++++----
 .../SCCP/do-not-convert-fpop-to-constexpr.ll       | 24 ++++++++++++++++++++++
 2 files changed, 36 insertions(+), 4 deletions(-)
 create mode 100644 test/Transforms/SCCP/do-not-convert-fpop-to-constexpr.ll

diff --git a/lib/Transforms/Scalar/SCCP.cpp b/lib/Transforms/Scalar/SCCP.cpp
index a35d2e8..cbaeabf 100644
--- a/lib/Transforms/Scalar/SCCP.cpp
+++ b/lib/Transforms/Scalar/SCCP.cpp
@@ -857,10 +857,18 @@ void SCCPSolver::visitBinaryOperator(Instruction &I) {
   LatticeVal &IV = ValueState[&I];
   if (IV.isOverdefined()) return;
 
-  if (V1State.isConstant() && V2State.isConstant())
-    return markConstant(IV, &I,
-                        ConstantExpr::get(I.getOpcode(), V1State.getConstant(),
-                                          V2State.getConstant()));
+  if (V1State.isConstant() && V2State.isConstant()) {
+    FastMathFlags FMF;
+
+    if (isa<FPMathOperator>(&I)) {
+      FMF = I.getFastMathFlags();
+    }
+
+    if (Constant *C = ConstantExpr::get(I.getOpcode(), V1State.getConstant(),
+                                        V2State.getConstant(), 0, nullptr,
+                                        FMF, true))
+      return markConstant(IV, &I, C);
+  }
 
   // If something is undef, wait for it to resolve.
   if (!V1State.isOverdefined() && !V2State.isOverdefined())
diff --git a/test/Transforms/SCCP/do-not-convert-fpop-to-constexpr.ll b/test/Transforms/SCCP/do-not-convert-fpop-to-constexpr.ll
new file mode 100644
index 0000000..df3275a
--- /dev/null
+++ b/test/Transforms/SCCP/do-not-convert-fpop-to-constexpr.ll
@@ -0,0 +1,24 @@
+; fmuls shouldn't be converted to constant expressions.
+; RUN: opt < %s -sccp -S -enable-except-access-fp-math | FileCheck %s
+
+define double @do-not-convert-fpop-to-constexpr(double %x) {
+; CHECK-LABEL: @do-not-convert-fpop-to-constexpr(
+; CHECK: fmul
+; CHECK: fmul
+; CHECK-NOT: select
+entry:
+  %cmp = fcmp oeq double %x, 0.000000e+00
+  br i1 %cmp, label %cond.true, label %cond.false
+
+cond.true:
+  %val.true = fmul kexc double 1.000000e+300, 1.000000e+300
+  br label %cond.end
+
+cond.false:
+  %val.false = fmul kexc double 1.000000e-300, 1.000000e-300
+  br label %cond.end
+
+cond.end:
+  %cond = phi double [ %val.true, %cond.true ], [ %val.false, %cond.false ]
+  ret double %cond
+}
-- 
2.3.5

-------------- next part --------------
>From e3b09b31da723f5a9e3ed32b9acd4363ee1c2b9f Mon Sep 17 00:00:00 2001
From: Sergey Dmitrouk <sdmitrouk at accesssoftek.com>
Date: Fri, 19 Dec 2014 15:08:59 +0200
Subject: [PATCH 12/13] Prevent InstCombine from hanging

InstCombine shouldn't hang in non-terminating sequence of conversions like:
    (a + b) => (a - (-b)) => (a + b) => etc.
---
 lib/Transforms/InstCombine/InstCombineAddSub.cpp   | 28 ++++++++-------
 test/Other/fpenv-constant-fold.ll                  |  9 ++---
 .../do-not-hand-on-fpops-with-fpenv-access.ll      | 42 ++++++++++++++++++++++
 3 files changed, 63 insertions(+), 16 deletions(-)
 create mode 100644 test/Transforms/InstCombine/do-not-hand-on-fpops-with-fpenv-access.ll

diff --git a/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/lib/Transforms/InstCombine/InstCombineAddSub.cpp
index ba6e687..c2d333a 100644
--- a/lib/Transforms/InstCombine/InstCombineAddSub.cpp
+++ b/lib/Transforms/InstCombine/InstCombineAddSub.cpp
@@ -1333,22 +1333,26 @@ Instruction *InstCombiner::visitFAdd(BinaryOperator &I) {
         return NV;
   }
 
-  // -A + B  -->  B - A
-  // -A + -B  -->  -(A + B)
-  if (Value *LHSV = dyn_castFNegVal(LHS)) {
-    Instruction *RI = BinaryOperator::CreateFSub(RHS, LHSV);
-    RI->copyFastMathFlags(&I);
-    return RI;
-  }
-
-  // A + -B  -->  A - B
-  if (!isa<Constant>(RHS))
-    if (Value *V = dyn_castFNegVal(RHS)) {
-      Instruction *RI = BinaryOperator::CreateFSub(LHS, V);
+  // Skip this to break implicit infinite loop with "fsub" transformations of
+  // the form: (a + b) => (a - (-b)) => (a + b) => etc
+  if (!I.hasKeepExceptions() && !I.hasKeepRounding()) {
+    // -A + B  -->  B - A
+    // -A + -B  -->  -(A + B)
+    if (Value *LHSV = dyn_castFNegVal(LHS)) {
+      Instruction *RI = BinaryOperator::CreateFSub(RHS, LHSV);
       RI->copyFastMathFlags(&I);
       return RI;
     }
 
+    // A + -B  -->  A - B
+    if (!isa<Constant>(RHS))
+      if (Value *V = dyn_castFNegVal(RHS)) {
+        Instruction *RI = BinaryOperator::CreateFSub(LHS, V);
+        RI->copyFastMathFlags(&I);
+        return RI;
+      }
+  }
+
   // Check for (fadd double (sitofp x), y), see if we can merge this into an
   // integer add followed by a promotion.
   if (SIToFPInst *LHSConv = dyn_cast<SIToFPInst>(LHS)) {
diff --git a/test/Other/fpenv-constant-fold.ll b/test/Other/fpenv-constant-fold.ll
index 01dbfa0..68b94ca 100644
--- a/test/Other/fpenv-constant-fold.ll
+++ b/test/Other/fpenv-constant-fold.ll
@@ -1,4 +1,5 @@
-; InstCombine is used just for its calls to constant folder.
+; InstCombine is used just for its calls to constant folder, it is the cause of
+; fsub -> fadd transformation.
 ; RUN: opt -S -instcombine -o - < %s | FileCheck %s
 
 ; Target independent constant folder should not fold floating-point operations
@@ -15,7 +16,7 @@ entry:
 
 define double @do-not-fold-fsub-that-can-trap() {
 ; CHECK-LABEL: @do-not-fold-fsub-that-can-trap
-; CHECK: fsub
+; CHECK: fadd
 entry:
   %val = fsub kexc double 1.000000e-308, 1.000000e+308
   ret double %val
@@ -55,7 +56,7 @@ entry:
 
 define double @do-not-fold-fsub-because-of-rounding() {
 ; CHECK-LABEL: @do-not-fold-fsub-because-of-rounding
-; CHECK: fsub
+; CHECK: fadd
 entry:
   %val = fsub kround double 0.101, 0.93
   ret double %val
@@ -92,7 +93,7 @@ define double @fold-fsub-that-cant-trap() {
 ; CHECK-LABEL: @fold-fsub-that-cant-trap
 ; CHECK-NOT: fsub
 entry:
-  %val = fsub kexc kround double 1.0, 1.0
+  %val = fadd kexc kround double 1.0, 1.0
   ret double %val
 }
 
diff --git a/test/Transforms/InstCombine/do-not-hand-on-fpops-with-fpenv-access.ll b/test/Transforms/InstCombine/do-not-hand-on-fpops-with-fpenv-access.ll
new file mode 100644
index 0000000..8ea33f0
--- /dev/null
+++ b/test/Transforms/InstCombine/do-not-hand-on-fpops-with-fpenv-access.ll
@@ -0,0 +1,42 @@
+; RUN: opt < %s -instcombine -S
+
+; InstCombine shouldn't hang in non-terminating sequence of conversions like:
+;    (a + b) => (a - (-b)) => (a + b) => etc.
+
+define double @test1() {
+entry:
+  %add = fadd kexc double 0x3FF921FB54442D18, 0x3870000000000000
+  ret double %add
+}
+
+define double @test2(double %x) {
+entry:
+  %sub = fsub kexc kround double -0.000000e+00, 1.000000e+00
+  %div = fdiv kexc kround double %sub, %x
+  ret double %div
+}
+
+define double @test3() {
+entry:
+  %sub = fsub kexc kround double -0.000000e+00, 0x400921FB54442D18
+  ret double %sub
+}
+
+define double @test4(i32 %y) {
+entry:
+  %tobool = icmp ne i32 %y, 0
+  br i1 %tobool, label %cond.true, label %cond.false
+
+cond.true:
+  %sub = fsub kexc kround double -0.000000e+00, 0x400921FB54442D18
+  %div = fdiv kexc kround double %sub, 2.000000e+00
+  br label %cond.end
+
+cond.false:
+  %div1 = fdiv kexc kround double 0x400921FB54442D18, 2.000000e+00
+  br label %cond.end
+
+cond.end:
+  %cond = phi double [ %div, %cond.true ], [ %div1, %cond.false ]
+  ret double %cond
+}
-- 
2.3.5

-------------- next part --------------
>From 2e9d40e34639df03322b2382b1b34ab7152a0ebd Mon Sep 17 00:00:00 2001
From: Sergey Dmitrouk <sdmitrouk at accesssoftek.com>
Date: Mon, 2 Feb 2015 18:53:43 +0200
Subject: [PATCH 13/13] Don't hoist FP-ops with side-effects in LICM

isSafeToSpeculativelyExecute() is not enough, because it's result
it taken into account only when it's positive and we need to forbid
the case where result is negative.
---
 lib/Transforms/Scalar/LICM.cpp |  7 +++++++
 test/Transforms/LICM/fpenv.ll  | 30 ++++++++++++++++++++++++++++++
 2 files changed, 37 insertions(+)
 create mode 100644 test/Transforms/LICM/fpenv.ll

diff --git a/lib/Transforms/Scalar/LICM.cpp b/lib/Transforms/Scalar/LICM.cpp
index 2348d83..6c6e52d 100644
--- a/lib/Transforms/Scalar/LICM.cpp
+++ b/lib/Transforms/Scalar/LICM.cpp
@@ -690,6 +690,13 @@ static bool isSafeToExecuteUnconditionally(const Instruction &Inst,
   if (isSafeToSpeculativelyExecute(&Inst, CtxI, DT, TLI))
     return true;
 
+  // isSafeToSpeculativelyExecute() is not enough, because its result is
+  // taken into account only when it's positive and we need to forbid the case
+  // where its result is negative.
+  if (const FPMathOperator *MathOp = dyn_cast<FPMathOperator>(&Inst))
+    if (MathOp->hasKeepExceptions() || MathOp->hasKeepRounding())
+      return false;
+
   return isGuaranteedToExecute(Inst, DT, CurLoop, SafetyInfo);
 }
 
diff --git a/test/Transforms/LICM/fpenv.ll b/test/Transforms/LICM/fpenv.ll
new file mode 100644
index 0000000..6fb459c
--- /dev/null
+++ b/test/Transforms/LICM/fpenv.ll
@@ -0,0 +1,30 @@
+; RUN: opt < %s -O3 -S | FileCheck %s
+
+define void @fdiv-is-not-hoisted-out-of-loop(i32 %n) {
+; CHECK-LABEL: @fdiv-is-not-hoisted-out-of-loop(
+; CHECK: for.body:
+; CHECK: fdiv
+entry:
+  %cmp2 = icmp sgt i32 %n, 0
+  br i1 %cmp2, label %for.body.lr.ph, label %for.end
+
+for.body.lr.ph:
+  %0 = add i32 %n, -1
+  br label %for.body
+
+for.body:
+  %i.03 = phi i32 [ 0, %for.body.lr.ph ], [ %inc, %for.body ]
+  %div = fdiv kexc kround double 0.000000e+00, 0.000000e+00
+  tail call void @process(double %div)
+  %inc = add nuw nsw i32 %i.03, 1
+  %exitcond = icmp eq i32 %i.03, %0
+  br i1 %exitcond, label %for.end.loopexit, label %for.body
+
+for.end.loopexit:
+  br label %for.end
+
+for.end:
+  ret void
+}
+
+declare void @process(double) nounwind
-- 
2.3.5



More information about the llvm-commits mailing list