Ticket #1: addmul_1.yasm

File addmul_1.yasm, 9.8 KB (added by wbhart, 4 years ago)
Line 
1;; *********************************************************************
2;;  Intel64 mpn_addmul_1 -- Multiply a limb vector with a limb and
3;;  add the result to a second limb vector.
4;;
5;;  Copyright (C) 2006  Jason Worth Martin <jason.worth.martin@gmail.com>
6;;
7;;  This program is free software; you can redistribute it and/or modify
8;;  it under the terms of the GNU General Public License as published by
9;;  the Free Software Foundation; either version 2 of the License, or
10;;  (at your option) any later version.
11;;
12;;  This program is distributed in the hope that it will be useful,
13;;  but WITHOUT ANY WARRANTY; without even the implied warranty of
14;;  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15;;  GNU General Public License for more details.
16;;
17;;  You should have received a copy of the GNU General Public License along
18;;  with this program; if not, write to the Free Software Foundation, Inc.,
19;;  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20;;
21;; **************************************************************************
22;;
23;;
24;; CREDITS
25;;
26;; This code is based largely on Pierrick Gaudry's excellent assembly
27;; support for the AMD64 architecture.  (Note that Intel64 and AMD64,
28;; while using the same instruction set, have very different
29;; microarchitectures.  So, this code performs very poorly on AMD64
30;; machines even though it is near-optimal on Intel64.)
31;;
32;; Roger Golliver works for Intel and provided insightful improvements
33;; particularly in using the "lea" instruction to perform additions
34;; and register-to-register moves.
35;;
36;; Eric Bainville has a brilliant exposition of optimizing arithmetic for
37;; AMD64 (http://www.bealto.it).  I adapted many of the ideas he
38;; describes to Intel64.
39;;
40;; Agner Fog is a demigod in the x86 world.  If you are reading assembly
41;; code files and you haven't heard of Agner Fog, then take a minute to
42;; look over his software optimization manuals (http://www.agner.org/).
43;; They are superb.
44;;
45;; *********************************************************************
46
47
48;; With a 4-way unroll the code has
49;;
50;;              cycles/limb
51;; Hammer:           4.8
52;; Woodcrest:        4.6
53;;
54;; With increased unrolling, it appears to converge to 4 cycles/limb
55;; on Intel Core 2 machines.  I believe that this is optimal, however
56;; it requires such absurd unrolling that it becomes unusable for all
57;; but the largest inputs.  A 4-way unroll seems like a good balance
58;; to me because then commonly used input sizes (e.g. 1024bit Public
59;; keys) still benifit from the speed up.
60
61;;
62;; This is just a check to see if we are in my code testing sandbox
63;; or if we are actually in the GMP source tree
64;;
65%ifdef  __JWM_Test_Code__
66%include 'yasm_mac.inc'
67%else
68%include '../yasm_mac.inc'
69%endif
70
71
72
73;; *********************************************************************
74;; *********************************************************************
75;;
76;; Here are the important macro parameters for the code
77;;
78;;      BpL is Bytes per Limb (8 since this is 64bit code)
79;;
80;;      UNROLL_SIZE is a power of 2 for which we unroll the code.
81;;                  possible values are 2,4,8,15,..., 256.  A reasonable
82;;                  value is probably 4.  If really large inputs
83;;                  are expected, then 16 is probably good.  Larger
84;;                  values are really only useful for flashy
85;;                  benchmarks and testing asymptotic behavior.
86;;
87;;      THRESHOLD is the minimum number of limbs needed before we bother
88;;                using the complicated loop.  A reasonable value is
89;;                2*UNROLL_SIZE + 6
90;;
91;; *********************************************************************
92;; *********************************************************************
93%define BpL     8
94%define UNROLL_SIZE     4
95%define UNROLL_MASK     UNROLL_SIZE-1
96%define THRESHOLD       2*UNROLL_SIZE+6
97
98;; Here is a convenient Macro for addressing
99;; memory.  Entries of the form
100;;
101;;      ADDR(ptr,index,displacement)
102;;
103;; get converted to
104;;
105;;      [displacement*BpL + ptr + index*BpL]
106;;
107%define ADDR(a,b,c)     [c*BpL+a+b*BpL]
108
109
110;; Register     Usage
111;; --------     -----
112;; rax          low word from mul
113;; rbx*
114;; rcx          s2limb
115;; rdx          high word from mul
116;; rsi          s1p
117;; rdi          rp
118;; rbp*         Base Pointer
119;; rsp*         Stack Pointer
120;; r8           A_x
121;; r9           A_y
122;; r10          A_z
123;; r11          B_x
124;; r12*         B_y
125;; r13*         B_z
126;; r14*         temp
127;; r15*         index
128;;
129;; * indicates that the register must be
130;; preserved for the caller.
131%define s2limb  rcx
132%define s1p     rsi
133%define rp      rdi
134%define A_x     r8
135%define A_y     r9
136%define A_z     r10
137%define B_x     r11
138%define B_y     r12
139%define B_z     r13
140%define temp    r14
141%define index   r15
142
143       
144;; INPUT PARAMETERS
145;; rp           rdi
146;; s1p          rsi
147;; n            rdx
148;; s2limb       rcx
149        BITS    64
150G_EXPORT mpn_addmul_1
151                                        ;; Compare the limb count
152                                        ;; with the threshold value.
153                                        ;; If the limb count is small
154                                        ;; we just use the small loop,
155                                        ;; otherwise we jump to the
156                                        ;; more complicated loop.
157        cmp     rdx,THRESHOLD
158        jge     L_mpn_addmul_1_main_loop_prep
159        mov     r11,rdx
160        lea     rsi,[rsi+rdx*8]
161        lea     rdi,[rdi+rdx*8]
162        neg     r11
163        xor     r8, r8
164        xor     r10, r10
165        jmp     L_mpn_addmul_1_small_loop
166       
167        align   16
168L_mpn_addmul_1_small_loop:
169        mov     rax,[rsi+r11*8]
170        mul     rcx
171        add     rax,[rdi+r11*8]
172        adc     rdx,r10
173        add     rax,r8
174        mov     r8,r10
175        mov     [rdi+r11*8],rax
176        adc     r8,rdx
177        inc     r11
178        jne     L_mpn_addmul_1_small_loop
179
180        mov     rax,r8
181        ret
182
183L_mpn_addmul_1_main_loop_prep:
184        push    r15
185        push    r14
186        push    r13
187        push    r12
188                                ;; If n is even, we need to do three
189                                ;; pre-multiplies, if n is odd we only
190                                ;; need to do two.
191        mov     temp,rdx
192        mov     index,0
193        mov     A_x,0
194        mov     A_y,0
195        and     rdx,1
196        jnz     L_mpn_addmul_1_odd_n
197
198                                        ;; Case n is even
199        mov     rax,ADDR(s1p,index,0)
200        mul     s2limb
201        add     ADDR(rp,index,0),rax
202        adc     A_x,rdx
203        add     index,1
204                                        ;; At this point
205                                        ;;  temp = n (even)
206                                        ;; index = 1
207
208L_mpn_addmul_1_odd_n:
209                                        ;; Now
210                                        ;; temp = n
211                                        ;; index = 1 if n even
212                                        ;;       = 0 if n odd
213                                        ;;
214        mov     rax,ADDR(s1p,index,0)
215        mul     s2limb
216        mov     A_z,ADDR(rp,index,0)
217        add     A_x,rax
218        adc     A_y,rdx
219
220        mov     rax,ADDR(s1p,index,1)
221        mul     s2limb
222        mov     B_z,ADDR(rp,index,1)
223        mov     B_x,rax
224        mov     B_y,rdx
225        mov     rax,ADDR(s1p,index,2)
226
227        add     index,3
228        lea     s1p,ADDR(s1p,temp,-1)
229        lea     rp,ADDR(rp,temp,-1)
230        neg     temp
231        add     index,temp
232                                ;; At this point:
233                                ;; s1p   = address of last s1limb
234                                ;; rp    = address of last rplimb
235                                ;; temp  = -n
236                                ;; index = 4 - n if n even
237                                ;;       = 3 - n if n odd
238                                ;;
239                                ;; So, index is a (negative) even
240                                ;; number.
241                                ;;
242                                ;; *****************************************
243                                ;; ATTENTION:
244                                ;;
245                                ;; From here on, I will use array
246                                ;; indexing notation in the comments
247                                ;; because it is convenient.  So, I
248                                ;; will pretend that index is positive
249                                ;; because then a comment like
250                                ;;      B_z = rp[index-1]
251                                ;; is easier to read.
252                                ;; However, keep in mind that index is
253                                ;; actually a negative number indexing
254                                ;; back from the end of the array.
255                                ;; This is a common trick to remove one
256                                ;; compare operation from the main loop.
257                                ;; *****************************************
258
259                                ;;
260                                ;; Now we enter a spin-up loop the
261                                ;; will make sure that the index is
262                                ;; a multiple of UNROLL_SIZE before
263                                ;; going to our main unrolled loop.
264        mov     temp,index
265        neg     temp
266        and     temp,UNROLL_MASK
267        jz      L_mpn_addmul_1_main_loop
268        shr     temp,1
269L_mpn_addmul_1_main_loop_spin_up:
270                                ;; At this point we should have:
271                                ;;
272                                ;; A_x = low_mul[index-2] + carry_in
273                                ;; A_y = high_mul[index-2] + CF
274                                ;; A_z = rp[index-2]
275                                ;;
276                                ;; B_x = low_mul[index-1]
277                                ;; B_y = high_mul[index-1]
278                                ;; B_z = rp[index-1]
279                                ;;
280                                ;; rax = s1p[index]
281        mul     s2limb
282        add     A_z,A_x
283        lea     A_x,[rax]
284        mov     rax,ADDR(s1p,index,1)
285        adc     B_x,A_y
286        mov     ADDR(rp,index,-2),A_z
287        mov     A_z,ADDR(rp,index,0)
288        adc     B_y,0
289        lea     A_y,[rdx]
290                                ;; At this point we should have:
291                                ;;
292                                ;; B_x = low_mul[index-1] + carry_in
293                                ;; B_y = high_mul[index-1] + CF
294                                ;; B_z = rp[index-1]
295                                ;;
296                                ;; A_x = low_mul[index]
297                                ;; A_y = high_mul[index]
298                                ;; A_z = rp[index]
299                                ;;
300                                ;; rax = s1p[index+1]
301        mul     s2limb
302        add     B_z,B_x
303        lea     B_x,[rax]
304        mov     rax,ADDR(s1p,index,2)
305        adc     A_x,B_y
306        mov     ADDR(rp,index,-1),B_z
307        mov     B_z,ADDR(rp,index,1)
308        adc     A_y,0
309        lea     B_y,[rdx]
310
311        add     index,2
312        sub     temp,1
313        jnz     L_mpn_addmul_1_main_loop_spin_up
314       
315        jmp     L_mpn_addmul_1_main_loop
316       
317        align   16
318L_mpn_addmul_1_main_loop:
319                                ;; The code here is really the same
320                                ;; logic as the spin-up loop.  It's
321                                ;; just been unrolled.
322%assign unroll_index 0
323%rep    UNROLL_SIZE/2
324        mul     s2limb
325        add     A_z,A_x
326        lea     A_x,[rax]
327        mov     rax,ADDR(s1p,index,(2*unroll_index+1))
328        adc     B_x,A_y
329        mov     ADDR(rp,index,(2*unroll_index-2)),A_z
330        mov     A_z,ADDR(rp,index,(2*unroll_index))
331        adc     B_y,0
332        lea     A_y,[rdx]
333
334        mul     s2limb
335        add     B_z,B_x
336        lea     B_x,[rax]
337        mov     rax,ADDR(s1p,index,(2*unroll_index+2))
338        adc     A_x,B_y
339        mov     ADDR(rp,index,(2*unroll_index-1)),B_z
340        mov     B_z,ADDR(rp,index,(2*unroll_index+1))
341        adc     A_y,0
342        lea     B_y,[rdx]
343%assign unroll_index    unroll_index+1
344%endrep
345
346        add     index,UNROLL_SIZE
347        jnz     L_mpn_addmul_1_main_loop
348
349L_mpn_addmul_1_finish: 
350                                ;; At this point we should have:
351                                ;;
352                                ;; index = n-1
353                                ;;
354                                ;; A_x = low_mul[index-2] + carry_in
355                                ;; A_y = high_mul[index-2] + CF
356                                ;; A_z = rp[index-2]
357                                ;;
358                                ;; B_x = low_mul[index-1]
359                                ;; B_y = high_mul[index-1]
360                                ;; B_z = rp[index-1]
361                                ;;
362                                ;; rax = s1p[index]
363        mul     s2limb
364        add     A_z,A_x
365        mov     A_x,rax
366        mov     ADDR(rp,index,-2),A_z
367        mov     A_z,ADDR(rp,index,0)
368        adc     B_x,A_y
369        adc     B_y,0
370        mov     A_y,rdx
371                                ;; At this point we should have:
372                                ;;
373                                ;; index = n-1
374                                ;;
375                                ;; B_x = low_mul[index-1] + carry_in
376                                ;; B_y = high_mul[index-1] + CF
377                                ;; B_z = rp[index-1]
378                                ;;
379                                ;; A_x = low_mul[index]
380                                ;; A_y = high_mul[index]
381                                ;; A_z = rp[index]
382        add     B_z,B_x
383        mov     ADDR(rp,index,-1),B_z
384        adc     A_x,B_y
385        adc     A_y,0
386                                ;; At this point we should have:
387                                ;;
388                                ;; index = n-1
389                                ;;
390                                ;; A_x = low_mul[index] + carry_in
391                                ;; A_y = high_mul[index] + CF
392                                ;; A_z = rp[index]
393                                ;;
394        add     A_z,A_x
395        mov     ADDR(rp,index,0),A_z
396        adc     A_y,0
397
398        mov     rax,A_y
399        pop     r12
400        pop     r13
401        pop     r14
402        pop     r15
403        ret