ilogb.s   [plain text]


/*
 *  ilogb.s
 *
 *      by Ian Ollmann
 *
 *  Copyright © 2007 Apple Inc. All Rights Reserved.
 */
 
#include <machine/asm.h>
#include "abi.h"

ENTRY( ilogbf )
//move argument to eax
#if defined( __i386__ )
    movl    FRAME_SIZE(STACKP), %eax
#else
    movd    %xmm0,  %eax
#endif

    andl    $0x7f800000,        %eax        //expose exponent
    addl    $0x00800000,        %eax        //add 1 to exponent, promotes NaN/Inf to 2**31
    cmpl    $0x00800000,        %eax        //test for exceptional case
    jle     1f                              //jump to special code for zeros, NaNs, Infs and denorms
    sarl    $23,                %eax        //move exponent to unit precision
    subl    $128,               %eax        //remove (bias+1)
    ret
    
1:  je  2f                                  //handle zeros/denorms at 2

    //load initial value
#if defined( __i386__ )
    movss   FRAME_SIZE(STACKP), %xmm0
#endif

    cvtss2si    %xmm0,          %eax        //set invalid and produce a 0x80000000
    cmpeqss %xmm0,              %xmm0       // x == x
    movd    %xmm0,              %edx
    addl    %edx,               %eax        // c = |x| == inf ? 0x7fffffff : 0x80000000
    ret
    
2:  //reload initial value
#if defined( __i386__ )
    movl    FRAME_SIZE(STACKP), %edx
    movss   FRAME_SIZE(STACKP), %xmm0
#else
    movd    %xmm0,  %edx
#endif

    //set invalid for zero
    xorps       %xmm1,  %xmm1
    cmpeqss     %xmm1,  %xmm0
    cvtss2si    %xmm0,  %ecx

    andl    $0x7fffffff,        %edx        //take absolute value
    bsrl    %edx,               %eax        //get positition of leading set bit
    subl    $(126+23),          %eax        //subtract out bias for denorm
    cmp     $0,                 %edx
    cmove   %ecx,               %eax
    ret
    
ENTRY( ilogb )
//move high 32-bits of argument to eax
#if defined( __i386__ )
    //We take this roundabout route to avoid a store forwarding stall, which costs us 8 cycles on Yonah.
    movq    FRAME_SIZE(STACKP), %xmm0
    psrlq   $32,    %xmm0
    movd    %xmm0,  %eax
#else
    movd    %xmm0,  %rax
    shrq    $32,    %rax
#endif

    andl    $0x7ff00000,        %eax        //expose exponent
    addl    $0x00100000,        %eax        //add 1 to exponent, promotes NaN/Inf to 2**31
    cmpl    $0x00100000,        %eax        //test for exceptional cases
    jle     1f                              //jump to special code for zeros, NaNs, Infs and denorms
    sarl    $20,                %eax        //move exponent to unit precision
    subl    $1024,              %eax        //remove (bias+1)
    ret

1:  je      2f                              //handle zeros/denorms at 2

    //reload initial value
#if defined( __i386__ )
    movsd   FRAME_SIZE(STACKP), %xmm0
#endif
    cvtsd2si    %xmm0,          %eax        //set invalid and produce a 0x80000000
    cmpeqsd %xmm0,              %xmm0       // x == x
    movd    %xmm0,              %edx
    addl    %edx,               %eax        // c = |x| == inf ? 0x7fffffff : 0x80000000
    ret
    
2: // zero/denorm case 
    //reload initial value
#if defined( __i386__ )
    movsd   FRAME_SIZE(STACKP), %xmm0
    movl    4+FRAME_SIZE(STACKP), %edx
#else
    movd    %xmm0,  %rdx
#endif
    xorpd   %xmm1,  %xmm1
    cmpeqsd   %xmm1,  %xmm0
    cvtsd2si  %xmm0, %ecx       //set invalid for zero, and produce ILOGB_ZERO. Denorms produce 0 here.
    
