* sysdeps/alpha/div_libc.h (_ITOFS, _ITOFT, _FTOIT, _ITOFT2): New.
[glibc-ports.git] / sysdeps / alpha / remq.S
bloba8795c8d24b38730c272aa4b8f6b92cb68d8ae49
1 /* Copyright (C) 2004 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
9    The GNU C Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library; if not, write to the Free
16    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
17    02111-1307 USA.  */
19 #include "div_libc.h"
22 /* 64-bit signed long remainder.  These are not normal C functions.  Argument
23    registers are t10 and t11, the result goes in t12.  Only t12 and AT may
24    be clobbered.
26    Theory of operation here is that we can use the FPU divider for virtually
27    all operands that we see: all dividend values between -2**53 and 2**53-1
28    can be computed directly.  Note that divisor values need not be checked
29    against that range because the rounded fp value will be close enough such
30    that the quotient is < 1, which will properly be truncated to zero when we
31    convert back to integer.
33    When the dividend is outside the range for which we can compute exact
34    results, we use the fp quotent as an estimate from which we begin refining
35    an exact integral value.  This reduces the number of iterations in the
36    shift-and-subtract loop significantly.  */
38         .text
39         .align  4
40         .globl  __remq
41         .type   __remq, @function
42         .usepv  __remq, no
44         cfi_startproc
45         cfi_return_column (RA)
46 __remq:
47         lda     sp, -FRAME(sp)
48         cfi_def_cfa_offset (FRAME)
49         CALL_MCOUNT
51         /* Get the fp divide insn issued as quickly as possible.  After
52            that's done, we have at least 22 cycles until its results are
53            ready -- all the time in the world to figure out how we're
54            going to use the results.  */
55         stt     $f0, 0(sp)
56         stt     $f1, 8(sp)
57         beq     Y, DIVBYZERO
58         cfi_rel_offset ($f0, 0)
59         cfi_rel_offset ($f1, 8)
61         _ITOFT2 X, $f0, 16, Y, $f1, 24
62         cvtqt   $f0, $f0
63         cvtqt   $f1, $f1
64         divt/c  $f0, $f1, $f0
66         /* Check to see if X fit in the double as an exact value.  */
67         sll     X, (64-53), AT
68         ldt     $f1, 8(sp)
69         sra     AT, (64-53), AT
70         cmpeq   X, AT, AT
71         beq     AT, $x_big
73         /* If we get here, we're expecting exact results from the division.
74            Do nothing else besides convert, compute remainder, clean up.  */
75         cvttq/c $f0, $f0
76         _FTOIT  $f0, AT, 16
77         mulq    AT, Y, AT
78         ldt     $f0, 0(sp)
79         cfi_restore ($f1)
80         cfi_remember_state
81         cfi_restore ($f0)
82         cfi_def_cfa_offset (0)
83         lda     sp, FRAME(sp)
84         subq    X, AT, RV
85         ret     $31, (RA), 1
87         .align  4
88         cfi_restore_state
89 $x_big:
90         /* If we get here, X is large enough that we don't expect exact
91            results, and neither X nor Y got mis-translated for the fp
92            division.  Our task is to take the fp result, figure out how
93            far it's off from the correct result and compute a fixup.  */
94         stq     t0, 16(sp)
95         stq     t1, 24(sp)
96         stq     t2, 32(sp)
97         stq     t5, 40(sp)
98         cfi_rel_offset (t0, 16)
99         cfi_rel_offset (t1, 24)
100         cfi_rel_offset (t2, 32)
101         cfi_rel_offset (t5, 40)
103 #define Q       t0              /* quotient */
104 #define R       RV              /* remainder */
105 #define SY      t1              /* scaled Y */
106 #define S       t2              /* scalar */
107 #define QY      t3              /* Q*Y */
109         /* The fixup code below can only handle unsigned values.  */
110         or      X, Y, AT
111         mov     $31, t5
112         blt     AT, $fix_sign_in
113 $fix_sign_in_ret1:
114         cvttq/c $f0, $f0
116         _FTOIT  $f0, Q, 8
117         .align  3
118 $fix_sign_in_ret2:
119         mulq    Q, Y, QY
120         stq     t4, 8(sp)
122         ldt     $f0, 0(sp)
123         unop
124         cfi_rel_offset (t4, 8)
125         cfi_restore ($f0)
126         stq     t3, 0(sp)
127         unop
128         cfi_rel_offset (t3, 0)
130         subq    QY, X, R
131         mov     Y, SY
132         mov     1, S
133         bgt     R, $q_high
135 $q_high_ret:
136         subq    X, QY, R
137         mov     Y, SY
138         mov     1, S
139         bgt     R, $q_low
141 $q_low_ret:
142         ldq     t0, 16(sp)
143         ldq     t1, 24(sp)
144         ldq     t2, 32(sp)
145         bne     t5, $fix_sign_out
147 $fix_sign_out_ret:
148         ldq     t3, 0(sp)
149         ldq     t4, 8(sp)
150         ldq     t5, 40(sp)
151         lda     sp, FRAME(sp)
152         cfi_remember_state
153         cfi_restore (t0)
154         cfi_restore (t1)
155         cfi_restore (t2)
156         cfi_restore (t3)
157         cfi_restore (t4)
158         cfi_restore (t5)
159         cfi_def_cfa_offset (0)
160         ret     $31, (RA), 1
162         .align  4
163         cfi_restore_state
164         /* The quotient that we computed was too large.  We need to reduce
165            it by S such that Y*S >= R.  Obviously the closer we get to the
166            correct value the better, but overshooting high is ok, as we'll
167            fix that up later.  */
169         addq    SY, SY, SY
170         addq    S, S, S
171 $q_high:
172         cmpult  SY, R, AT
173         bne     AT, 0b
175         subq    Q, S, Q
176         unop
177         subq    QY, SY, QY
178         br      $q_high_ret
180         .align  4
181         /* The quotient that we computed was too small.  Divide Y by the 
182            current remainder (R) and add that to the existing quotient (Q).
183            The expectation, of course, is that R is much smaller than X.  */
184         /* Begin with a shift-up loop.  Compute S such that Y*S >= R.  We
185            already have a copy of Y in SY and the value 1 in S.  */
187         addq    SY, SY, SY
188         addq    S, S, S
189 $q_low:
190         cmpult  SY, R, AT
191         bne     AT, 0b
193         /* Shift-down and subtract loop.  Each iteration compares our scaled
194            Y (SY) with the remainder (R); if SY <= R then X is divisible by
195            Y's scalar (S) so add it to the quotient (Q).  */
196 2:      addq    Q, S, t3
197         srl     S, 1, S
198         cmpule  SY, R, AT
199         subq    R, SY, t4
201         cmovne  AT, t3, Q
202         cmovne  AT, t4, R
203         srl     SY, 1, SY
204         bne     S, 2b
206         br      $q_low_ret
208         .align  4
209 $fix_sign_in:
210         /* If we got here, then X|Y is negative.  Need to adjust everything
211            such that we're doing unsigned division in the fixup loop.  */
212         /* T5 records the changes we had to make:
213                 bit 0:  set if X was negated.  Note that the sign of the
214                         remainder follows the sign of the divisor.
215                 bit 2:  set if Y was negated.
216         */
217         xor     X, Y, t1
218         cmplt   X, 0, t5
219         negq    X, t0
220         cmovne  t5, t0, X
222         cmplt   Y, 0, AT
223         negq    Y, t0
224         s4addq  AT, t5, t5
225         cmovne  AT, t0, Y
227         bge     t1, $fix_sign_in_ret1
228         cvttq/c $f0, $f0
229         _FTOIT  $f0, Q, 8
230         .align  3
231         negq    Q, Q
232         br      $fix_sign_in_ret2
234         .align  4
235 $fix_sign_out:
236         /* Now we get to undo what we did above.  */
237         /* ??? Is this really faster than just increasing the size of
238            the stack frame and storing X and Y in memory?  */
239         and     t5, 4, AT
240         negq    Y, t4
241         cmovne  AT, t4, Y
243         negq    X, t4
244         cmovlbs t5, t4, X
245         negq    RV, t4
246         cmovlbs t5, t4, RV
248         br      $fix_sign_out_ret
250         cfi_endproc
251         .size   __remq, .-__remq
253         DO_DIVBYZERO