diff options
Diffstat (limited to 'sysdeps/alpha/ldiv.S')
-rw-r--r-- | sysdeps/alpha/ldiv.S | 228 |
1 files changed, 165 insertions, 63 deletions
diff --git a/sysdeps/alpha/ldiv.S b/sysdeps/alpha/ldiv.S index 81b48cd559..c90edfb784 100644 --- a/sysdeps/alpha/ldiv.S +++ b/sysdeps/alpha/ldiv.S @@ -1,4 +1,4 @@ -/* Copyright (C) 1996, 1997, 2001 Free Software Foundation, Inc. +/* Copyright (C) 1996, 1997, 2001, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Richard Henderson <rth@tamu.edu>. @@ -17,93 +17,195 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -#include <sysdep.h> +#include "div_libc.h" -#ifdef __linux__ -# include <asm/gentrap.h> -# include <asm/pal.h> +#undef FRAME +#ifdef __alpha_fix__ +#define FRAME 0 #else -# include <machine/pal.h> +#define FRAME 16 #endif +#undef X +#undef Y +#define X $17 +#define Y $18 + .set noat .align 4 .globl ldiv .ent ldiv ldiv: - .frame sp, 0, ra + .frame sp, FRAME, ra +#if FRAME > 0 + lda sp, -FRAME(sp) +#endif #ifdef PROF + .set macro ldgp gp, 0(pv) lda AT, _mcount jsr AT, (AT), _mcount + .set nomacro .prologue 1 #else .prologue 0 #endif -#define divisor t1 -#define mask t2 -#define quotient t3 -#define modulus t4 -#define tmp1 t5 -#define tmp2 t6 -#define compare t7 - - /* find correct sign for input to unsigned divide loop. */ - mov a1, modulus # e0 : - mov a2, divisor # .. e1 : - negq a1, tmp1 # e0 : - negq a2, tmp2 # .. e1 : - mov zero, quotient # e0 : - mov 1, mask # .. e1 : - cmovlt a1, tmp1, modulus # e0 : - cmovlt a2, tmp2, divisor # .. e1 : - beq a2, $divbyzero # e1 : - unop # : - - /* shift divisor left. */ -1: cmpult divisor, modulus, compare # e0 : - blt divisor, 2f # .. e1 : - addq divisor, divisor, divisor # e0 : - addq mask, mask, mask # .. e1 : - bne compare, 1b # e1 : - unop # : - - /* start to go right again. */ -2: addq quotient, mask, tmp2 # e1 : - srl mask, 1, mask # .. e0 : - cmpule divisor, modulus, compare # e0 : - subq modulus, divisor, tmp1 # .. e1 : - cmovne compare, tmp2, quotient # e1 : - srl divisor, 1, divisor # .. e0 : - cmovne compare, tmp1, modulus # e0 : - bne mask, 2b # .. e1 : - - /* find correct sign for result. */ - xor a1, a2, compare # e0 : - negq quotient, tmp1 # .. e1 : - negq modulus, tmp2 # e0 : - cmovlt compare, tmp1, quotient # .. e1 : - cmovlt a1, tmp2, modulus # e1 : - - /* and store it away in the structure. */ -9: stq quotient, 0(a0) # .. e0 : - mov a0, v0 # e1 : - stq modulus, 8(a0) # .. e0 : - ret # e1 : + beq Y, $divbyzero + + _ITOFT2 X, $f0, 0, Y, $f1, 8 + + .align 4 + cvtqt $f0, $f0 + cvtqt $f1, $f1 + divt/c $f0, $f1, $f0 + unop + + /* Check to see if X fit in the double as an exact value. */ + sll X, (64-53), AT + sra AT, (64-53), AT + cmpeq X, AT, AT + beq AT, $x_big + + /* If we get here, we're expecting exact results from the division. + Do nothing else besides convert and clean up. */ + cvttq/c $f0, $f0 + _FTOIT $f0, $0, 0 + +$egress: + mulq $0, Y, $1 + subq X, $1, $1 + + stq $0, 0($16) + stq $1, 8($16) + mov $16, $0 + +#if FRAME > 0 + lda sp, FRAME(sp) +#endif + ret + + .align 4 +$x_big: + /* If we get here, X is large enough that we don't expect exact + results, and neither X nor Y got mis-translated for the fp + division. Our task is to take the fp result, figure out how + far it's off from the correct result and compute a fixup. */ + +#define Q v0 /* quotient */ +#define R t0 /* remainder */ +#define SY t1 /* scaled Y */ +#define S t2 /* scalar */ +#define QY t3 /* Q*Y */ + + /* The fixup code below can only handle unsigned values. */ + or X, Y, AT + mov $31, t5 + blt AT, $fix_sign_in +$fix_sign_in_ret1: + cvttq/c $f0, $f0 + + _FTOIT $f0, Q, 8 + .align 3 +$fix_sign_in_ret2: + mulq Q, Y, QY + + .align 4 + subq QY, X, R + mov Y, SY + mov 1, S + bgt R, $q_high + +$q_high_ret: + subq X, QY, R + mov Y, SY + mov 1, S + bgt R, $q_low + +$q_low_ret: + negq Q, t4 + cmovlbs t5, t4, Q + br $egress + + .align 4 + /* The quotient that we computed was too large. We need to reduce + it by S such that Y*S >= R. Obviously the closer we get to the + correct value the better, but overshooting high is ok, as we'll + fix that up later. */ +0: + addq SY, SY, SY + addq S, S, S +$q_high: + cmpult SY, R, AT + bne AT, 0b + + subq Q, S, Q + unop + subq QY, SY, QY + br $q_high_ret + + .align 4 + /* The quotient that we computed was too small. Divide Y by the + current remainder (R) and add that to the existing quotient (Q). + The expectation, of course, is that R is much smaller than X. */ + /* Begin with a shift-up loop. Compute S such that Y*S >= R. We + already have a copy of Y in SY and the value 1 in S. */ +0: + addq SY, SY, SY + addq S, S, S +$q_low: + cmpult SY, R, AT + bne AT, 0b + + /* Shift-down and subtract loop. Each iteration compares our scaled + Y (SY) with the remainder (R); if SY <= R then X is divisible by + Y's scalar (S) so add it to the quotient (Q). */ +2: addq Q, S, t3 + srl S, 1, S + cmpule SY, R, AT + subq R, SY, t4 + + cmovne AT, t3, Q + cmovne AT, t4, R + srl SY, 1, SY + bne S, 2b + + br $q_low_ret + + .align 4 +$fix_sign_in: + /* If we got here, then X|Y is negative. Need to adjust everything + such that we're doing unsigned division in the fixup loop. */ + /* T5 is true if result should be negative. */ + xor X, Y, AT + cmplt AT, 0, t5 + cmplt X, 0, AT + negq X, t0 + + cmovne AT, t0, X + cmplt Y, 0, AT + negq Y, t0 + + cmovne AT, t0, Y + blbc t5, $fix_sign_in_ret1 + + cvttq/c $f0, $f0 + _FTOIT $f0, Q, 8 + .align 3 + negq Q, Q + br $fix_sign_in_ret2 $divbyzero: mov a0, v0 lda a0, GEN_INTDIV call_pal PAL_gentrap - - /* if trap returns, return zero. */ stq zero, 0(v0) stq zero, 8(v0) - ret - .end ldiv +#if FRAME > 0 + lda sp, FRAME(sp) +#endif + ret -weak_alias(ldiv, lldiv) -weak_alias(ldiv, imaxdiv) + .end ldiv |