#if defined( __i386__ )
    andl    $0x7fffffff,    %edx    //take absolute value
    movl    $32,            %eax
    cmp     $0,             %edx
    cmove   %edx,           %eax
    cmove   FRAME_SIZE(STACKP), %edx
    bsrl    %edx,           %edx
    subl    $(1022+52),     %eax
    addl    %edx,           %eax
    cmp     $0,             %ecx
    cmovne  %ecx,           %eax
    ret

#else
    //take absolute value
    salq    $1,     %rdx
    shrq    $1,     %rdx
    bsrq    %rdx,   %rax
    subl    $(1022+52), %eax
    cmp     $0,     %edx
    cmove   %ecx,   %eax
    ret
#endif

ENTRY( ilogbl )
    movw    8+FRAME_SIZE( STACKP ),     %ax
    andw    $0x7fff,                    %ax
    addw    $1,                         %ax
    cmpw    $1,                         %ax
    jle     1f
    andl    $0xffff,                    %eax
    subl    $16384,                     %eax
    ret
    
1:  je      2f
    
    fldt    FRAME_SIZE(STACKP)          // {x}
    pcmpeqb %xmm0,      %xmm0           // -1
    xor     %eax,		%eax
    cvtss2si %xmm0,     %edx            // set invalid
    fucomip  %st(0), %st(0)             // test for NaN
    setp    %al                         // isnan(x)
    addl    $0x7fffffff,   %eax         // isnan(x) ? 0x80000000 : 0x7fffffff
    ret


2: //zeros / denorms
    fildll  FRAME_SIZE(STACKP)          // load in 0/denormal mantissa as integer
    SUBP    $(32-FRAME_SIZE), STACKP
    fstpt   (STACKP)                    // write it back out
    xorl    %eax,           %eax
    movw    8(STACKP),      %ax
    subl    $(16382+63+16383), %eax
    movl    $0x80000000,    %edx
    cmp     $(-16382+-63+-16383), %eax
    cmove   %edx,           %eax
    ADDP    $(32-FRAME_SIZE), STACKP

    //set invalid if necessary
    xorps   %xmm1,          %xmm1
    movd    %eax,           %xmm0
    cmpeqss %xmm1,          %xmm0
    cvtss2si    %xmm0,      %edx
    ret


ENTRY( logbf )
    //move argument to eax
#if defined( __i386__ )
    movl    FRAME_SIZE(STACKP), %eax
#else
    movd    %xmm0,  %eax
#endif

    andl    $0x7f800000,        %eax        //expose exponent
    addl    $0x00800000,        %eax        //add 1 to exponent, promotes NaN/Inf to 2**31
    cmpl    $0x00800000,        %eax        //test for exceptional case
    jle     1f                              //jump to special code for zeros, NaNs, Infs and denorms
    sarl    $23,                %eax        //move exponent to unit precision
    subl    $128,               %eax        //remove (bias+1)

#if defined( __i386__ )    
    SUBP    $4, STACKP
    movl    %eax,   (STACKP)
    fildl   (STACKP)
    ADDP    $4, STACKP
#else
    cvtsi2ss   %eax,           %xmm0
#endif
    ret
    
1:  je  2f                                  //handle zeros/denorms at 2

    //Infinity and NaN just return |x|
#if defined( __i386__ )
    flds    FRAME_SIZE(STACKP)
    fabs
#else
    movaps  %xmm0,  %xmm1
    cmpeqps %xmm0, %xmm0
    pslld   $31,    %xmm0
    andnps  %xmm1,  %xmm0
#endif
    ret
    
2:  
    //load |x| as if it was an int (equivalent to multiplying denorm by 2**(23+126))
#if defined( __i386__ )
    movl    FRAME_SIZE(STACKP),     %edx
#else
    movd    %xmm0,                  %edx
