mul_basecase.asm
上传用户:qaz666999
上传日期:2022-08-06
资源大小:2570k
文件大小:12k
源码类别:

数学计算

开发平台:

Unix_Linux

  1. dnl  AMD K6 mpn_mul_basecase -- multiply two mpn numbers.
  2. dnl  Copyright 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
  3. dnl
  4. dnl  This file is part of the GNU MP Library.
  5. dnl
  6. dnl  The GNU MP Library is free software; you can redistribute it and/or
  7. dnl  modify it under the terms of the GNU Lesser General Public License as
  8. dnl  published by the Free Software Foundation; either version 3 of the
  9. dnl  License, or (at your option) any later version.
  10. dnl
  11. dnl  The GNU MP Library is distributed in the hope that it will be useful,
  12. dnl  but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. dnl  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14. dnl  Lesser General Public License for more details.
  15. dnl
  16. dnl  You should have received a copy of the GNU Lesser General Public License
  17. dnl  along with the GNU MP Library.  If not, see http://www.gnu.org/licenses/.
  18. include(`../config.m4')
  19. C K6: approx 9.0 cycles per cross product on 30x30 limbs (with 16 limbs/loop
  20. C     unrolling).
  21. dnl  K6: UNROLL_COUNT cycles/product (approx)
  22. dnl           8           9.75
  23. dnl          16           9.3
  24. dnl          32           9.3
  25. dnl  Maximum possible with the current code is 32.
  26. dnl
  27. dnl  With 16 the inner unrolled loop fits exactly in a 256 byte block, which
  28. dnl  might explain it's good performance.
  29. deflit(UNROLL_COUNT, 16)
  30. C void mpn_mul_basecase (mp_ptr wp,
  31. C                        mp_srcptr xp, mp_size_t xsize,
  32. C                        mp_srcptr yp, mp_size_t ysize);
  33. C
  34. C Calculate xp,xsize multiplied by yp,ysize, storing the result in
  35. C wp,xsize+ysize.
  36. C
  37. C This routine is essentially the same as mpn/generic/mul_basecase.c, but
  38. C it's faster because it does most of the mpn_addmul_1() entry code only
  39. C once.  The saving is about 10-20% on typical sizes coming from the
  40. C Karatsuba multiply code.
  41. C
  42. C Enhancements:
  43. C
  44. C The mul_1 loop is about 8.5 c/l, which is slower than mpn_mul_1 at 6.25
  45. C c/l.  Could call mpn_mul_1 when ysize is big enough to make it worthwhile.
  46. C
  47. C The main unrolled addmul loop could be shared by mpn_addmul_1, using some
  48. C extra stack setups and maybe 2 or 3 wasted cycles at the end.  Code saving
  49. C would be 256 bytes.
  50. ifdef(`PIC',`
  51. deflit(UNROLL_THRESHOLD, 8)
  52. ',`
  53. deflit(UNROLL_THRESHOLD, 8)
  54. ')
  55. defframe(PARAM_YSIZE,20)
  56. defframe(PARAM_YP,   16)
  57. defframe(PARAM_XSIZE,12)
  58. defframe(PARAM_XP,   8)
  59. defframe(PARAM_WP,   4)
  60. TEXT
  61. ALIGN(32)
  62. PROLOGUE(mpn_mul_basecase)
  63. deflit(`FRAME',0)
  64. movl PARAM_XSIZE, %ecx
  65. movl PARAM_YP, %eax
  66. movl PARAM_XP, %edx
  67. movl (%eax), %eax C yp low limb
  68. cmpl $2, %ecx
  69. ja L(xsize_more_than_two_limbs)
  70. je L(two_by_something)
  71. C one limb by one limb
  72. movl (%edx), %edx C xp low limb
  73. movl PARAM_WP, %ecx
  74. mull %edx
  75. movl %eax, (%ecx)
  76. movl %edx, 4(%ecx)
  77. ret
  78. C -----------------------------------------------------------------------------
  79. L(two_by_something):
  80. decl PARAM_YSIZE
  81. pushl %ebx
  82. deflit(`FRAME',4)
  83. movl PARAM_WP, %ebx
  84. pushl %esi
  85. deflit(`FRAME',8)
  86. movl %eax, %ecx C yp low limb
  87. movl (%edx), %eax C xp low limb
  88. movl %edx, %esi C xp
  89. jnz L(two_by_two)
  90. C two limbs by one limb
  91. mull %ecx
  92. movl %eax, (%ebx)
  93. movl 4(%esi), %eax
  94. movl %edx, %esi C carry
  95. mull %ecx
  96. addl %eax, %esi
  97. movl %esi, 4(%ebx)
  98. adcl $0, %edx
  99. movl %edx, 8(%ebx)
  100. popl %esi
  101. popl %ebx
  102. ret
  103. C -----------------------------------------------------------------------------
  104. ALIGN(16)
  105. L(two_by_two):
  106. C eax xp low limb
  107. C ebx wp
  108. C ecx yp low limb
  109. C edx
  110. C esi xp
  111. C edi
  112. C ebp
  113. deflit(`FRAME',8)
  114. mull %ecx C xp[0] * yp[0]
  115. push %edi
  116. deflit(`FRAME',12)
  117. movl %eax, (%ebx)
  118. movl 4(%esi), %eax
  119. movl %edx, %edi C carry, for wp[1]
  120. mull %ecx C xp[1] * yp[0]
  121. addl %eax, %edi
  122. movl PARAM_YP, %ecx
  123. adcl $0, %edx
  124. movl %edi, 4(%ebx)
  125. movl 4(%ecx), %ecx C yp[1]
  126. movl 4(%esi), %eax C xp[1]
  127. movl %edx, %edi C carry, for wp[2]
  128. mull %ecx C xp[1] * yp[1]
  129. addl %eax, %edi
  130. adcl $0, %edx
  131. movl (%esi), %eax C xp[0]
  132. movl %edx, %esi C carry, for wp[3]
  133. mull %ecx C xp[0] * yp[1]
  134. addl %eax, 4(%ebx)
  135. adcl %edx, %edi
  136. adcl $0, %esi
  137. movl %edi, 8(%ebx)
  138. popl %edi
  139. movl %esi, 12(%ebx)
  140. popl %esi
  141. popl %ebx
  142. ret
  143. C -----------------------------------------------------------------------------
  144. ALIGN(16)
  145. L(xsize_more_than_two_limbs):
  146. C The first limb of yp is processed with a simple mpn_mul_1 style loop
  147. C inline.  Unrolling this doesn't seem worthwhile since it's only run once
  148. C (whereas the addmul below is run ysize-1 many times).  A call to the
  149. C actual mpn_mul_1 will be slowed down by the call and parameter pushing and
  150. C popping, and doesn't seem likely to be worthwhile on the typical 10-20
  151. C limb operations the Karatsuba code calls here with.
  152. C eax yp[0]
  153. C ebx
  154. C ecx xsize
  155. C edx xp
  156. C esi
  157. C edi
  158. C ebp
  159. deflit(`FRAME',0)
  160. pushl %edi defframe_pushl(SAVE_EDI)
  161. pushl %ebp defframe_pushl(SAVE_EBP)
  162. movl PARAM_WP, %edi
  163. pushl %esi defframe_pushl(SAVE_ESI)
  164. movl %eax, %ebp
  165. pushl %ebx defframe_pushl(SAVE_EBX)
  166. leal (%edx,%ecx,4), %ebx C xp end
  167. xorl %esi, %esi
  168. leal (%edi,%ecx,4), %edi C wp end of mul1
  169. negl %ecx
  170. L(mul1):
  171. C eax scratch
  172. C ebx xp end
  173. C ecx counter, negative
  174. C edx scratch
  175. C esi carry
  176. C edi wp end of mul1
  177. C ebp multiplier
  178. movl (%ebx,%ecx,4), %eax
  179. mull %ebp
  180. addl %esi, %eax
  181. movl $0, %esi
  182. adcl %edx, %esi
  183. movl %eax, (%edi,%ecx,4)
  184. incl %ecx
  185. jnz L(mul1)
  186. movl PARAM_YSIZE, %edx
  187. movl %esi, (%edi) C final carry
  188. movl PARAM_XSIZE, %ecx
  189. decl %edx
  190. jnz L(ysize_more_than_one_limb)
  191. popl %ebx
  192. popl %esi
  193. popl %ebp
  194. popl %edi
  195. ret
  196. L(ysize_more_than_one_limb):
  197. cmpl $UNROLL_THRESHOLD, %ecx
  198. movl PARAM_YP, %eax
  199. jae L(unroll)
  200. C -----------------------------------------------------------------------------
  201. C Simple addmul loop.
  202. C
  203. C Using ebx and edi pointing at the ends of their respective locations saves
  204. C a couple of instructions in the outer loop.  The inner loop is still 11
  205. C cycles, the same as the simple loop in aorsmul_1.asm.
  206. C eax yp
  207. C ebx xp end
  208. C ecx xsize
  209. C edx ysize-1
  210. C esi
  211. C edi wp end of mul1
  212. C ebp
  213. movl 4(%eax), %ebp C multiplier
  214. negl %ecx
  215. movl %ecx, PARAM_XSIZE C -xsize
  216. xorl %esi, %esi C initial carry
  217. leal 4(%eax,%edx,4), %eax C yp end
  218. negl %edx
  219. movl %eax, PARAM_YP
  220. movl %edx, PARAM_YSIZE
  221. jmp L(simple_outer_entry)
  222. C aligning here saves a couple of cycles
  223. ALIGN(16)
  224. L(simple_outer_top):
  225. C edx ysize counter, negative
  226. movl PARAM_YP, %eax C yp end
  227. xorl %esi, %esi C carry
  228. movl PARAM_XSIZE, %ecx C -xsize
  229. movl %edx, PARAM_YSIZE
  230. movl (%eax,%edx,4), %ebp C yp limb multiplier
  231. L(simple_outer_entry):
  232. addl $4, %edi
  233. L(simple_inner):
  234. C eax scratch
  235. C ebx xp end
  236. C ecx counter, negative
  237. C edx scratch
  238. C esi carry
  239. C edi wp end of this addmul
  240. C ebp multiplier
  241. movl (%ebx,%ecx,4), %eax
  242. mull %ebp
  243. addl %esi, %eax
  244. movl $0, %esi
  245. adcl $0, %edx
  246. addl %eax, (%edi,%ecx,4)
  247. adcl %edx, %esi
  248. incl %ecx
  249. jnz L(simple_inner)
  250. movl PARAM_YSIZE, %edx
  251. movl %esi, (%edi)
  252. incl %edx
  253. jnz L(simple_outer_top)
  254. popl %ebx
  255. popl %esi
  256. popl %ebp
  257. popl %edi
  258. ret
  259. C -----------------------------------------------------------------------------
  260. C Unrolled loop.
  261. C
  262. C The unrolled inner loop is the same as in aorsmul_1.asm, see that code for
  263. C some comments.
  264. C
  265. C VAR_COUNTER is for the inner loop, running from VAR_COUNTER_INIT down to
  266. C 0, inclusive.
  267. C
  268. C VAR_JMP is the computed jump into the unrolled loop.
  269. C
  270. C PARAM_XP and PARAM_WP get offset appropriately for where the unrolled loop
  271. C is entered.
  272. C
  273. C VAR_XP_LOW is the least significant limb of xp, which is needed at the
  274. C start of the unrolled loop.  This can't just be fetched through the xp
  275. C pointer because of the offset applied to it.
  276. C
  277. C PARAM_YSIZE is the outer loop counter, going from -(ysize-1) up to -1,
  278. C inclusive.
  279. C
  280. C PARAM_YP is offset appropriately so that the PARAM_YSIZE counter can be
  281. C added to give the location of the next limb of yp, which is the multiplier
  282. C in the unrolled loop.
  283. C
  284. C PARAM_WP is similarly offset so that the PARAM_YSIZE counter can be added
  285. C to give the starting point in the destination for each unrolled loop (this
  286. C point is one limb upwards for each limb of yp processed).
  287. C
  288. C Having PARAM_YSIZE count negative to zero means it's not necessary to
  289. C store new values of PARAM_YP and PARAM_WP on each loop.  Those values on
  290. C the stack remain constant and on each loop an leal adjusts them with the
  291. C PARAM_YSIZE counter value.
  292. defframe(VAR_COUNTER,      -20)
  293. defframe(VAR_COUNTER_INIT, -24)
  294. defframe(VAR_JMP,          -28)
  295. defframe(VAR_XP_LOW,       -32)
  296. deflit(VAR_STACK_SPACE, 16)
  297. dnl  For some strange reason using (%esp) instead of 0(%esp) is a touch
  298. dnl  slower in this code, hence the defframe empty-if-zero feature is
  299. dnl  disabled.
  300. dnl
  301. dnl  If VAR_COUNTER is at (%esp), the effect is worse.  In this case the
  302. dnl  unrolled loop is 255 instead of 256 bytes, but quite how this affects
  303. dnl  anything isn't clear.
  304. dnl
  305. define(`defframe_empty_if_zero_disabled',1)
  306. L(unroll):
  307. C eax yp (not used)
  308. C ebx xp end (not used)
  309. C ecx xsize
  310. C edx ysize-1
  311. C esi
  312. C edi wp end of mul1 (not used)
  313. C ebp
  314. deflit(`FRAME', 16)
  315. leal -2(%ecx), %ebp C one limb processed at start,
  316. decl %ecx C and ebp is one less
  317. shrl $UNROLL_LOG2, %ebp
  318. negl %ecx
  319. subl $VAR_STACK_SPACE, %esp
  320. deflit(`FRAME', 16+VAR_STACK_SPACE)
  321. andl $UNROLL_MASK, %ecx
  322. movl %ecx, %esi
  323. shll $4, %ecx
  324. movl %ebp, VAR_COUNTER_INIT
  325. negl %esi
  326. C 15 code bytes per limb
  327. ifdef(`PIC',`
  328. call L(pic_calc)
  329. L(unroll_here):
  330. ',`
  331. leal L(unroll_entry) (%ecx,%esi,1), %ecx
  332. ')
  333. movl PARAM_XP, %ebx
  334. movl %ebp, VAR_COUNTER
  335. movl PARAM_WP, %edi
  336. movl %ecx, VAR_JMP
  337. movl (%ebx), %eax
  338. leal 4(%edi,%esi,4), %edi C wp adjust for unrolling and mul1
  339. leal (%ebx,%esi,4), %ebx C xp adjust for unrolling
  340. movl %eax, VAR_XP_LOW
  341. movl %ebx, PARAM_XP
  342. movl PARAM_YP, %ebx
  343. leal (%edi,%edx,4), %ecx C wp adjust for ysize indexing
  344. movl 4(%ebx), %ebp C multiplier (yp second limb)
  345. leal 4(%ebx,%edx,4), %ebx C yp adjust for ysize indexing
  346. movl %ecx, PARAM_WP
  347. leal 1(%esi), %ecx C adjust parity for decl %ecx above
  348. movl %ebx, PARAM_YP
  349. negl %edx
  350. movl %edx, PARAM_YSIZE
  351. jmp L(unroll_outer_entry)
  352. ifdef(`PIC',`
  353. L(pic_calc):
  354. C See mpn/x86/README about old gas bugs
  355. leal (%ecx,%esi,1), %ecx
  356. addl $L(unroll_entry)-L(unroll_here), %ecx
  357. addl (%esp), %ecx
  358. ret_internal
  359. ')
  360. C -----------------------------------------------------------------------------
  361. C Aligning here saves a couple of cycles per loop.  Using 32 doesn't
  362. C cost any extra space, since the inner unrolled loop below is
  363. C aligned to 32.
  364. ALIGN(32)
  365. L(unroll_outer_top):
  366. C edx ysize
  367. movl PARAM_YP, %eax
  368. movl %edx, PARAM_YSIZE C incremented ysize counter
  369. movl PARAM_WP, %edi
  370. movl VAR_COUNTER_INIT, %ebx
  371. movl (%eax,%edx,4), %ebp C next multiplier
  372. movl PARAM_XSIZE, %ecx
  373. leal (%edi,%edx,4), %edi C adjust wp for where we are in yp
  374. movl VAR_XP_LOW, %eax
  375. movl %ebx, VAR_COUNTER
  376. L(unroll_outer_entry):
  377. mull %ebp
  378. C using testb is a tiny bit faster than testl
  379. testb $1, %cl
  380. movl %eax, %ecx C low carry
  381. movl VAR_JMP, %eax
  382. movl %edx, %esi C high carry
  383. movl PARAM_XP, %ebx
  384. jnz L(unroll_noswap)
  385. movl %ecx, %esi C high,low carry other way around
  386. movl %edx, %ecx
  387. L(unroll_noswap):
  388. jmp *%eax
  389. C -----------------------------------------------------------------------------
  390. ALIGN(32)
  391. L(unroll_top):
  392. C eax scratch
  393. C ebx xp
  394. C ecx carry low
  395. C edx scratch
  396. C esi carry high
  397. C edi wp
  398. C ebp multiplier
  399. C VAR_COUNTER  loop counter
  400. C
  401. C 15 code bytes each limb
  402. leal UNROLL_BYTES(%edi), %edi
  403. L(unroll_entry):
  404. deflit(CHUNK_COUNT,2)
  405. forloop(`i', 0, UNROLL_COUNT/CHUNK_COUNT-1, `
  406. deflit(`disp0', eval(i*CHUNK_COUNT*4))
  407. deflit(`disp1', eval(disp0 + 4))
  408. deflit(`disp2', eval(disp1 + 4))
  409. movl disp1(%ebx), %eax
  410. mull %ebp
  411. Zdisp( addl, %ecx, disp0,(%edi))
  412. adcl %eax, %esi
  413. movl %edx, %ecx
  414. jadcl0( %ecx)
  415. movl disp2(%ebx), %eax
  416. mull %ebp
  417. addl %esi, disp1(%edi)
  418. adcl %eax, %ecx
  419. movl %edx, %esi
  420. jadcl0( %esi)
  421. ')
  422. decl VAR_COUNTER
  423. leal UNROLL_BYTES(%ebx), %ebx
  424. jns L(unroll_top)
  425. movl PARAM_YSIZE, %edx
  426. addl %ecx, UNROLL_BYTES(%edi)
  427. adcl $0, %esi
  428. incl %edx
  429. movl %esi, UNROLL_BYTES+4(%edi)
  430. jnz L(unroll_outer_top)
  431. movl SAVE_ESI, %esi
  432. movl SAVE_EBP, %ebp
  433. movl SAVE_EDI, %edi
  434. movl SAVE_EBX, %ebx
  435. addl $FRAME, %esp
  436. ret
  437. EPILOGUE()