Annotation of OpenXM_contrib/gmp/mpn/x86/p6/mmx/divrem_1.asm, Revision 1.1.1.2
1.1 maekawa 1: dnl Intel Pentium-II mpn_divrem_1 -- mpn by limb division.
2:
1.1.1.2 ! ohara 3: dnl Copyright 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
1.1 maekawa 4: dnl
5: dnl This file is part of the GNU MP Library.
6: dnl
7: dnl The GNU MP Library is free software; you can redistribute it and/or
8: dnl modify it under the terms of the GNU Lesser General Public License as
9: dnl published by the Free Software Foundation; either version 2.1 of the
10: dnl License, or (at your option) any later version.
11: dnl
12: dnl The GNU MP Library is distributed in the hope that it will be useful,
13: dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
14: dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15: dnl Lesser General Public License for more details.
16: dnl
17: dnl You should have received a copy of the GNU Lesser General Public
18: dnl License along with the GNU MP Library; see the file COPYING.LIB. If
19: dnl not, write to the Free Software Foundation, Inc., 59 Temple Place -
20: dnl Suite 330, Boston, MA 02111-1307, USA.
21:
22: include(`../config.m4')
23:
24:
1.1.1.2 ! ohara 25: C P6MMX: 25.0 cycles/limb integer part, 17.5 cycles/limb fraction part.
! 26:
! 27:
1.1 maekawa 28: C mp_limb_t mpn_divrem_1 (mp_ptr dst, mp_size_t xsize,
29: C mp_srcptr src, mp_size_t size,
30: C mp_limb_t divisor);
31: C mp_limb_t mpn_divrem_1c (mp_ptr dst, mp_size_t xsize,
32: C mp_srcptr src, mp_size_t size,
33: C mp_limb_t divisor, mp_limb_t carry);
1.1.1.2 ! ohara 34: C mp_limb_t mpn_preinv_divrem_1 (mp_ptr dst, mp_size_t xsize,
! 35: C mp_srcptr src, mp_size_t size,
! 36: C mp_limb_t divisor, mp_limb_t inverse,
! 37: C unsigned shift);
1.1 maekawa 38: C
39: C This code is a lightly reworked version of mpn/x86/k7/mmx/divrem_1.asm,
1.1.1.2 ! ohara 40: C see that file for some comments. It's possible what's here can be improved.
1.1 maekawa 41:
42:
43: dnl MUL_THRESHOLD is the value of xsize+size at which the multiply by
44: dnl inverse method is used, rather than plain "divl"s. Minimum value 1.
45: dnl
46: dnl The different speeds of the integer and fraction parts means that using
47: dnl xsize+size isn't quite right. The threshold wants to be a bit higher
48: dnl for the integer part and a bit lower for the fraction part. (Or what's
49: dnl really wanted is to speed up the integer part!)
50: dnl
51: dnl The threshold is set to make the integer part right. At 4 limbs the
52: dnl div and mul are about the same there, but on the fractional part the
53: dnl mul is much faster.
54:
55: deflit(MUL_THRESHOLD, 4)
56:
57:
1.1.1.2 ! ohara 58: defframe(PARAM_PREINV_SHIFT, 28) dnl mpn_preinv_divrem_1
! 59: defframe(PARAM_PREINV_INVERSE, 24) dnl mpn_preinv_divrem_1
! 60: defframe(PARAM_CARRY, 24) dnl mpn_divrem_1c
1.1 maekawa 61: defframe(PARAM_DIVISOR,20)
62: defframe(PARAM_SIZE, 16)
63: defframe(PARAM_SRC, 12)
64: defframe(PARAM_XSIZE, 8)
65: defframe(PARAM_DST, 4)
66:
67: defframe(SAVE_EBX, -4)
68: defframe(SAVE_ESI, -8)
69: defframe(SAVE_EDI, -12)
70: defframe(SAVE_EBP, -16)
71:
72: defframe(VAR_NORM, -20)
73: defframe(VAR_INVERSE, -24)
74: defframe(VAR_SRC, -28)
75: defframe(VAR_DST, -32)
76: defframe(VAR_DST_STOP,-36)
77:
78: deflit(STACK_SPACE, 36)
79:
1.1.1.2 ! ohara 80: TEXT
! 81: ALIGN(16)
! 82:
! 83: PROLOGUE(mpn_preinv_divrem_1)
! 84: deflit(`FRAME',0)
! 85: movl PARAM_XSIZE, %ecx
! 86: subl $STACK_SPACE, %esp FRAME_subl_esp(STACK_SPACE)
! 87:
! 88: movl %esi, SAVE_ESI
! 89: movl PARAM_SRC, %esi
! 90:
! 91: movl %ebx, SAVE_EBX
! 92: movl PARAM_SIZE, %ebx
! 93:
! 94: movl %ebp, SAVE_EBP
! 95: movl PARAM_DIVISOR, %ebp
! 96:
! 97: movl %edi, SAVE_EDI
! 98: movl PARAM_DST, %edx
! 99:
! 100: movl -4(%esi,%ebx,4), %eax C src high limb
! 101: xorl %edi, %edi C initial carry (if can't skip a div)
! 102:
! 103: C
! 104:
! 105: leal 8(%edx,%ecx,4), %edx C &dst[xsize+2]
! 106: xor %ecx, %ecx
! 107:
! 108: movl %edx, VAR_DST_STOP C &dst[xsize+2]
! 109: cmpl %ebp, %eax C high cmp divisor
! 110:
! 111: cmovc( %eax, %edi) C high is carry if high<divisor
! 112:
! 113: cmovnc( %eax, %ecx) C 0 if skip div, src high if not
! 114: C (the latter in case src==dst)
! 115:
! 116: movl %ecx, -12(%edx,%ebx,4) C dst high limb
! 117:
! 118: sbbl $0, %ebx C skip one division if high<divisor
! 119: movl PARAM_PREINV_SHIFT, %ecx
! 120:
! 121: leal -8(%edx,%ebx,4), %edx C &dst[xsize+size]
! 122: movl $32, %eax
! 123:
! 124: movl %edx, VAR_DST C &dst[xsize+size]
! 125:
! 126: shll %cl, %ebp C d normalized
! 127: subl %ecx, %eax
! 128: movl %ecx, VAR_NORM
! 129:
! 130: movd %eax, %mm7 C rshift
! 131: movl PARAM_PREINV_INVERSE, %eax
! 132: jmp L(start_preinv)
! 133:
! 134: EPILOGUE()
! 135:
! 136:
! 137:
1.1 maekawa 138: ALIGN(16)
139:
140: PROLOGUE(mpn_divrem_1c)
141: deflit(`FRAME',0)
142: movl PARAM_CARRY, %edx
143:
144: movl PARAM_SIZE, %ecx
145: subl $STACK_SPACE, %esp
146: deflit(`FRAME',STACK_SPACE)
147:
148: movl %ebx, SAVE_EBX
149: movl PARAM_XSIZE, %ebx
150:
151: movl %edi, SAVE_EDI
152: movl PARAM_DST, %edi
153:
154: movl %ebp, SAVE_EBP
155: movl PARAM_DIVISOR, %ebp
156:
157: movl %esi, SAVE_ESI
158: movl PARAM_SRC, %esi
159:
160: leal -4(%edi,%ebx,4), %edi
1.1.1.2 ! ohara 161: jmp L(start_1c)
1.1 maekawa 162:
163: EPILOGUE()
164:
165:
166: C offset 0x31, close enough to aligned
167: PROLOGUE(mpn_divrem_1)
168: deflit(`FRAME',0)
169:
170: movl PARAM_SIZE, %ecx
171: movl $0, %edx C initial carry (if can't skip a div)
172: subl $STACK_SPACE, %esp
173: deflit(`FRAME',STACK_SPACE)
174:
175: movl %ebp, SAVE_EBP
176: movl PARAM_DIVISOR, %ebp
177:
178: movl %ebx, SAVE_EBX
179: movl PARAM_XSIZE, %ebx
180:
181: movl %esi, SAVE_ESI
182: movl PARAM_SRC, %esi
1.1.1.2 ! ohara 183: orl %ecx, %ecx C size
1.1 maekawa 184:
185: movl %edi, SAVE_EDI
186: movl PARAM_DST, %edi
187:
188: leal -4(%edi,%ebx,4), %edi C &dst[xsize-1]
1.1.1.2 ! ohara 189: jz L(no_skip_div) C if size==0
1.1 maekawa 190:
191: movl -4(%esi,%ecx,4), %eax C src high limb
1.1.1.2 ! ohara 192: xorl %esi, %esi
! 193: cmpl %ebp, %eax C high cmp divisor
1.1 maekawa 194:
1.1.1.2 ! ohara 195: cmovc( %eax, %edx) C high is carry if high<divisor
! 196:
! 197: cmovnc( %eax, %esi) C 0 if skip div, src high if not
! 198: C (the latter in case src==dst)
! 199:
! 200: movl %esi, (%edi,%ecx,4) C dst high limb
! 201:
! 202: sbbl $0, %ecx C size-1 if high<divisor
! 203: movl PARAM_SRC, %esi C reload
1.1 maekawa 204: L(no_skip_div):
205:
206:
207: L(start_1c):
208: C eax
209: C ebx xsize
210: C ecx size
211: C edx carry
212: C esi src
213: C edi &dst[xsize-1]
214: C ebp divisor
215:
216: leal (%ebx,%ecx), %eax C size+xsize
217: cmpl $MUL_THRESHOLD, %eax
218: jae L(mul_by_inverse)
219:
220: orl %ecx, %ecx
221: jz L(divide_no_integer)
222:
223: L(divide_integer):
224: C eax scratch (quotient)
225: C ebx xsize
226: C ecx counter
227: C edx scratch (remainder)
228: C esi src
229: C edi &dst[xsize-1]
230: C ebp divisor
231:
232: movl -4(%esi,%ecx,4), %eax
233:
234: divl %ebp
235:
236: movl %eax, (%edi,%ecx,4)
237: decl %ecx
238: jnz L(divide_integer)
239:
240:
241: L(divide_no_integer):
242: movl PARAM_DST, %edi
243: orl %ebx, %ebx
244: jnz L(divide_fraction)
245:
246: L(divide_done):
247: movl SAVE_ESI, %esi
248:
249: movl SAVE_EDI, %edi
250:
251: movl SAVE_EBX, %ebx
252: movl %edx, %eax
253:
254: movl SAVE_EBP, %ebp
255: addl $STACK_SPACE, %esp
256:
257: ret
258:
259:
260: L(divide_fraction):
261: C eax scratch (quotient)
262: C ebx counter
263: C ecx
264: C edx scratch (remainder)
265: C esi
266: C edi dst
267: C ebp divisor
268:
269: movl $0, %eax
270:
271: divl %ebp
272:
273: movl %eax, -4(%edi,%ebx,4)
274: decl %ebx
275: jnz L(divide_fraction)
276:
277: jmp L(divide_done)
278:
279:
280:
281: C -----------------------------------------------------------------------------
282:
283: L(mul_by_inverse):
284: C eax
285: C ebx xsize
286: C ecx size
287: C edx carry
288: C esi src
289: C edi &dst[xsize-1]
290: C ebp divisor
291:
1.1.1.2 ! ohara 292: leal 12(%edi), %ebx C &dst[xsize+2], loop dst stop
1.1 maekawa 293:
294: movl %ebx, VAR_DST_STOP
295: leal 4(%edi,%ecx,4), %edi C &dst[xsize+size]
296:
297: movl %edi, VAR_DST
298: movl %ecx, %ebx C size
299:
300: bsrl %ebp, %ecx C 31-l
301: movl %edx, %edi C carry
302:
303: leal 1(%ecx), %eax C 32-l
304: xorl $31, %ecx C l
305:
306: movl %ecx, VAR_NORM
307: movl $-1, %edx
308:
309: shll %cl, %ebp C d normalized
310: movd %eax, %mm7
311:
312: movl $-1, %eax
313: subl %ebp, %edx C (b-d)-1 giving edx:eax = b*(b-d)-1
314:
315: divl %ebp C floor (b*(b-d)-1) / d
316:
1.1.1.2 ! ohara 317: L(start_preinv):
! 318: C eax inverse
! 319: C ebx size
! 320: C ecx shift
! 321: C edx
! 322: C esi src
! 323: C edi carry
! 324: C ebp divisor
! 325: C
! 326: C mm7 rshift
! 327:
1.1 maekawa 328: movl %eax, VAR_INVERSE
329: orl %ebx, %ebx C size
330: leal -12(%esi,%ebx,4), %eax C &src[size-3]
331:
332: movl %eax, VAR_SRC
333: jz L(start_zero)
334:
335: movl 8(%eax), %esi C src high limb
336: cmpl $1, %ebx
337: jz L(start_one)
338:
339: L(start_two_or_more):
340: movl 4(%eax), %edx C src second highest limb
341:
342: shldl( %cl, %esi, %edi) C n2 = carry,high << l
343:
344: shldl( %cl, %edx, %esi) C n10 = high,second << l
345:
346: cmpl $2, %ebx
347: je L(integer_two_left)
348: jmp L(integer_top)
349:
350:
351: L(start_one):
352: shldl( %cl, %esi, %edi) C n2 = carry,high << l
353:
354: shll %cl, %esi C n10 = high << l
355: jmp L(integer_one_left)
356:
357:
358: L(start_zero):
1.1.1.2 ! ohara 359: C Can be here with xsize==0 if mpn_preinv_divrem_1 had size==1 and
! 360: C skipped a division.
! 361:
1.1 maekawa 362: shll %cl, %edi C n2 = carry << l
1.1.1.2 ! ohara 363: movl %edi, %eax C return value for zero_done
! 364: cmpl $0, PARAM_XSIZE
1.1 maekawa 365:
1.1.1.2 ! ohara 366: je L(zero_done)
1.1 maekawa 367: jmp L(fraction_some)
368:
369:
370:
371: C -----------------------------------------------------------------------------
372: C
373: C This loop runs at about 25 cycles, which is probably sub-optimal, and
374: C certainly more than the dependent chain would suggest. A better loop, or
375: C a better rough analysis of what's possible, would be welcomed.
376: C
377: C In the current implementation, the following successively dependent
378: C micro-ops seem to exist.
379: C
380: C uops
381: C n2+n1 1 (addl)
382: C mul 5
383: C q1+1 3 (addl/adcl)
384: C mul 5
385: C sub 3 (subl/sbbl)
386: C addback 2 (cmov)
387: C ---
388: C 19
389: C
390: C Lack of registers hinders explicit scheduling and it might be that the
391: C normal out of order execution isn't able to hide enough under the mul
392: C latencies.
393: C
394: C Using sarl/negl to pick out n1 for the n2+n1 stage is a touch faster than
395: C cmov (and takes one uop off the dependent chain). A sarl/andl/addl
396: C combination was tried for the addback (despite the fact it would lengthen
397: C the dependent chain) but found to be no faster.
398:
399:
400: ALIGN(16)
401: L(integer_top):
402: C eax scratch
403: C ebx scratch (nadj, q1)
404: C ecx scratch (src, dst)
405: C edx scratch
406: C esi n10
407: C edi n2
408: C ebp d
409: C
410: C mm0 scratch (src qword)
411: C mm7 rshift for normalization
412:
413: movl %esi, %eax
414: movl %ebp, %ebx
415:
416: sarl $31, %eax C -n1
417: movl VAR_SRC, %ecx
418:
419: andl %eax, %ebx C -n1 & d
420: negl %eax C n1
421:
422: addl %esi, %ebx C nadj = n10 + (-n1 & d), ignoring overflow
423: addl %edi, %eax C n2+n1
424: movq (%ecx), %mm0 C next src limb and the one below it
425:
426: mull VAR_INVERSE C m*(n2+n1)
427:
428: subl $4, %ecx
429:
430: movl %ecx, VAR_SRC
431:
432: C
433:
434: C
435:
436: addl %ebx, %eax C m*(n2+n1) + nadj, low giving carry flag
437: movl %ebp, %eax C d
1.1.1.2 ! ohara 438: leal 1(%edi), %ebx C n2+1
1.1 maekawa 439:
440: adcl %edx, %ebx C 1 + high(n2<<32 + m*(n2+n1) + nadj) = q1+1
441: jz L(q1_ff)
442:
443: mull %ebx C (q1+1)*d
444:
445: movl VAR_DST, %ecx
446: psrlq %mm7, %mm0
447:
448: C
449:
450: C
451:
452: C
453:
454: subl %eax, %esi
455: movl VAR_DST_STOP, %eax
456:
457: sbbl %edx, %edi C n - (q1+1)*d
458: movl %esi, %edi C remainder -> n2
459: leal (%ebp,%esi), %edx
460:
461: cmovc( %edx, %edi) C n - q1*d if underflow from using q1+1
462: movd %mm0, %esi
463:
464: sbbl $0, %ebx C q
465: subl $4, %ecx
466:
467: movl %ebx, (%ecx)
468: cmpl %eax, %ecx
469:
470: movl %ecx, VAR_DST
471: jne L(integer_top)
472:
473:
474: L(integer_loop_done):
475:
476:
477: C -----------------------------------------------------------------------------
478: C
479: C Here, and in integer_one_left below, an sbbl $0 is used rather than a jz
480: C q1_ff special case. This make the code a bit smaller and simpler, and
481: C costs only 2 cycles (each).
482:
483: L(integer_two_left):
484: C eax scratch
485: C ebx scratch (nadj, q1)
486: C ecx scratch (src, dst)
487: C edx scratch
488: C esi n10
489: C edi n2
490: C ebp divisor
491: C
492: C mm7 rshift
493:
494:
495: movl %esi, %eax
496: movl %ebp, %ebx
497:
498: sarl $31, %eax C -n1
499: movl PARAM_SRC, %ecx
500:
501: andl %eax, %ebx C -n1 & d
502: negl %eax C n1
503:
504: addl %esi, %ebx C nadj = n10 + (-n1 & d), ignoring overflow
505: addl %edi, %eax C n2+n1
506:
507: mull VAR_INVERSE C m*(n2+n1)
508:
509: movd (%ecx), %mm0 C src low limb
510:
511: movl VAR_DST_STOP, %ecx
512:
513: C
514:
515: C
516:
517: addl %ebx, %eax C m*(n2+n1) + nadj, low giving carry flag
1.1.1.2 ! ohara 518: leal 1(%edi), %ebx C n2+1
1.1 maekawa 519: movl %ebp, %eax C d
520:
521: adcl %edx, %ebx C 1 + high(n2<<32 + m*(n2+n1) + nadj) = q1+1
522:
523: sbbl $0, %ebx
524:
525: mull %ebx C (q1+1)*d
526:
527: psllq $32, %mm0
528:
529: psrlq %mm7, %mm0
530:
531: C
532:
533: C
534:
535: subl %eax, %esi
536:
537: sbbl %edx, %edi C n - (q1+1)*d
538: movl %esi, %edi C remainder -> n2
539: leal (%ebp,%esi), %edx
540:
541: cmovc( %edx, %edi) C n - q1*d if underflow from using q1+1
542: movd %mm0, %esi
543:
544: sbbl $0, %ebx C q
545:
546: movl %ebx, -4(%ecx)
547:
548:
549: C -----------------------------------------------------------------------------
550: L(integer_one_left):
551: C eax scratch
552: C ebx scratch (nadj, q1)
553: C ecx scratch (dst)
554: C edx scratch
555: C esi n10
556: C edi n2
557: C ebp divisor
558: C
559: C mm7 rshift
560:
561:
562: movl %esi, %eax
563: movl %ebp, %ebx
564:
565: sarl $31, %eax C -n1
566: movl VAR_DST_STOP, %ecx
567:
568: andl %eax, %ebx C -n1 & d
569: negl %eax C n1
570:
571: addl %esi, %ebx C nadj = n10 + (-n1 & d), ignoring overflow
572: addl %edi, %eax C n2+n1
573:
574: mull VAR_INVERSE C m*(n2+n1)
575:
576: C
577:
578: C
579:
580: C
581:
582: addl %ebx, %eax C m*(n2+n1) + nadj, low giving carry flag
1.1.1.2 ! ohara 583: leal 1(%edi), %ebx C n2+1
1.1 maekawa 584: movl %ebp, %eax C d
585:
586: C
587:
588: adcl %edx, %ebx C 1 + high(n2<<32 + m*(n2+n1) + nadj) = q1+1
589:
590: sbbl $0, %ebx C q1 if q1+1 overflowed
591:
592: mull %ebx
593:
594: C
595:
596: C
597:
598: C
599:
600: C
601:
602: subl %eax, %esi
603: movl PARAM_XSIZE, %eax
604:
605: sbbl %edx, %edi C n - (q1+1)*d
606: movl %esi, %edi C remainder -> n2
607: leal (%ebp,%esi), %edx
608:
609: cmovc( %edx, %edi) C n - q1*d if underflow from using q1+1
610:
611: sbbl $0, %ebx C q
612:
613: movl %ebx, -8(%ecx)
614: subl $8, %ecx
615:
616:
617:
618: orl %eax, %eax C xsize
619: jnz L(fraction_some)
620:
621: movl %edi, %eax
622: L(fraction_done):
623: movl VAR_NORM, %ecx
1.1.1.2 ! ohara 624: L(zero_done):
1.1 maekawa 625: movl SAVE_EBP, %ebp
626:
627: movl SAVE_EDI, %edi
628:
629: movl SAVE_ESI, %esi
630:
631: movl SAVE_EBX, %ebx
632: addl $STACK_SPACE, %esp
633:
634: shrl %cl, %eax
635: emms
636:
637: ret
638:
639:
640: C -----------------------------------------------------------------------------
641: C
642: C Special case for q1=0xFFFFFFFF, giving q=0xFFFFFFFF meaning the low dword
643: C of q*d is simply -d and the remainder n-q*d = n10+d
644:
645: L(q1_ff):
646: C eax (divisor)
647: C ebx (q1+1 == 0)
648: C ecx
649: C edx
650: C esi n10
651: C edi n2
652: C ebp divisor
653:
654: movl VAR_DST, %ecx
655: movl VAR_DST_STOP, %edx
656: subl $4, %ecx
657:
658: movl %ecx, VAR_DST
659: psrlq %mm7, %mm0
660: leal (%ebp,%esi), %edi C n-q*d remainder -> next n2
661:
662: movl $-1, (%ecx)
663: movd %mm0, %esi C next n10
664:
665: cmpl %ecx, %edx
666: jne L(integer_top)
667:
668: jmp L(integer_loop_done)
669:
670:
671:
672: C -----------------------------------------------------------------------------
673: C
674: C In the current implementation, the following successively dependent
675: C micro-ops seem to exist.
676: C
677: C uops
678: C mul 5
679: C q1+1 1 (addl)
680: C mul 5
681: C sub 3 (negl/sbbl)
682: C addback 2 (cmov)
683: C ---
684: C 16
685: C
686: C The loop in fact runs at about 17.5 cycles. Using a sarl/andl/addl for
687: C the addback was found to be a touch slower.
688:
689:
690: ALIGN(16)
691: L(fraction_some):
692: C eax
693: C ebx
694: C ecx
695: C edx
696: C esi
697: C edi carry
698: C ebp divisor
699:
700: movl PARAM_DST, %esi
1.1.1.2 ! ohara 701: movl VAR_DST_STOP, %ecx C &dst[xsize+2]
1.1 maekawa 702: movl %edi, %eax
703:
1.1.1.2 ! ohara 704: subl $8, %ecx C &dst[xsize]
1.1 maekawa 705:
706:
707: ALIGN(16)
708: L(fraction_top):
709: C eax n2, then scratch
710: C ebx scratch (nadj, q1)
711: C ecx dst, decrementing
712: C edx scratch
713: C esi dst stop point
714: C edi n2
715: C ebp divisor
716:
717: mull VAR_INVERSE C m*n2
718:
719: movl %ebp, %eax C d
720: subl $4, %ecx C dst
721: leal 1(%edi), %ebx
722:
723: C
724:
725: C
726:
727: C
728:
729: addl %edx, %ebx C 1 + high(n2<<32 + m*n2) = q1+1
730:
731: mull %ebx C (q1+1)*d
732:
733: C
734:
735: C
736:
737: C
738:
739: C
740:
741: negl %eax C low of n - (q1+1)*d
742:
743: sbbl %edx, %edi C high of n - (q1+1)*d, caring only about carry
744: leal (%ebp,%eax), %edx
745:
746: cmovc( %edx, %eax) C n - q1*d if underflow from using q1+1
747:
748: sbbl $0, %ebx C q
749: movl %eax, %edi C remainder->n2
750: cmpl %esi, %ecx
751:
752: movl %ebx, (%ecx) C previous q
753: jne L(fraction_top)
754:
755:
756: jmp L(fraction_done)
757:
758: EPILOGUE()
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>