[builtins] Divide shouldn't underflow if rounded result would be normal.

We were treating certain edge cases that are actually normal as denormal
results, and flushing them to zero; we shouldn't do that. Not sure this
is the cleanest way to implement this edge case, but I wanted to avoid
adding any code on the common path.

Differential Revision: https://reviews.llvm.org/D59070

llvm-svn: 356529
diff --git a/compiler-rt/lib/builtins/divdf3.c b/compiler-rt/lib/builtins/divdf3.c
index 3993507..9132f83 100644
--- a/compiler-rt/lib/builtins/divdf3.c
+++ b/compiler-rt/lib/builtins/divdf3.c
@@ -162,6 +162,18 @@
     }
 
     else if (writtenExponent < 1) {
+        if (writtenExponent == 0) {
+            // Check whether the rounded result is normal.
+            const bool round = (residual << 1) > bSignificand;
+            // Clear the implicit bit.
+            rep_t absResult = quotient & significandMask;
+            // Round.
+            absResult += round;
+            if (absResult & ~significandMask) {
+                // The rounded result is normal; return it.
+                return fromRep(absResult | quotientSign);
+            }
+        }
         // Flush denormals to zero.  In the future, it would be nice to add
         // code to round them correctly.
         return fromRep(quotientSign);
diff --git a/compiler-rt/lib/builtins/divsf3.c b/compiler-rt/lib/builtins/divsf3.c
index 97cbcc1..9445247 100644
--- a/compiler-rt/lib/builtins/divsf3.c
+++ b/compiler-rt/lib/builtins/divsf3.c
@@ -147,6 +147,18 @@
     }
 
     else if (writtenExponent < 1) {
+        if (writtenExponent == 0) {
+            // Check whether the rounded result is normal.
+            const bool round = (residual << 1) > bSignificand;
+            // Clear the implicit bit.
+            rep_t absResult = quotient & significandMask;
+            // Round.
+            absResult += round;
+            if (absResult & ~significandMask) {
+                // The rounded result is normal; return it.
+                return fromRep(absResult | quotientSign);
+            }
+        }
         // Flush denormals to zero.  In the future, it would be nice to add
         // code to round them correctly.
         return fromRep(quotientSign);
diff --git a/compiler-rt/lib/builtins/divtf3.c b/compiler-rt/lib/builtins/divtf3.c
index 1727374..ba5de99 100644
--- a/compiler-rt/lib/builtins/divtf3.c
+++ b/compiler-rt/lib/builtins/divtf3.c
@@ -181,6 +181,18 @@
         return fromRep(infRep | quotientSign);
     }
     else if (writtenExponent < 1) {
+        if (writtenExponent == 0) {
+            // Check whether the rounded result is normal.
+            const bool round = (residual << 1) > bSignificand;
+            // Clear the implicit bit.
+            rep_t absResult = quotient & significandMask;
+            // Round.
+            absResult += round;
+            if (absResult & ~significandMask) {
+                // The rounded result is normal; return it.
+                return fromRep(absResult | quotientSign);
+            }
+        }
         // Flush denormals to zero.  In the future, it would be nice to add
         // code to round them correctly.
         return fromRep(quotientSign);
diff --git a/compiler-rt/test/builtins/Unit/divdf3_test.c b/compiler-rt/test/builtins/Unit/divdf3_test.c
new file mode 100644
index 0000000..00b021f
--- /dev/null
+++ b/compiler-rt/test/builtins/Unit/divdf3_test.c
@@ -0,0 +1,45 @@
+// RUN: %clang_builtins %s %librt -o %t && %run %t
+//===--------------- divdf3_test.c - Test __divdf3 ------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file tests __divdf3 for the compiler_rt library.
+//
+//===----------------------------------------------------------------------===//
+
+#include "int_lib.h"
+#include <stdio.h>
+
+#include "fp_test.h"
+
+// Returns: a / b
+COMPILER_RT_ABI double __divdf3(double a, double b);
+
+int test__divdf3(double a, double b, uint64_t expected)
+{
+    double x = __divdf3(a, b);
+    int ret = compareResultD(x, expected);
+
+    if (ret){
+        printf("error in test__divdf3(%.20e, %.20e) = %.20e, "
+               "expected %.20e\n", a, b, x,
+               fromRep64(expected));
+    }
+    return ret;
+}
+
+int main()
+{
+    // 1/3
+    if (test__divdf3(1., 3., 0x3fd5555555555555ULL))
+      return 1;
+    // smallest normal result
+    if (test__divdf3(4.450147717014403e-308, 2., 0x10000000000000ULL))
+      return 1;
+
+    return 0;
+}
diff --git a/compiler-rt/test/builtins/Unit/divsf3_test.c b/compiler-rt/test/builtins/Unit/divsf3_test.c
new file mode 100644
index 0000000..4b14c81
--- /dev/null
+++ b/compiler-rt/test/builtins/Unit/divsf3_test.c
@@ -0,0 +1,45 @@
+// RUN: %clang_builtins %s %librt -o %t && %run %t
+//===--------------- divsf3_test.c - Test __divsf3 ------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file tests __divsf3 for the compiler_rt library.
+//
+//===----------------------------------------------------------------------===//
+
+#include "int_lib.h"
+#include <stdio.h>
+
+#include "fp_test.h"
+
+// Returns: a / b
+COMPILER_RT_ABI float __divsf3(float a, float b);
+
+int test__divsf3(float a, float b, uint32_t expected)
+{
+    float x = __divsf3(a, b);
+    int ret = compareResultF(x, expected);
+
+    if (ret){
+        printf("error in test__divsf3(%.20e, %.20e) = %.20e, "
+               "expected %.20e\n", a, b, x,
+               fromRep32(expected));
+    }
+    return ret;
+}
+
+int main()
+{
+    // 1/3
+    if (test__divsf3(1.f, 3.f, 0x3EAAAAABU))
+      return 1;
+    // smallest normal result
+    if (test__divsf3(2.3509887e-38, 2., 0x00800000U))
+      return 1;
+
+    return 0;
+}
diff --git a/compiler-rt/test/builtins/Unit/divtf3_test.c b/compiler-rt/test/builtins/Unit/divtf3_test.c
index a9ff43e..23e1b55 100644
--- a/compiler-rt/test/builtins/Unit/divtf3_test.c
+++ b/compiler-rt/test/builtins/Unit/divtf3_test.c
@@ -28,8 +28,8 @@
     int ret = compareResultLD(x, expectedHi, expectedLo);
 
     if (ret){
-        printf("error in test__divtf3(%.20Lf, %.20Lf) = %.20Lf, "
-               "expected %.20Lf\n", a, b, x,
+        printf("error in test__divtf3(%.20Le, %.20Le) = %.20Le, "
+               "expected %.20Le\n", a, b, x,
                fromRep128(expectedHi, expectedLo));
     }
     return ret;
@@ -86,6 +86,11 @@
                      UINT64_C(0x50bf2e02f0798d36),
                      UINT64_C(0x5e6fcb6b60044078)))
         return 1;
+    if (test__divtf3(6.72420628622418701252535563464350521E-4932L,
+                     2.L,
+                     UINT64_C(0x0001000000000000),
+                     UINT64_C(0)))
+        return 1;
 
 #else
     printf("skipped\n");