#endif
    andl    $0x7fffffff,            %edx
    cvtsi2ss  %edx,                 %xmm1       //read the denorm/0 in as an integer

    //move a = x==0 ? 1.0f: 0.0f to xmm
    movl    $0x3f800000,            %eax
    xorl    %ecx,                   %ecx
    cmp     $0,                     %edx
    cmovne  %ecx,                   %eax
    movd    %eax,                   %xmm0       //load x == 0 ? 1.0f : 0.0f     (0.0f is to avoid inexact flag on denorms)

    // do isdenorm(x) ? 0.0f / denorm*2**(23+126) : 1.0 / 0
    divss   %xmm1,                  %xmm0       //set div/0 flag if x is zero

    //extract the exponent of the scaled denorm
    movd    %xmm1,                  %eax        //move the bits back to gpr, formatted as float now
    andl    $0x7f800000,            %eax        //extract exponent
    sarl    $23,                    %eax        //reduce to unit precision
    subl    $(126+23+127),          %eax        //correct for new bias after int->float conversion

    //convert to float
    cvtsi2ss %eax,                  %xmm0       //logb requires its result in floating point format, so convert the unbiased exponent to float
    movd    %xmm0,                  %edx        //and bring it back

    //if( x == 0 ) return -inf
    movl    $0xff800000,            %ecx
    cmp     $(-126-23-127),         %eax
    cmove   %ecx,                   %edx

#if defined( __i386__ )
    SUBP    $4, STACKP
    movl    %edx,  (STACKP)
    flds    (STACKP)
    ADDP    $4, STACKP
#else
    movd    %edx,                   %xmm0
#endif
    ret

ENTRY( logb )
    //move argument to eax
#if defined( __i386__ )
    //We take this roundabout route to avoid a store forwarding stall, which costs us 8 cycles on Yonah.
    movq    FRAME_SIZE(STACKP), %xmm0
    psrlq   $32,    %xmm0
    movd    %xmm0,  %eax
#else
    movd    %xmm0,  %rax
    shrq    $32,    %rax
#endif

    andl    $0x7ff00000,        %eax        //expose exponent
    addl    $0x00100000,        %eax        //add 1 to exponent, promotes NaN/Inf to 2**31
    cmpl    $0x00100000,        %eax        //test for exceptional case
    jle     1f                              //jump to special code for zeros, NaNs, Infs and denorms
    sarl    $20,                %eax        //move exponent to unit precision
    subl    $1024,              %eax        //remove (bias+1)

#if defined( __i386__ )    
    SUBP    $4, STACKP
    movl    %eax,   (STACKP)
    fildl   (STACKP)
    ADDP    $4, STACKP
#else
    cvtsi2sd    %eax,          %xmm0
#endif
    ret
    
1:  je  2f                                  //handle zeros/denorms at 2

    //Infinity and NaN just return |x|
#if defined( __i386__ )
    fldl    FRAME_SIZE(STACKP)
    fabs
#else
    movapd  %xmm0,  %xmm1
    cmpeqpd %xmm0, %xmm0
    psllq   $63,    %xmm0
    andnpd  %xmm1,  %xmm0
#endif
    ret
    
2:  
    SUBP    $(16 - FRAME_SIZE),     STACKP

#if defined( __i386__ )
    //take absolute value of x
    movq    (16-FRAME_SIZE+FRAME_SIZE)(STACKP),             %xmm0
    pcmpeqb %xmm1,                  %xmm1
    psllq   $63,                    %xmm1
    pandn   %xmm0,                  %xmm1
    movq    %xmm1,                  (STACKP)
    
    //convert to double (interpreting bits as int -- same a multiplying denormal by 2**(1022+52))
    fildll  (STACKP)                // { x * 2**(1022+52) }
    fstpt   (STACKP)
    
    //load in the exponent
    xorl    %eax,                   %eax
    movw    8(STACKP),              %ax
    
    //subtract the bias
    subl    $(1022+52+16383),       %eax
    cmp     $(-1022-52-16383),      %eax
    movl    %eax,                   (STACKP)
    
    //deal with the div/0 flag we need to set for the 0 case
    fld1                            // { 1 }
    fldz                            // { 0, 1 }
    fcmovne     %st(1), %st(0)
    fdivrp      %st(1), %st(0)      // set div/0 if necessary, gives either 1 or Inf
    
    fildl       (STACKP)            //{ denormal result, 1 or Inf }
    fmulp                           //{ result }
    
    ADDP    $(16 - FRAME_SIZE),     STACKP
    ret    
    
