Don't do (X != Y) ? X : Y -> X for floating-point values; it doesn't
authorDan Gohman <gohman@apple.com>
Tue, 23 Feb 2010 17:17:57 +0000 (17:17 +0000)
committerDan Gohman <gohman@apple.com>
Tue, 23 Feb 2010 17:17:57 +0000 (17:17 +0000)
handle NaN properly.

Do (X une Y) ? X : Y  -> X if one of X and Y is not zero.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@96955 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Transforms/InstCombine/InstCombineSelect.cpp
test/Transforms/InstCombine/fcmp-select.ll [new file with mode: 0644]

index 7807d9a6334190cb4e50642d323c08a80b805f51..2fc932594f7b0931cf4dcad099dc5cfa5154bf17 100644 (file)
@@ -539,9 +539,18 @@ Instruction *InstCombiner::visitSelectInst(SelectInst &SI) {
              !CFPf->getValueAPF().isZero()))
         return ReplaceInstUsesWith(SI, FalseVal);
       }
-      // Transform (X != Y) ? X : Y  -> X
-      if (FCI->getPredicate() == FCmpInst::FCMP_ONE)
+      // Transform (X une Y) ? X : Y  -> X
+      if (FCI->getPredicate() == FCmpInst::FCMP_UNE) {
+        // This is not safe in general for floating point:  
+        // consider X== -0, Y== +0.
+        // It becomes safe if either operand is a nonzero constant.
+        ConstantFP *CFPt, *CFPf;
+        if (((CFPt = dyn_cast<ConstantFP>(TrueVal)) &&
+              !CFPt->getValueAPF().isZero()) ||
+            ((CFPf = dyn_cast<ConstantFP>(FalseVal)) &&
+             !CFPf->getValueAPF().isZero()))
         return ReplaceInstUsesWith(SI, TrueVal);
+      }
       // NOTE: if we wanted to, this is where to detect MIN/MAX
 
     } else if (FCI->getOperand(0) == FalseVal && FCI->getOperand(1) == TrueVal){
@@ -557,9 +566,18 @@ Instruction *InstCombiner::visitSelectInst(SelectInst &SI) {
              !CFPf->getValueAPF().isZero()))
           return ReplaceInstUsesWith(SI, FalseVal);
       }
-      // Transform (X != Y) ? Y : X  -> Y
-      if (FCI->getPredicate() == FCmpInst::FCMP_ONE)
-        return ReplaceInstUsesWith(SI, TrueVal);
+      // Transform (X une Y) ? Y : X  -> Y
+      if (FCI->getPredicate() == FCmpInst::FCMP_UNE) {
+        // This is not safe in general for floating point:  
+        // consider X== -0, Y== +0.
+        // It becomes safe if either operand is a nonzero constant.
+        ConstantFP *CFPt, *CFPf;
+        if (((CFPt = dyn_cast<ConstantFP>(TrueVal)) &&
+              !CFPt->getValueAPF().isZero()) ||
+            ((CFPf = dyn_cast<ConstantFP>(FalseVal)) &&
+             !CFPf->getValueAPF().isZero()))
+          return ReplaceInstUsesWith(SI, TrueVal);
+      }
       // NOTE: if we wanted to, this is where to detect MIN/MAX
     }
     // NOTE: if we wanted to, this is where to detect ABS
diff --git a/test/Transforms/InstCombine/fcmp-select.ll b/test/Transforms/InstCombine/fcmp-select.ll
new file mode 100644 (file)
index 0000000..e04ab3e
--- /dev/null
@@ -0,0 +1,53 @@
+; RUN: opt < %s -instcombine -S | FileCheck %s
+
+; x != y ? x : y -> x if it's the right kind of != and at least
+; one of x and y is not negative zero.
+
+; CHECK: f0
+; CHECK: ret double %x
+define double @f0(double %x) nounwind readnone {
+entry:
+  %cmp = fcmp une double %x, -1.0
+  %cond = select i1 %cmp, double %x, double -1.0
+  ret double %cond
+}
+; CHECK: f1
+; CHECK: ret double -1.000000e+00
+define double @f1(double %x) nounwind readnone {
+entry:
+  %cmp = fcmp une double %x, -1.0
+  %cond = select i1 %cmp, double -1.0, double %x
+  ret double %cond
+}
+; CHECK: f2
+; CHECK: ret double %cond
+define double @f2(double %x, double %y) nounwind readnone {
+entry:
+  %cmp = fcmp une double %x, %y
+  %cond = select i1 %cmp, double %x, double %y
+  ret double %cond
+}
+; CHECK: f3
+; CHECK: ret double %cond
+define double @f3(double %x, double %y) nounwind readnone {
+entry:
+  %cmp = fcmp une double %x, %y
+  %cond = select i1 %cmp, double %y, double %x
+  ret double %cond
+}
+; CHECK: f4
+; CHECK: ret double %cond
+define double @f4(double %x) nounwind readnone {
+entry:
+  %cmp = fcmp one double %x, -1.0
+  %cond = select i1 %cmp, double %x, double -1.0
+  ret double %cond
+}
+; CHECK: f5
+; CHECK: ret double %cond
+define double @f5(double %x) nounwind readnone {
+entry:
+  %cmp = fcmp one double %x, -1.0
+  %cond = select i1 %cmp, double -1.0, double %x
+  ret double %cond
+}