Annotation of OpenXM_contrib/gmp/mpn/x86/k6/sqr_basecase.asm, Revision 1.1.1.1
1.1 maekawa 1: dnl AMD K6 mpn_sqr_basecase -- square an mpn number.
2: dnl
3: dnl K6: approx 4.7 cycles per cross product, or 9.2 cycles per triangular
4: dnl product (measured on the speed difference between 17 and 33 limbs,
5: dnl which is roughly the Karatsuba recursing range).
6:
7:
8: dnl Copyright (C) 1999, 2000 Free Software Foundation, Inc.
9: dnl
10: dnl This file is part of the GNU MP Library.
11: dnl
12: dnl The GNU MP Library is free software; you can redistribute it and/or
13: dnl modify it under the terms of the GNU Lesser General Public License as
14: dnl published by the Free Software Foundation; either version 2.1 of the
15: dnl License, or (at your option) any later version.
16: dnl
17: dnl The GNU MP Library is distributed in the hope that it will be useful,
18: dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
19: dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20: dnl Lesser General Public License for more details.
21: dnl
22: dnl You should have received a copy of the GNU Lesser General Public
23: dnl License along with the GNU MP Library; see the file COPYING.LIB. If
24: dnl not, write to the Free Software Foundation, Inc., 59 Temple Place -
25: dnl Suite 330, Boston, MA 02111-1307, USA.
26:
27:
28: include(`../config.m4')
29:
30:
31: dnl KARATSUBA_SQR_THRESHOLD_MAX is the maximum KARATSUBA_SQR_THRESHOLD this
32: dnl code supports. This value is used only by the tune program to know
33: dnl what it can go up to. (An attempt to compile with a bigger value will
34: dnl trigger some m4_assert()s in the code, making the build fail.)
35: dnl
36: dnl The value is determined by requiring the displacements in the unrolled
37: dnl addmul to fit in single bytes. This means a maximum UNROLL_COUNT of
38: dnl 63, giving a maximum KARATSUBA_SQR_THRESHOLD of 66.
39:
40: deflit(KARATSUBA_SQR_THRESHOLD_MAX, 66)
41:
42:
43: dnl Allow a value from the tune program to override config.m4.
44:
45: ifdef(`KARATSUBA_SQR_THRESHOLD_OVERRIDE',
46: `define(`KARATSUBA_SQR_THRESHOLD',KARATSUBA_SQR_THRESHOLD_OVERRIDE)')
47:
48:
49: dnl UNROLL_COUNT is the number of code chunks in the unrolled addmul. The
50: dnl number required is determined by KARATSUBA_SQR_THRESHOLD, since
51: dnl mpn_sqr_basecase only needs to handle sizes < KARATSUBA_SQR_THRESHOLD.
52: dnl
53: dnl The first addmul is the biggest, and this takes the second least
54: dnl significant limb and multiplies it by the third least significant and
55: dnl up. Hence for a maximum operand size of KARATSUBA_SQR_THRESHOLD-1
56: dnl limbs, UNROLL_COUNT needs to be KARATSUBA_SQR_THRESHOLD-3.
57:
58: m4_config_gmp_mparam(`KARATSUBA_SQR_THRESHOLD')
59: deflit(UNROLL_COUNT, eval(KARATSUBA_SQR_THRESHOLD-3))
60:
61:
62: C void mpn_sqr_basecase (mp_ptr dst, mp_srcptr src, mp_size_t size);
63: C
64: C The algorithm is essentially the same as mpn/generic/sqr_basecase.c, but a
65: C lot of function call overheads are avoided, especially when the given size
66: C is small.
67: C
68: C The code size might look a bit excessive, but not all of it is executed
69: C and so won't fill up the code cache. The 1x1, 2x2 and 3x3 special cases
70: C clearly apply only to those sizes; mid sizes like 10x10 only need part of
71: C the unrolled addmul; and big sizes like 35x35 that do need all of it will
72: C at least be getting value for money, because 35x35 spends something like
73: C 5780 cycles here.
74: C
75: C Different values of UNROLL_COUNT give slightly different speeds, between
76: C 9.0 and 9.2 c/tri-prod measured on the difference between 17 and 33 limbs.
77: C This isn't a big difference, but it's presumably some alignment effect
78: C which if understood could give a simple speedup.
79:
80: defframe(PARAM_SIZE,12)
81: defframe(PARAM_SRC, 8)
82: defframe(PARAM_DST, 4)
83:
84: .text
85: ALIGN(32)
86: PROLOGUE(mpn_sqr_basecase)
87: deflit(`FRAME',0)
88:
89: movl PARAM_SIZE, %ecx
90: movl PARAM_SRC, %eax
91:
92: cmpl $2, %ecx
93: je L(two_limbs)
94:
95: movl PARAM_DST, %edx
96: ja L(three_or_more)
97:
98:
99: C -----------------------------------------------------------------------------
100: C one limb only
101: C eax src
102: C ebx
103: C ecx size
104: C edx dst
105:
106: movl (%eax), %eax
107: movl %edx, %ecx
108:
109: mull %eax
110:
111: movl %eax, (%ecx)
112: movl %edx, 4(%ecx)
113: ret
114:
115:
116: C -----------------------------------------------------------------------------
117: ALIGN(16)
118: L(two_limbs):
119: C eax src
120: C ebx
121: C ecx size
122: C edx dst
123:
124: pushl %ebx
125: movl %eax, %ebx C src
126: deflit(`FRAME',4)
127:
128: movl (%ebx), %eax
129: movl PARAM_DST, %ecx
130:
131: mull %eax C src[0]^2
132:
133: movl %eax, (%ecx)
134: movl 4(%ebx), %eax
135:
136: movl %edx, 4(%ecx)
137:
138: mull %eax C src[1]^2
139:
140: movl %eax, 8(%ecx)
141: movl (%ebx), %eax
142:
143: movl %edx, 12(%ecx)
144: movl 4(%ebx), %edx
145:
146: mull %edx C src[0]*src[1]
147:
148: addl %eax, 4(%ecx)
149:
150: adcl %edx, 8(%ecx)
151: adcl $0, 12(%ecx)
152:
153: popl %ebx
154: addl %eax, 4(%ecx)
155:
156: adcl %edx, 8(%ecx)
157: adcl $0, 12(%ecx)
158:
159: ret
160:
161:
162: C -----------------------------------------------------------------------------
163: L(three_or_more):
164: deflit(`FRAME',0)
165: cmpl $4, %ecx
166: jae L(four_or_more)
167:
168:
169: C -----------------------------------------------------------------------------
170: C three limbs
171: C eax src
172: C ecx size
173: C edx dst
174:
175: pushl %ebx
176: movl %eax, %ebx C src
177:
178: movl (%ebx), %eax
179: movl %edx, %ecx C dst
180:
181: mull %eax C src[0] ^ 2
182:
183: movl %eax, (%ecx)
184: movl 4(%ebx), %eax
185:
186: movl %edx, 4(%ecx)
187: pushl %esi
188:
189: mull %eax C src[1] ^ 2
190:
191: movl %eax, 8(%ecx)
192: movl 8(%ebx), %eax
193:
194: movl %edx, 12(%ecx)
195: pushl %edi
196:
197: mull %eax C src[2] ^ 2
198:
199: movl %eax, 16(%ecx)
200: movl (%ebx), %eax
201:
202: movl %edx, 20(%ecx)
203: movl 4(%ebx), %edx
204:
205: mull %edx C src[0] * src[1]
206:
207: movl %eax, %esi
208: movl (%ebx), %eax
209:
210: movl %edx, %edi
211: movl 8(%ebx), %edx
212:
213: pushl %ebp
214: xorl %ebp, %ebp
215:
216: mull %edx C src[0] * src[2]
217:
218: addl %eax, %edi
219: movl 4(%ebx), %eax
220:
221: adcl %edx, %ebp
222:
223: movl 8(%ebx), %edx
224:
225: mull %edx C src[1] * src[2]
226:
227: addl %eax, %ebp
228:
229: adcl $0, %edx
230:
231:
232: C eax will be dst[5]
233: C ebx
234: C ecx dst
235: C edx dst[4]
236: C esi dst[1]
237: C edi dst[2]
238: C ebp dst[3]
239:
240: xorl %eax, %eax
241: addl %esi, %esi
242: adcl %edi, %edi
243: adcl %ebp, %ebp
244: adcl %edx, %edx
245: adcl $0, %eax
246:
247: addl %esi, 4(%ecx)
248: adcl %edi, 8(%ecx)
249: adcl %ebp, 12(%ecx)
250:
251: popl %ebp
252: popl %edi
253:
254: adcl %edx, 16(%ecx)
255:
256: popl %esi
257: popl %ebx
258:
259: adcl %eax, 20(%ecx)
260: ASSERT(nc)
261:
262: ret
263:
264:
265: C -----------------------------------------------------------------------------
266:
267: defframe(SAVE_EBX, -4)
268: defframe(SAVE_ESI, -8)
269: defframe(SAVE_EDI, -12)
270: defframe(SAVE_EBP, -16)
271: defframe(VAR_COUNTER,-20)
272: defframe(VAR_JMP, -24)
273: deflit(STACK_SPACE, 24)
274:
275: ALIGN(16)
276: L(four_or_more):
277:
278: C eax src
279: C ebx
280: C ecx size
281: C edx dst
282: C esi
283: C edi
284: C ebp
285:
286: C First multiply src[0]*src[1..size-1] and store at dst[1..size].
287: C
288: C A test was done calling mpn_mul_1 here to get the benefit of its unrolled
289: C loop, but this was only a tiny speedup; at 35 limbs it took 24 cycles off
290: C a 5780 cycle operation, which is not surprising since the loop here is 8
291: C c/l and mpn_mul_1 is 6.25 c/l.
292:
293: subl $STACK_SPACE, %esp deflit(`FRAME',STACK_SPACE)
294:
295: movl %edi, SAVE_EDI
296: leal 4(%edx), %edi
297:
298: movl %ebx, SAVE_EBX
299: leal 4(%eax), %ebx
300:
301: movl %esi, SAVE_ESI
302: xorl %esi, %esi
303:
304: movl %ebp, SAVE_EBP
305:
306: C eax
307: C ebx src+4
308: C ecx size
309: C edx
310: C esi
311: C edi dst+4
312: C ebp
313:
314: movl (%eax), %ebp C multiplier
315: leal -1(%ecx), %ecx C size-1, and pad to a 16 byte boundary
316:
317:
318: ALIGN(16)
319: L(mul_1):
320: C eax scratch
321: C ebx src ptr
322: C ecx counter
323: C edx scratch
324: C esi carry
325: C edi dst ptr
326: C ebp multiplier
327:
328: movl (%ebx), %eax
329: addl $4, %ebx
330:
331: mull %ebp
332:
333: addl %esi, %eax
334: movl $0, %esi
335:
336: adcl %edx, %esi
337:
338: movl %eax, (%edi)
339: addl $4, %edi
340:
341: loop L(mul_1)
342:
343:
344: C Addmul src[n]*src[n+1..size-1] at dst[2*n-1...], for each n=1..size-2.
345: C
346: C The last two addmuls, which are the bottom right corner of the product
347: C triangle, are left to the end. These are src[size-3]*src[size-2,size-1]
348: C and src[size-2]*src[size-1]. If size is 4 then it's only these corner
349: C cases that need to be done.
350: C
351: C The unrolled code is the same as mpn_addmul_1(), see that routine for some
352: C comments.
353: C
354: C VAR_COUNTER is the outer loop, running from -(size-4) to -1, inclusive.
355: C
356: C VAR_JMP is the computed jump into the unrolled code, stepped by one code
357: C chunk each outer loop.
358: C
359: C K6 doesn't do any branch prediction on indirect jumps, which is good
360: C actually because it's a different target each time. The unrolled addmul
361: C is about 3 cycles/limb faster than a simple loop, so the 6 cycle cost of
362: C the indirect jump is quickly recovered.
363:
364:
365: dnl This value is also implicitly encoded in a shift and add.
366: dnl
367: deflit(CODE_BYTES_PER_LIMB, 15)
368:
369: dnl With the unmodified &src[size] and &dst[size] pointers, the
370: dnl displacements in the unrolled code fit in a byte for UNROLL_COUNT
371: dnl values up to 31. Above that an offset must be added to them.
372: dnl
373: deflit(OFFSET,
374: ifelse(eval(UNROLL_COUNT>31),1,
375: eval((UNROLL_COUNT-31)*4),
376: 0))
377:
378: C eax
379: C ebx &src[size]
380: C ecx
381: C edx
382: C esi carry
383: C edi &dst[size]
384: C ebp
385:
386: movl PARAM_SIZE, %ecx
387: movl %esi, (%edi)
388:
389: subl $4, %ecx
390: jz L(corner)
391:
392: movl %ecx, %edx
393: ifelse(OFFSET,0,,
394: ` subl $OFFSET, %ebx')
395:
396: shll $4, %ecx
397: ifelse(OFFSET,0,,
398: ` subl $OFFSET, %edi')
399:
400: negl %ecx
401:
402: ifdef(`PIC',`
403: call L(pic_calc)
404: L(here):
405: ',`
406: leal L(unroll_inner_end)-eval(2*CODE_BYTES_PER_LIMB)(%ecx,%edx), %ecx
407: ')
408: negl %edx
409:
410:
411: C The calculated jump mustn't be before the start of the available
412: C code. This is the limitation UNROLL_COUNT puts on the src operand
413: C size, but checked here using the jump address directly.
414: C
415: ASSERT(ae,`
416: movl_text_address( L(unroll_inner_start), %eax)
417: cmpl %eax, %ecx
418: ')
419:
420:
421: C -----------------------------------------------------------------------------
422: ALIGN(16)
423: L(unroll_outer_top):
424: C eax
425: C ebx &src[size], constant
426: C ecx VAR_JMP
427: C edx VAR_COUNTER, limbs, negative
428: C esi high limb to store
429: C edi dst ptr, high of last addmul
430: C ebp
431:
432: movl -12+OFFSET(%ebx,%edx,4), %ebp C multiplier
433: movl %edx, VAR_COUNTER
434:
435: movl -8+OFFSET(%ebx,%edx,4), %eax C first limb of multiplicand
436:
437: mull %ebp
438:
439: testb $1, %cl
440:
441: movl %edx, %esi C high carry
442: movl %ecx, %edx C jump
443:
444: movl %eax, %ecx C low carry
445: leal CODE_BYTES_PER_LIMB(%edx), %edx
446:
447: movl %edx, VAR_JMP
448: leal 4(%edi), %edi
449:
450: C A branch-free version of this using some xors was found to be a
451: C touch slower than just a conditional jump, despite the jump
452: C switching between taken and not taken on every loop.
453:
454: ifelse(eval(UNROLL_COUNT%2),0,
455: jz,jnz) L(unroll_noswap)
456: movl %esi, %eax C high,low carry other way around
457:
458: movl %ecx, %esi
459: movl %eax, %ecx
460: L(unroll_noswap):
461:
462: jmp *%edx
463:
464:
465: C Must be on an even address here so the low bit of the jump address
466: C will indicate which way around ecx/esi should start.
467: C
468: C An attempt was made at padding here to get the end of the unrolled
469: C code to come out on a good alignment, to save padding before
470: C L(corner). This worked, but turned out to run slower than just an
471: C ALIGN(2). The reason for this is not clear, it might be related
472: C to the different speeds on different UNROLL_COUNTs noted above.
473:
474: ALIGN(2)
475:
476: L(unroll_inner_start):
477: C eax scratch
478: C ebx src
479: C ecx carry low
480: C edx scratch
481: C esi carry high
482: C edi dst
483: C ebp multiplier
484: C
485: C 15 code bytes each limb
486: C ecx/esi swapped on each chunk
487:
488: forloop(`i', UNROLL_COUNT, 1, `
489: deflit(`disp_src', eval(-i*4 + OFFSET))
490: deflit(`disp_dst', eval(disp_src - 4))
491:
492: m4_assert(`disp_src>=-128 && disp_src<128')
493: m4_assert(`disp_dst>=-128 && disp_dst<128')
494:
495: ifelse(eval(i%2),0,`
496: Zdisp( movl, disp_src,(%ebx), %eax)
497: mull %ebp
498: Zdisp( addl, %esi, disp_dst,(%edi))
499: adcl %eax, %ecx
500: movl %edx, %esi
501: jadcl0( %esi)
502: ',`
503: dnl this one comes out last
504: Zdisp( movl, disp_src,(%ebx), %eax)
505: mull %ebp
506: Zdisp( addl, %ecx, disp_dst,(%edi))
507: adcl %eax, %esi
508: movl %edx, %ecx
509: jadcl0( %ecx)
510: ')
511: ')
512: L(unroll_inner_end):
513:
514: addl %esi, -4+OFFSET(%edi)
515:
516: movl VAR_COUNTER, %edx
517: jadcl0( %ecx)
518:
519: movl %ecx, m4_empty_if_zero(OFFSET)(%edi)
520: movl VAR_JMP, %ecx
521:
522: incl %edx
523: jnz L(unroll_outer_top)
524:
525:
526: ifelse(OFFSET,0,,`
527: addl $OFFSET, %ebx
528: addl $OFFSET, %edi
529: ')
530:
531:
532: C -----------------------------------------------------------------------------
533: ALIGN(16)
534: L(corner):
535: C ebx &src[size]
536: C edi &dst[2*size-5]
537:
538: movl -12(%ebx), %ebp
539:
540: movl -8(%ebx), %eax
541: movl %eax, %ecx
542:
543: mull %ebp
544:
545: addl %eax, -4(%edi)
546: adcl $0, %edx
547:
548: movl -4(%ebx), %eax
549: movl %edx, %esi
550: movl %eax, %ebx
551:
552: mull %ebp
553:
554: addl %esi, %eax
555: adcl $0, %edx
556:
557: addl %eax, (%edi)
558: adcl $0, %edx
559:
560: movl %edx, %esi
561: movl %ebx, %eax
562:
563: mull %ecx
564:
565: addl %esi, %eax
566: movl %eax, 4(%edi)
567:
568: adcl $0, %edx
569:
570: movl %edx, 8(%edi)
571:
572:
573: C -----------------------------------------------------------------------------
574: C Left shift of dst[1..2*size-2], the bit shifted out becomes dst[2*size-1].
575: C The loop measures about 6 cycles/iteration, though it looks like it should
576: C decode in 5.
577:
578: L(lshift_start):
579: movl PARAM_SIZE, %ecx
580:
581: movl PARAM_DST, %edi
582: subl $1, %ecx C size-1 and clear carry
583:
584: movl PARAM_SRC, %ebx
585: movl %ecx, %edx
586:
587: xorl %eax, %eax C ready for adcl
588:
589:
590: ALIGN(16)
591: L(lshift):
592: C eax
593: C ebx src (for later use)
594: C ecx counter, decrementing
595: C edx size-1 (for later use)
596: C esi
597: C edi dst, incrementing
598: C ebp
599:
600: rcll 4(%edi)
601: rcll 8(%edi)
602: leal 8(%edi), %edi
603: loop L(lshift)
604:
605:
606: adcl %eax, %eax
607:
608: movl %eax, 4(%edi) C dst most significant limb
609: movl (%ebx), %eax C src[0]
610:
611: leal 4(%ebx,%edx,4), %ebx C &src[size]
612: subl %edx, %ecx C -(size-1)
613:
614:
615: C -----------------------------------------------------------------------------
616: C Now add in the squares on the diagonal, src[0]^2, src[1]^2, ...,
617: C src[size-1]^2. dst[0] hasn't yet been set at all yet, and just gets the
618: C low limb of src[0]^2.
619:
620:
621: mull %eax
622:
623: movl %eax, (%edi,%ecx,8) C dst[0]
624:
625:
626: ALIGN(16)
627: L(diag):
628: C eax scratch
629: C ebx &src[size]
630: C ecx counter, negative
631: C edx carry
632: C esi scratch
633: C edi dst[2*size-2]
634: C ebp
635:
636: movl (%ebx,%ecx,4), %eax
637: movl %edx, %esi
638:
639: mull %eax
640:
641: addl %esi, 4(%edi,%ecx,8)
642: adcl %eax, 8(%edi,%ecx,8)
643: adcl $0, %edx
644:
645: incl %ecx
646: jnz L(diag)
647:
648:
649: movl SAVE_EBX, %ebx
650: movl SAVE_ESI, %esi
651:
652: addl %edx, 4(%edi) C dst most significant limb
653:
654: movl SAVE_EDI, %edi
655: movl SAVE_EBP, %ebp
656: addl $FRAME, %esp
657: ret
658:
659:
660:
661: C -----------------------------------------------------------------------------
662: ifdef(`PIC',`
663: L(pic_calc):
664: C See README.family about old gas bugs
665: addl (%esp), %ecx
666: addl $L(unroll_inner_end)-L(here)-eval(2*CODE_BYTES_PER_LIMB), %ecx
667: addl %edx, %ecx
668: ret
669: ')
670:
671:
672: EPILOGUE()
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>