#else
    //Fetch original value. 
    movd    %xmm0,                  %rax

	//Check to see if original value is zero
	xorpd	%xmm1,					%xmm1
	cmpneqsd	%xmm0,				%xmm1		// x == 0 ? 0 : -1ULL 

	//take absolute value
    salq    $1,                     %rax
    shrq    $1,                     %rax

    //convert to double (interpreting bits as int -- same a multiplying denormal by 2**(1022+52))
    cvtsi2sd    %rax,               %xmm0

	//change test result from -1 to 1.0
	psllq	$54,					%xmm1		// x == 0 ? 0 : 0xFFC0000000000000 
	psrlq	$2,						%xmm1		// x == 0 ? 0 : 0x3ff0000000000000

    //move exponent to units position
    movd    %xmm0,                  %rax
    shrq    $52,                    %rax

    //subtract out bias
    subq    $(1022+52+1023),		%rax
    
    //convert to double precision
    cvtsi2sd    %eax,               %xmm0

	//divide result by x == 0 ? 0 : 1.0 to get correct zero result and set div/0 if x == 0
	divsd	%xmm1,					%xmm0

    ADDP    $(16 - FRAME_SIZE),     STACKP
    ret
    
#endif


ENTRY( logbl )
    SUBP    $(32-FRAME_SIZE),       STACKP
    
    //load exponent
    xorl    %eax,                   %eax
    movw    40(STACKP),             %ax
    
    //take absolute value
    andl    $0x7fff,                %eax
    
    //add 1 to exponent, pushes NaN/Inf into 2**15 bit
    addl    $1,                     %eax
    
    //check for exceptional conditions
    cmpw    $1,                     %ax
    
    //deal with exceptional conditions elsewhere
    jle     1f
    
    //remove the bias+1
    subl    $(16383+1),             %eax
    
    //convert to long double
    movl    %eax,                   (STACKP)

    fildl   (STACKP)
    
    ADDP    $(32-FRAME_SIZE),       STACKP
    ret

1:  je      2f          //deal with zero / denorm elsewhere

    //load value
    fldt    32(STACKP)              // {x}
    fucomi  %st(0), %st(0)          // isnan(x)
    fld     %st(0)                  // {x, x}
    fabs                            // {|x|, x}
    fcmovne %st(1), %st(0)          // {result, x}
    fstp    %st(1)                  // {result}
    ADDP    $(32-FRAME_SIZE),       STACKP
    ret

2:  //denormal/zero
    fildll  32(STACKP)              // load in 0/denormal mantissa as integer. Fails for pseudodenormals, but they "don't happen".
    fstpt   (STACKP)                // write it back out to stack as floating point
    xorl    %eax,           %eax
    movw    8(STACKP),      %ax     // read the exponent
    subl    $(16382+63+16383), %eax // subtract out the bias
    cmp     $(-16382+-63+-16383), %eax //check for zero

    //convert the exponent to float
    movl    %eax,       (STACKP)
    fildl   (STACKP)                // { exp }

    //set div/0 for zero result
    fld1                            // { 1, exp }
    fldz                            // { 0, 1, exp }
    fcmovne     %st(1), %st(0)
    fdivrp      %st(0), %st(1)      // { 1 or Inf, exp } set div/0 if necessary, gives either 1 or Inf
    fmulp                           // { result }
    
    ADDP    $(32-FRAME_SIZE), STACKP
    ret