~ubuntu-branches/ubuntu/quantal/gclcvs/quantal

« back to all changes in this revision

Viewing changes to gmp3/mpn/x86/p6/mod_1.asm

  • Committer: Bazaar Package Importer
  • Author(s): Camm Maguire
  • Date: 2004-06-24 15:13:46 UTC
  • Revision ID: james.westby@ubuntu.com-20040624151346-xh0xaaktyyp7aorc
Tags: 2.7.0-26
C_GC_OFFSET is 2 on m68k-linux

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
dnl  Intel P6 mpn_mod_1 -- mpn by limb remainder.
 
2
dnl 
 
3
dnl  P6: 21.5 cycles/limb
 
4
 
 
5
 
 
6
dnl  Copyright (C) 1999, 2000 Free Software Foundation, Inc.
 
7
dnl 
 
8
dnl  This file is part of the GNU MP Library.
 
9
dnl 
 
10
dnl  The GNU MP Library is free software; you can redistribute it and/or
 
11
dnl  modify it under the terms of the GNU Lesser General Public License as
 
12
dnl  published by the Free Software Foundation; either version 2.1 of the
 
13
dnl  License, or (at your option) any later version.
 
14
dnl 
 
15
dnl  The GNU MP Library is distributed in the hope that it will be useful,
 
16
dnl  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
17
dnl  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
18
dnl  Lesser General Public License for more details.
 
19
dnl 
 
20
dnl  You should have received a copy of the GNU Lesser General Public
 
21
dnl  License along with the GNU MP Library; see the file COPYING.LIB.  If
 
22
dnl  not, write to the Free Software Foundation, Inc., 59 Temple Place -
 
23
dnl  Suite 330, Boston, MA 02111-1307, USA.
 
