[LLVMbugs] [Bug 21385] New: optimize reciprocals with fast-math (x86)

bugzilla-daemon at llvm.org bugzilla-daemon at llvm.org
Sat Oct 25 15:57:27 PDT 2014


http://llvm.org/bugs/show_bug.cgi?id=21385

            Bug ID: 21385
           Summary: optimize reciprocals with fast-math (x86)
           Product: libraries
           Version: trunk
          Hardware: PC
                OS: All
            Status: NEW
          Severity: normal
          Priority: P
         Component: Backend: X86
          Assignee: unassignedbugs at nondot.org
          Reporter: spatel+llvm at rotateright.com
                CC: llvmbugs at cs.uiuc.edu
    Classification: Unclassified

This is the sibling to bug 20900 (square root estimates).

We could be generating RCPSS / RCPPS instead of divisions when -ffast-math
allows it.

I thought this would be simpler than square root, but we'll need to again
measure the performance gains per CPU to decide where to enable it, and we may
need to offer a 2nd Newton-Raphson refinement implementation for x86 that lacks
FMA.

I've also written a test program (below) that tries to confirm the goodness of
the estimate. It shows that one N-R step is inadequate to get us to 1 ULP of
the IEEE result on both Intel Sandy Bridge and AMD Jaguar. 

Most unfortunately, we get the wrong answer for a reciprocal of 1.0f! This is
why RCPPS isn't generated with gcc -ffast-math.

Both Intel and AMD manuals say that the estimate instruction provides almost
12-bits of accuracy, and one N-R step should double that accuracy to 24-bits
which is good enough for a single-precision float, but no...that "almost
12-bits" is definitely not good enough. We need two N-R steps.

Test program:

#include <xmmintrin.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
float recip_ieee(float x) { return 1.0f/x; }

float recip_est(float x) {
    float est;
    __m128 vecX = { x,0,0,0 };
    __m128 vecEst = _mm_rcp_ss(vecX);
        _mm_store_ss(&est, vecEst);    
    return est;     
}

float recip_est_NR1(float x) {
    float est;
    __m128 vecX = { x,0,0,0 };
    __m128 vecEst = _mm_rcp_ss(vecX);
        _mm_store_ss(&est, vecEst);    

    est = est + est * (1 - est * x);
    return est;     
}

float recip_est_NR2(float x) {
    float est;
    __m128 vecX = { x,0,0,0 };
    __m128 vecEst = _mm_rcp_ss(vecX);
        _mm_store_ss(&est, vecEst);    

    est = est + est * (1 - est * x);
    est = est + est * (1 - est * x);
    return est;     
}

int main() {
    int i;
    unsigned int tests = 0;
    unsigned int not_exact_NR1 = 0;
    unsigned int not_exact_NR2 = 0;
    unsigned int bad_NR1 = 0;
    unsigned int bad_NR2 = 0;
    // ignore denorm inputs (anything less than 0x00800000)
    // ignore denorm outputs (any input over 0x7e800000)
    // ignore inf (0x7f800000)
    // ignore nans (anything more than 0x7f800000)
    // ignore negative (anything more than 0x80000000)
    for (i = 0x00800000; i < 0x7e800000; i++) {
        float f, ieee, est, est_nr1, est_nr2;
        int ieee_int, est_int, est_nr1_int, est_nr2_int;

        tests++;
        memcpy(&f, &i, 4);
        ieee = recip_ieee(f);
//        est = recip_est(f);
        est_nr1 = recip_est_NR1(f);
        est_nr2 = recip_est_NR2(f);
        memcpy(&ieee_int, &ieee, 4);
//        memcpy(&est_int, &est, 4);
        memcpy(&est_nr1_int, &est_nr1, 4);
        memcpy(&est_nr2_int, &est_nr2, 4);

//        if (i % 0x01000000 == 0) printf("0x%08x\n", i); // just a status
print...

        if (ieee != est_nr1) {

            not_exact_NR1++;
            if (abs(ieee_int - est_nr1_int) > 1) {
                bad_NR1++;
//                printf("0x%08x: %e, ieee = %e (0x%08x), est_nr1 = %e
(0x%08x), est_nr2 = %e (0x%08x)\n",
//                     i, f, ieee, ieee_int, est_nr1, est_nr1_int, est_nr2,
est_nr2_int);
            }
        }
        if (ieee != est_nr2) {
            not_exact_NR2++;
            if (abs(ieee_int - est_nr2_int) > 1) {
                bad_NR2++;
            }
        }
    }
    printf("Total tests = %d\n", tests);
    printf("Inexact results with one N-R step = %d\n", not_exact_NR1++); 
    printf("Results off by > 1 ULP with one N-R step = %d\n", bad_NR1);
    printf("Inexact results with two N-R steps = %d\n", not_exact_NR2++); 
    printf("Results off by > 1 ULP with two N-R steps = %d\n", bad_NR2);
    return 0;
}

-- 
You are receiving this mail because:
You are on the CC list for the bug.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-bugs/attachments/20141025/0d93727e/attachment.html>


More information about the llvm-bugs mailing list