modfl.s   [plain text]


/*
 *  modfl.s
 *
 *      by Ian Ollmann 
 *
 *  Copyright 2007 Apple Inc. All rights reserved.
 *
 */
 
/*
#include <machine/asm.h>
#define LOCAL_STACK_SIZE	12
#include "abi.h"

#if defined( __LP64__ )
	#define DEST_P			%rdi
	#define LOAD_DEST_P
#else
	#define DEST_P			%eax
	#define LOAD_DEST_P		mov			SECOND_ARG_OFFSET(STACKP), DEST_P
#endif

// I tried branch free code here. Alas there were so many special cases, that 2/3 of the code was patchup after the fidll instruction.
// I've moved some special cases { 0, +-inf, NaN} out, which simplifies things quite a bit on any path you care to follow.

ENTRY( modfl )
        SUBP		$LOCAL_STACK_SIZE, STACKP
        
        LOAD_DEST_P
        fldt		FIRST_ARG_OFFSET(STACKP)			// {x}
        fld         %st(0)                              //  { x, x }
        fabs                                            //  { |x|, x }
        fld         %st(0)                              //  { |x|, |x|, x }
        frndint                                         //  { |i|, |x|, x }
        fucomi      %st(1), %st(0)                      //  { |i|, |x|, x }
        jz          1f
        
        //at this point we know we are a denormal or normal non-integer
        //the next step is to truncate the value. We've already rounded it
        //to an int. We just need to make sure that the right rounding direction
        //applied
        fld1                                            //  { 1, |i|, |x|, x }
        fldz                                            //  { 0, 1, |i|, |x|, x }
        fcmovnbe    %st(1), %st(0)                      //  { 0 or 1, 1, |i|, |x|, x }
        fstp        %st(1)                              //  { 0 or 1, |i|, |x|, x }
        fsubrp      %st(1), %st(0)                      //  { |i|, |x|, x }

        //fix the sign
        fld         %st(0)                              //  { |i|, |i|, |x|, x }
        fchs                                            //  { -|i|, |i|, |x|, x }
        fxch        %st(2)                              //  { |x|, |i|, -|i|, x }
        fucomip     %st(3), %st(0)                      //  { |i|, -|i|, x }
        fcmovne     %st(1), %st(0)                      //  { i result, -|i|, x }
        fstp        %st(1)                              //  { i result, x }
        
        //get the fractional part and store the iresult
        fsubr       %st(0), %st(1)                      //  { i result, f result }
        fstpt       (DEST_P)                            //  { -|i|, f result }
        ADDP		$LOCAL_STACK_SIZE, STACKP
        ret
        
1:      //special case entry for NaN, inf and integers, including zero
        fstp        %st(0)                              //  { |x|, x }
        fstp        %st(0)                              //  { x }

        //handle integers, Inf and zero 
        fldz                                            //  { 0, x }
        fadd        %st(0), %st(1)                      //  { 0, x }  silence NaN
        fucomi      %st(1), %st(0)                      //  { 0, x }
        fchs                                            //  { -0, x }
        fldz                                            //  { 0, -0, x }
        fcmovnbe    %st(1), %st(0)                      //  { +-0, -0, x }
        fcmove      %st(2), %st(0)                      //  { f, -0, x }
        fxch        %st(2)                              //  { x, -0, f }
        fstpt       (DEST_P)                            //  { -0, f }
        fstp        %st(0)                              //  { f }
        ADDP		$LOCAL_STACK_SIZE, STACKP
        ret
 */       

#include <machine/asm.h>
#include "abi.h"

#if defined( __i386__ )
    #define RESULT_P        %edx
#else
    #define RESULT_P        %rdi
#endif

ENTRY( modfl )
    xorl    %eax,                   %eax
    movw    8+FRAME_SIZE(STACKP),   %ax         //load sign + exponent of input
    movl    %eax,                   %ecx        //set aside sign + exponent
    andl    $0x7fff,                %eax        // remove sign
    addl    $(16384-62),            %eax
    cmpw    $(16383+16384-62),      %ax
    jl      1f
    
    //common case of 1.0 <= x < 2**64
    movq    FRAME_SIZE( STACKP),    %xmm0
    subl    $(16383+16384-62),      %eax
    movl    $63,                    %edx
    subl    %eax,                   %edx
    movd    %edx,                   %xmm1
#if defined( __i386__ )
    movl    16+FRAME_SIZE( STACKP ),    RESULT_P
#endif
    pcmpeqb %xmm2,                  %xmm2
    psllq   %xmm1,                  %xmm2
    pand    %xmm2,                  %xmm0
    movq    %xmm0,                  (RESULT_P)
    movw    %cx,                    8(RESULT_P)
    fldt    FRAME_SIZE(STACKP)      // { x, 0 }
    fldt    (RESULT_P)              // { truncl(x), x }
    fucomi  %st(1), %st(0)          // { truncl(x), x }
    je      4f          //if x is an integer goto 4
    
    fsubr   %st(0), %st(1)          // { truncl(x), fract }
    fstpt   (RESULT_P)              // { fract }
    ret
        
1:
#if defined( __i386__ )
    movl    16+FRAME_SIZE( STACKP ),    RESULT_P
#endif
    jae     2f                          //Inf, NaN, big numbers go to 2

    // |x| < 1.0
    pxor    %xmm0,                  %xmm0
    movq    %xmm0,                  (RESULT_P)
    andl    $0x8000,                %ecx
    movw    %cx,                    8(RESULT_P)
    fldt    FRAME_SIZE( STACKP )
    ret
    
2:  
    // |x| >= 2**63 or NAN
    fldz                            // { 0 }
    fldt    FRAME_SIZE( STACKP )    // { x, 0 }
    fucomi  %st(1), %st(0)      
    jp      3f                      // do NaNs elsewhere
    fstpt   ( RESULT_P)             // { 0 }
    fchs                            // { -0 }
    fldz                            // { 0, -0 }
    fcmovb  %st(1), %st(0)          // { fract, -0 }
    fstp    %st(1)
    ret

3:  //NaN                           // { x, 0 }
    fld     %st(0)                  // { x, x, 0 }
    fstpt   (RESULT_P)              // { x, 0 }
    fstp    %st(1)                  // { x }s
    ret

4:  //integer                   // { truncl(x), x }
    fstpt   (RESULT_P)          // { x }
    fldz                        // { 0, x }
    fucomi  %st(1), %st(0)
    fchs                        // { -0, x }
    fldz                        // { 0, -0, x }
    fcmovnb %st(1), %st(0)      // { fract, -0, x }
    fstp    %st(2)              // { -0, fract }
    fstp    %st(0)              // { fract }
    ret