24
 
 
25
 
 
26
include(`../config.m4')
 
27
 
 
28
 
 
29
C mp_limb_t mpn_mod_1 (mp_srcptr src, mp_size_t size, mp_limb_t divisor);
 
30
C mp_limb_t mpn_mod_1c (mp_srcptr src, mp_size_t size, mp_limb_t divisor,
 
31
C                       mp_limb_t carry);
 
32
C mp_limb_t mpn_preinv_mod_1 (mp_srcptr src, mp_size_t size, mp_limb_t divisor,
 
33
C                             mp_limb_t inverse);
 
34
C
 
35
C The code here is in two parts, a simple divl loop and a mul-by-inverse.
 
36
C The divl is used by mod_1 and mod_1c for small sizes, until the savings in
 
37
C the mul-by-inverse can overcome the time to calculate an inverse.
 
38
C preinv_mod_1 goes straight to the mul-by-inverse.
 
39
C
 
40
C The mul-by-inverse normalizes the divisor (or for preinv_mod_1 it's
 
41
C already normalized).  The calculation done is r=a%(d*2^n) followed by a
 
42
C final (r*2^n)%(d*2^n), where a is the dividend, d the divisor, and n is
 
43
C the number of leading zero bits on d.  This means there's no bit shifts in
 
44
C the main loop, at the cost of an extra divide step at the end.
 
45
C
 
46
C The simple divl for mod_1 is able to skip one divide step if high<divisor.
 
47
C For mod_1c the carry parameter is the high of the first divide step, and
 
48
C no attempt is make to skip that step since carry==0 will be very rare.
 
49
C
 
50
C The mul-by-inverse always skips one divide step, but then needs an extra
 
51
C step at the end, unless the divisor was already normalized (n==0).  This
 
52
C leads to different mul-by-inverse thresholds for normalized and
 
53
C unnormalized divisors, in mod_1 and mod_1c.
 
54
C
 
55
C Alternatives:
 
56
C
 
57
C If n is small then the extra divide step could be done by a few shift and
 
58
C trial subtract steps instead of a full divide.  That would probably be 3
 
59
C or 4 cycles/bit, so say up to n=8 might benefit from that over a 21 cycle
 
60
C divide.  However it's considered that small divisors, meaning biggish n,
 
61
C are more likely than small n, and that it's not worth the branch
 
62
C mispredicts of a loop.
 
63
C
 
64
C Past:
 
65
C
 
66
C There used to be some MMX based code for P-II and P-III, roughly following
 
67
C the K7 form, but it was slower (about 24.0 c/l) than the code here.  That
 
68
C code did have an advantage that mod_1 was able to do one less divide step
 
69
C when high<divisor and the divisor unnormalized, but the speed advantage of
 
70
C the current code soon overcomes that.
 
71
C
 
72
C Future:
 
73
C
 
74
C It's not clear whether what's here is optimal.  A rough count of micro-ops
 
75
C on the dependent chain would suggest a couple of cycles could be shaved,
 
76
C perhaps.
 
77
 
 
78
 
 
79
dnl  The following thresholds are the sizes where the multiply by inverse
 
80
dnl  method is used instead of plain divl's.  Minimum value 2 each.
 
81
dnl
 
82
dnl  MUL_NORM_THRESHOLD is for normalized divisors (high bit set),
 
83
dnl  MUL_UNNORM_THRESHOLD for unnormalized divisors.
 
84
dnl
 
85
dnl  With the divl loop at 39 c/l, and the inverse loop at 21.5 c/l but
 
86
dnl  setups for the inverse of about 50, the threshold should be around
 
87
dnl  50/(39-21.5)==2.85.  An unnormalized divisor gets an extra divide step
 
88
dnl  at the end, so if that's about 25 cycles then that threshold might be
 
89
dnl  around (50+25)/(39-21.5) == 4.3.
 
90
 
 
91
deflit(MUL_NORM_THRESHOLD,   4)
 
92
deflit(MUL_UNNORM_THRESHOLD, 5)
 
93
 
 
94
deflit(MUL_NORM_DELTA, eval(MUL_NORM_THRESHOLD - MUL_UNNORM_THRESHOLD))
 
95
 
 
96
 
 
97
defframe(PARAM_INVERSE, 16)  dnl  mpn_preinv_mod_1
 
98
defframe(PARAM_CARRY,   16)  dnl  mpn_mod_1c
 
99
defframe(PARAM_DIVISOR, 12)
 
100
defframe(PARAM_SIZE,     8)
 
101
defframe(PARAM_SRC,      4)
 
102
 
 
103
defframe(SAVE_EBX,    -4)
 
104
defframe(SAVE_ESI,    -8)
 
105
defframe(SAVE_EDI,    -12)
 
106
defframe(SAVE_EBP,    -16)
 
107
 
 
108
defframe(VAR_NORM,    -20)
 
109
defframe(VAR_INVERSE, -24)
 
110
 
 
111
deflit(STACK_SPACE, 24)
 
112
 
 
113
        TEXT
 
114
 
 
115
        ALIGN(16)
 
116
PROLOGUE(mpn_preinv_mod_1)
 
117
deflit(`FRAME',0)
 
118
 
 
119
        movl    PARAM_SRC, %edx
 
120
        subl    $STACK_SPACE, %esp      FRAME_subl_esp(STACK_SPACE)
 
121
 
 
122
        movl    %ebx, SAVE_EBX
 
123
        movl    PARAM_SIZE, %ebx
 
124
 
 
125
        movl    %ebp, SAVE_EBP
 
126
        movl    PARAM_DIVISOR, %ebp
 
127
 
 
128
        movl    %esi, SAVE_ESI
 
129
        movl    PARAM_INVERSE, %eax
 
130
 
 
131
        movl    %edi, SAVE_EDI
 
132
        movl    -4(%edx,%ebx,4), %edi   C src high limb
 
133
 
 
134
        movl    $0, VAR_NORM
 
135
        leal    -8(%edx,%ebx,4), %ecx   C &src[size-2]
 
136
 
 
137
        C
 
138
 
 
139
        movl    %edi, %esi
 
140
        subl    %ebp, %edi              C high-divisor
 
141
 
 
142
        cmovc(  %esi, %edi)             C restore if underflow
 
143
        decl    %ebx
 
144
        jnz     LF(mpn_mod_1,preinv_entry)
 
145
 
 
146
        jmp     LF(mpn_mod_1,done_edi)
 
147
 
 
148
EPILOGUE()
 
149
 
 
150
 
 
151
        ALIGN(16)
 
152
PROLOGUE(mpn_mod_1c)
 
153
deflit(`FRAME',0)
 
154
 
 
155
        movl    PARAM_SIZE, %ecx
 
156
        subl    $STACK_SPACE, %esp      FRAME_subl_esp(STACK_SPACE)
 
157
 
 
158
        movl    %ebp, SAVE_EBP
 
159
        movl    PARAM_DIVISOR, %eax
 
160
 
 
161
        movl    %esi, SAVE_ESI
 
162
        movl    PARAM_CARRY, %edx
 
163
 
 
164
        movl    PARAM_SRC, %esi
 
165
        orl     %ecx, %ecx
 
166
        jz      LF(mpn_mod_1,done_edx)  C result==carry if size==0
 
167
 
 
168
        sarl    $31, %eax
 
169
        movl    PARAM_DIVISOR, %ebp
 
170
 
 
171
        andl    $MUL_NORM_DELTA, %eax
 
172
 
 
173
        addl    $MUL_UNNORM_THRESHOLD, %eax
 
174
 
 
175
        cmpl    %eax, %ecx
 
176
        jb      LF(mpn_mod_1,divide_top)
 
177
 
 
178
 
 
179
        C The carry parameter pretends to be the src high limb.
 
180
 
 
181
        movl    %ebx, SAVE_EBX
 
182
        leal    1(%ecx), %ebx           C size+1
 
183
 
 
184
        movl    %edx, %eax              C carry
 
185
        jmp     LF(mpn_mod_1,mul_by_inverse_1c)
 
186
 
 
187
EPILOGUE()
 
188
 
 
189
 
 
190
        ALIGN(16)
 
191
PROLOGUE(mpn_mod_1)
 
192
deflit(`FRAME',0)
 
193
 
 
194
        movl    PARAM_SIZE, %ecx
 
195
        subl    $STACK_SPACE, %esp      FRAME_subl_esp(STACK_SPACE)
 
196
        movl    $0, %edx                C initial carry (if can't skip a div)
 
197
 
 
198
        movl    %esi, SAVE_ESI
 
199
        movl    PARAM_SRC, %eax
 
200
 
 
201
        movl    %ebp, SAVE_EBP
 
202
        movl    PARAM_DIVISOR, %ebp
 
203
 
 
204
        movl    PARAM_DIVISOR, %esi
 
205
        orl     %ecx, %ecx
 
206
        jz      L(done_edx)
 
207
 
 
208
        movl    -4(%eax,%ecx,4), %eax   C src high limb
 
209
 
 
210
        sarl    $31, %ebp
 
211
 
 
212
        andl    $MUL_NORM_DELTA, %ebp
 
213
 
 
214
        addl    $MUL_UNNORM_THRESHOLD, %ebp
 
215
        cmpl    %esi, %eax              C carry flag if high<divisor
 
216
 
 
217
        cmovc(  %eax, %edx)             C src high limb as initial carry
 
218
        movl    PARAM_SRC, %esi
 
219
 
 
220
        sbbl    $0, %ecx                C size-1 to skip one div
 
221
        jz      L(done_eax)             C done if had size==1
 
222
 
 
223
        cmpl    %ebp, %ecx
 
224
        movl    PARAM_DIVISOR, %ebp
 
225
        jae     L(mul_by_inverse)
 
226
 
 
227
 
 
228
L(divide_top):
 
229
        C eax   scratch (quotient)
 
230
        C ebx
 
231
        C ecx   counter, limbs, decrementing
 
232
        C edx   scratch (remainder)
 
233
        C esi   src
 
234
        C edi
 
235
        C ebp   divisor
 
236
 
 
237
        movl    -4(%esi,%ecx,4), %eax
 
238
 
 
239
        divl    %ebp
 
240
 
 
241
        decl    %ecx
 
242
        jnz     L(divide_top)
 
243
 
 
244
 
 
245
L(done_edx):
 
246
        movl    %edx, %eax
 
247
L(done_eax):
 
248
        movl    SAVE_ESI, %esi
 
249
 
 
250
        movl    SAVE_EBP, %ebp
 
251
        addl    $STACK_SPACE, %esp
 
252
 
 
253
        ret
 
254
 
 
255
 
 
256
C -----------------------------------------------------------------------------
 
257
 
 
258
L(mul_by_inverse):
 
259
        C eax   src high limb
 
260
        C ebx
 
261
        C ecx
 
262
        C edx
 
263
        C esi   src
 
264
        C edi
 
265
        C ebp   divisor
 
266
 
 
267
        movl    %ebx, SAVE_EBX
 
268
        movl    PARAM_SIZE, %ebx
 
269
 
 
270
L(mul_by_inverse_1c):
 
271
        bsrl    %ebp, %ecx              C 31-l
 
272
 
 
273
        movl    %edi, SAVE_EDI
 
274
        xorl    $31, %ecx               C l
 
275
 
 
276
        movl    %ecx, VAR_NORM
 
277
        shll    %cl, %ebp               C d normalized
 
278
 
 
279
        movl    %eax, %edi              C src high -> n2
 
280
        subl    %ebp, %eax
 
281
 
 
282
        cmovnc( %eax, %edi)             C n2-divisor if no underflow
 
283
 
 
284
        movl    $-1, %eax
 
285
        movl    $-1, %edx
 
286
 
 
287
        subl    %ebp, %edx              C (b-d)-1 so  edx:eax = b*(b-d)-1
 
288
        leal    -8(%esi,%ebx,4), %ecx   C &src[size-2]
 
289
 
 
290
        divl    %ebp                    C floor (b*(b-d)-1) / d
 
291
 
 
292
L(preinv_entry):
 
293
        movl    %eax, VAR_INVERSE
 
294
 
 
295
 
 
296
 
 
297
C No special scheduling of loads is necessary in this loop, out of order
 
298
C execution hides the latencies already.
 
299
C
 
300
C The way q1+1 is generated in %ebx and d is moved to %eax for the multiply
 
301
C seems fastest.  The obvious change to generate q1+1 in %eax and then just
 
302
C multiply by %ebp (as per mpn/x86/pentium/mod_1.asm in fact) runs 1 cycle
 
303
C slower, for no obvious reason.
 
304
 
 
305
 
 
306
        ALIGN(16)
 
307
L(inverse_top):
 
308
        C eax   n10 (then scratch)
 
309
        C ebx   scratch (nadj, q1)
 
310
        C ecx   src pointer, decrementing
 
311
        C edx   scratch
 
312
        C esi   n10
 
313
        C edi   n2
 
314
        C ebp   divisor
 
315
 
 
316
        movl    (%ecx), %eax       C next src limb
 
317
        movl    %eax, %esi
 
318
 
 
319
        sarl    $31, %eax          C -n1
 
320
        movl    %ebp, %ebx
 
321
 
 
322
        andl    %eax, %ebx         C -n1 & d
 
323
        negl    %eax               C n1
 
324
 
 
325
        addl    %edi, %eax         C n2+n1
 
326
 
 
327
        mull    VAR_INVERSE        C m*(n2+n1)
 
328
 
 
329
        addl    %esi, %ebx         C nadj = n10 + (-n1 & d), ignoring overflow
 
330
        subl    $4, %ecx
 
331
 
 
332
        C
 
333
 
 
334
        addl    %ebx, %eax         C m*(n2+n1) + nadj, low giving carry flag
 
335
        leal    1(%edi), %ebx      C n2+1
 
336
        movl    %ebp, %eax         C d
 
337
 
 
338
        adcl    %edx, %ebx         C 1 + high(n2<<32 + m*(n2+n1) + nadj) = q1+1
 
339
        jz      L(q1_ff)
 
340
 
 
341
        mull    %ebx               C (q1+1)*d
 
342
 
 
343
        C
 
344
 
 
345
        subl    %eax, %esi         C low n - (q1+1)*d
 
346
 
 
347
        sbbl    %edx, %edi         C high n - (q1+1)*d, 0 or -1
 
348
 
 
349
        andl    %ebp, %edi         C d if underflow
 
350
 
 
351
        addl    %esi, %edi         C remainder with addback if necessary
 
352
 
 
353
        cmpl    PARAM_SRC, %ecx
 
354
        jae     L(inverse_top)
 
355
 
 
356
 
 
357
C -----------------------------------------------------------------------------
 
358
L(inverse_loop_done):
 
359
 
 
360
        C %edi is the remainder modulo d*2^n and now must be reduced to
 
361
        C 0<=r<d by calculating r*2^n mod d*2^n and then right shifting by
 
362
        C n.  If d was already normalized on entry so that n==0 then nothing
 
363
        C is needed here.  The chance of n==0 is low, but it's true of say
 
364
        C PP from gmp-impl.h.
 
365
        C
 
366
        C eax
 
367
        C ebx
 
368
        C ecx
 
369
        C edx
 
370
        C esi   
 
371
        C edi   remainder
 
372
        C ebp   divisor (normalized)
 
373
 
 
374
        movl    VAR_NORM, %ecx
 
375
        movl    $0, %esi
 
376
 
 
377
        orl     %ecx, %ecx
 
378
        jz      L(done_edi)
 
379
 
 
380
 
 
381
        C Here use %edi=n10 and %esi=n2, opposite to the loop above.
 
382
        C
 
383
        C The q1=0xFFFFFFFF case is handled with an sbbl to adjust q1+1
 
384
        C back, rather than q1_ff special case code.  This is simpler and
 
385
        C costs only 2 uops.
 
386
        
 
387
        shldl(  %cl, %edi, %esi)
 
388
 
 
389
        shll    %cl, %edi
 
390
 
 
391
        movl    %edi, %eax         C n10
 
392
        movl    %ebp, %ebx         C d
 
393
 
 
394
        sarl    $31, %eax          C -n1
 
395
 
 
396
        andl    %eax, %ebx         C -n1 & d
 
397
        negl    %eax               C n1
 
398
 
 
399
        addl    %edi, %ebx         C nadj = n10 + (-n1 & d), ignoring overflow
 
400
        addl    %esi, %eax         C n2+n1
 
401
 
 
402
        mull    VAR_INVERSE        C m*(n2+n1)
 
403
 
 
404
        C
 
405
 
 
406
        addl    %ebx, %eax         C m*(n2+n1) + nadj, low giving carry flag
 
407
        leal    1(%esi), %ebx      C n2+1
 
408
 
 
409
        adcl    %edx, %ebx         C 1 + high(n2<<32 + m*(n2+n1) + nadj) = q1+1
 
410
 
 
411
        sbbl    $0, %ebx
 
412
        movl    %ebp, %eax         C d
 
413
 
 
414
        mull    %ebx               C (q1+1)*d
 
415
 
 
416
        movl    SAVE_EBX, %ebx
 
417
 
 
418
        C
 
419
 
 
420
        subl    %eax, %edi         C low  n - (q1+1)*d is remainder
 
421
 
 
422
        sbbl    %edx, %esi         C high n - (q1+1)*d, 0 or -1
 
423
 
 
424
        andl    %ebp, %esi
 
425
        movl    SAVE_EBP, %ebp
 
426
 
 
427
        leal    (%esi,%edi), %eax  C remainder
 
428
        movl    SAVE_ESI, %esi
 
429
 
 
430
        shrl    %cl, %eax          C denorm remainder
 
431
        movl    SAVE_EDI, %edi
 
432
        addl    $STACK_SPACE, %esp
 
433
 
 
434
        ret
 
435
 
 
436
 
 
437
L(done_edi):
 
438
        movl    SAVE_EBX, %ebx
 
439
        movl    %edi, %eax
 
440
 
 
441
        movl    SAVE_ESI, %esi
 
442
 
 
443
        movl    SAVE_EDI, %edi
 
444
 
 
445
        movl    SAVE_EBP, %ebp
 
446
        addl    $STACK_SPACE, %esp
 
447
 
 
448
        ret
 
449
 
 
450
 
 
451
C -----------------------------------------------------------------------------
 
452
C
 
453
C Special case for q1=0xFFFFFFFF, giving q=0xFFFFFFFF meaning the low dword
 
454
C of q*d is simply -d and the remainder n-q*d = n10+d.
 
455
C
 
456
C This is reached only very rarely.
 
457
 
 
458
L(q1_ff):
 
459
        C eax   (divisor)
 
460
        C ebx   (q1+1 == 0)
 
461
        C ecx   src pointer
 
462
        C edx
 
463
        C esi   n10
 
464
        C edi   (n2)
 
465
        C ebp   divisor
 
466
 
 
467
        leal    (%ebp,%esi), %edi       C n-q*d remainder -> next n2
 
468
 
 
469
        cmpl    PARAM_SRC, %ecx
 
470
        jae     L(inverse_top)
 
471
 
 
472
        jmp     L(inverse_loop_done)
 
473
 
 
474
 
 
475
EPILOGUE()