3 # Implemented as a Perl wrapper as we want to support several different
4 # architectures with single file. We pick up the target based on the
5 # file name we are asked to generate.
7 # It should be noted though that this perl code is nothing like
8 # <openssl>/crypto/perlasm/x86*. In this case perl is used pretty much
9 # as pre-processor to cover for platform differences in name decoration,
10 # linker tables, 32-/64-bit instruction sets...
12 # As you might know there're several PowerPC ABI in use. Most notably
13 # Linux and AIX use different 32-bit ABIs. Good news are that these ABIs
14 # are similar enough to implement leaf(!) functions, which would be ABI
15 # neutral. And that's what you find here: ABI neutral leaf functions.
16 # In case you wonder what that is...
20 # MEASUREMENTS WITH cc ON a 200 MhZ PowerPC 604e.
22 # The following is the performance of 32-bit compiler
25 # OpenSSL 0.9.6c 21 dec 2001
26 # built on: Tue Jun 11 11:06:51 EDT 2002
27 # options:bn(64,32) ...
28 #compiler: cc -DTHREADS -DAIX -DB_ENDIAN -DBN_LLONG -O3
29 # sign verify sign/s verify/s
30 #rsa 512 bits 0.0098s 0.0009s 102.0 1170.6
31 #rsa 1024 bits 0.0507s 0.0026s 19.7 387.5
32 #rsa 2048 bits 0.3036s 0.0085s 3.3 117.1
33 #rsa 4096 bits 2.0040s 0.0299s 0.5 33.4
34 #dsa 512 bits 0.0087s 0.0106s 114.3 94.5
35 #dsa 1024 bits 0.0256s 0.0313s 39.0 32.0
37 # Same bechmark with this assembler code:
39 #rsa 512 bits 0.0056s 0.0005s 178.6 2049.2
40 #rsa 1024 bits 0.0283s 0.0015s 35.3 674.1
41 #rsa 2048 bits 0.1744s 0.0050s 5.7 201.2
42 #rsa 4096 bits 1.1644s 0.0179s 0.9 55.7
43 #dsa 512 bits 0.0052s 0.0062s 191.6 162.0
44 #dsa 1024 bits 0.0149s 0.0180s 67.0 55.5
46 # Number of operations increases by at almost 75%
48 # Here are performance numbers for 64-bit compiler
51 # OpenSSL 0.9.6g [engine] 9 Aug 2002
52 # built on: Fri Apr 18 16:59:20 EDT 2003
53 # options:bn(64,64) ...
54 # compiler: cc -DTHREADS -D_REENTRANT -q64 -DB_ENDIAN -O3
55 # sign verify sign/s verify/s
56 #rsa 512 bits 0.0028s 0.0003s 357.1 3844.4
57 #rsa 1024 bits 0.0148s 0.0008s 67.5 1239.7
58 #rsa 2048 bits 0.0963s 0.0028s 10.4 353.0
59 #rsa 4096 bits 0.6538s 0.0102s 1.5 98.1
60 #dsa 512 bits 0.0026s 0.0032s 382.5 313.7
61 #dsa 1024 bits 0.0081s 0.0099s 122.8 100.6
63 # Same benchmark with this assembler code:
65 #rsa 512 bits 0.0020s 0.0002s 510.4 6273.7
66 #rsa 1024 bits 0.0088s 0.0005s 114.1 2128.3
67 #rsa 2048 bits 0.0540s 0.0016s 18.5 622.5
68 #rsa 4096 bits 0.3700s 0.0058s 2.7 171.0
69 #dsa 512 bits 0.0016s 0.0020s 610.7 507.1
70 #dsa 1024 bits 0.0047s 0.0058s 212.5 173.2
72 # Again, performance increases by at about 75%
74 # Mac OS X, Apple G5 1.8GHz (Note this is 32 bit code)
75 # OpenSSL 0.9.7c 30 Sep 2003
79 #rsa 512 bits 0.0011s 0.0001s 906.1 11012.5
80 #rsa 1024 bits 0.0060s 0.0003s 166.6 3363.1
81 #rsa 2048 bits 0.0370s 0.0010s 27.1 982.4
82 #rsa 4096 bits 0.2426s 0.0036s 4.1 280.4
83 #dsa 512 bits 0.0010s 0.0012s 1038.1 841.5
84 #dsa 1024 bits 0.0030s 0.0037s 329.6 269.7
85 #dsa 2048 bits 0.0101s 0.0127s 98.9 78.6
87 # Same benchmark with this assembler code:
89 #rsa 512 bits 0.0007s 0.0001s 1416.2 16645.9
90 #rsa 1024 bits 0.0036s 0.0002s 274.4 5380.6
91 #rsa 2048 bits 0.0222s 0.0006s 45.1 1589.5
92 #rsa 4096 bits 0.1469s 0.0022s 6.8 449.6
93 #dsa 512 bits 0.0006s 0.0007s 1664.2 1376.2
94 #dsa 1024 bits 0.0018s 0.0023s 545.0 442.2
95 #dsa 2048 bits 0.0061s 0.0075s 163.5 132.8
97 # Performance increase of ~60%
99 # If you have comments or suggestions to improve code send
100 # me a note at schari@us.ibm.com
105 if ($opf =~ /32\.s/) {
111 $LDU= "lwzu"; # load and update
113 $STU= "stwu"; # store and update
114 $UMULL= "mullw"; # unsigned multiply low
115 $UMULH= "mulhwu"; # unsigned multiply high
116 $UDIV= "divwu"; # unsigned divide
117 $UCMPI= "cmplwi"; # unsigned compare with immediate
118 $UCMP= "cmplw"; # unsigned compare
119 $COUNTZ="cntlzw"; # count leading zeros
120 $SHL= "slw"; # shift left
121 $SHR= "srw"; # unsigned shift right
122 $SHRI= "srwi"; # unsigned shift right by immediate
123 $SHLI= "slwi"; # shift left by immediate
124 $CLRU= "clrlwi"; # clear upper bits
125 $INSR= "insrwi"; # insert right
126 $ROTL= "rotlwi"; # rotate left by immediate
127 } elsif ($opf =~ /64\.s/) {
132 # same as above, but 64-bit mnemonics...
134 $LDU= "ldu"; # load and update
136 $STU= "stdu"; # store and update
137 $UMULL= "mulld"; # unsigned multiply low
138 $UMULH= "mulhdu"; # unsigned multiply high
139 $UDIV= "divdu"; # unsigned divide
140 $UCMPI= "cmpldi"; # unsigned compare with immediate
141 $UCMP= "cmpld"; # unsigned compare
142 $COUNTZ="cntlzd"; # count leading zeros
143 $SHL= "sld"; # shift left
144 $SHR= "srd"; # unsigned shift right
145 $SHRI= "srdi"; # unsigned shift right by immediate
146 $SHLI= "sldi"; # shift left by immediate
147 $CLRU= "clrldi"; # clear upper bits
148 $INSR= "insrdi"; # insert right
149 $ROTL= "rotldi"; # rotate left by immediate
150 } else { die "nonsense $opf"; }
152 ( defined shift || open STDOUT,">$opf" ) || die "can't open $opf: $!";
154 # function entry points from the AIX code
156 # There are other, more elegant, ways to handle this. We (IBM) chose
157 # this approach as it plays well with scripts we run to 'namespace'
158 # OpenSSL .i.e. we add a prefix to all the public symbols so we can
159 # co-exist in the same process with other implementations of OpenSSL.
160 # 'cleverer' ways of doing these substitutions tend to hide data we
161 # need to be obvious.
163 my @items = ("bn_sqr_comba4",
174 if ($opf =~ /linux/) { do_linux(); }
175 elsif ($opf =~ /aix/) { do_aix(); }
176 elsif ($opf =~ /osx/) { do_osx(); }
183 foreach $t (@items) {
185 \t.section\t".opd","aw"\
189 \t.quad\t.$t,.TOC.\@tocbase,0\
192 \t.type\t.$t,\@function\
198 foreach $t (@items) {
202 # hide internal labels to avoid pollution of name table...
203 $d=~s/Lppcasm_/.Lppcasm_/gm;
208 # AIX assembler is smart enough to please the linker without
209 # making us do something special...
216 # Change the bn symbol prefix from '.' to '_'
217 foreach $t (@items) {
220 # Change .machine to something OS X asm will accept
221 $d=~s/\.machine.*/.text/g;
222 $d=~s/\#/;/g; # change comment from '#' to ';'
229 foreach $t (@items) {
237 #--------------------------------------------------------------------
244 # Created by: Suresh Chari
245 # IBM Thomas J. Watson Research Library
249 # Description: Optimized assembly routines for OpenSSL crypto
250 # on the 32 bitPowerPC platform.
255 # 2. Fixed bn_add,bn_sub and bn_div_words, added comments,
256 # cleaned up code. Also made a single version which can
257 # be used for both the AIX and Linux compilers. See NOTE
259 # 12/05/03 Suresh Chari
260 # (with lots of help from) Andy Polyakov
262 # 1. Initial version 10/20/02 Suresh Chari
265 # The following file works for the xlc,cc
268 # NOTE: To get the file to link correctly with the gcc compiler
269 # you have to change the names of the routines and remove
270 # the first .(dot) character. This should automatically
271 # be done in the build process.
273 # Hand optimized assembly code for the following routines
286 # NOTE: It is possible to optimize this code more for
287 # specific PowerPC or Power architectures. On the Northstar
288 # architecture the optimizations in this file do
289 # NOT provide much improvement.
291 # If you have comments or suggestions to improve code send
292 # me a note at schari\@us.ibm.com
294 #--------------------------------------------------------------------------
296 # Defines to be used in the assembly code.
298 .set r0,0 # we use it as storage for value of 0
299 .set SP,1 # preserved
300 .set RTOC,2 # preserved
301 .set r3,3 # 1st argument/return value
302 .set r4,4 # 2nd argument/volatile register
303 .set r5,5 # 3rd argument/volatile register
311 .set r13,13 # not used, nor any other "below" it...
315 .set BO_dCTR_NZERO,16
326 # Declare function names to be global
327 # NOTE: For gcc these names MUST be changed to remove
328 # the first . i.e. for example change ".bn_sqr_comba4"
329 # to "bn_sqr_comba4". This should be automatically done
332 .globl .bn_sqr_comba4
333 .globl .bn_sqr_comba8
334 .globl .bn_mul_comba4
335 .globl .bn_mul_comba8
341 .globl .bn_mul_add_words
348 # NOTE: The following label name should be changed to
349 # "bn_sqr_comba4" i.e. remove the first dot
350 # for the gcc compiler. This should be automatically
357 # Optimized version of bn_sqr_comba4.
359 # void bn_sqr_comba4(BN_ULONG *r, BN_ULONG *a)
363 # Freely use registers r5,r6,r7,r8,r9,r10,r11 as follows:
365 # r5,r6 are the two BN_ULONGs being multiplied.
366 # r7,r8 are the results of the 32x32 giving 64 bit multiply.
367 # r9,r10, r11 are the equivalents of c1,c2, c3.
368 # Here's the assembly
371 xor r0,r0,r0 # set r0 = 0. Used in the addze
374 #sqr_add_c(a,0,c1,c2,c3)
377 $UMULH r10,r5,r5 #in first iteration. No need
378 #to add since c1=c2=c3=0.
379 # Note c3(r11) is NOT set to 0
382 $ST r9,`0*$BNSZ`(r3) # r[0]=c1;
383 # sqr_add_c2(a,1,0,c2,c3,c1);
388 addc r7,r7,r7 # compute (r7,r8)=2*(r7,r8)
390 addze r9,r0 # catch carry if any.
391 # r9= r0(=0) and carry
393 addc r10,r7,r10 # now add to temp result.
394 addze r11,r8 # r8 added to r11 which is 0
397 $ST r10,`1*$BNSZ`(r3) #r[1]=c2;
398 #sqr_add_c(a,1,c3,c1,c2)
404 #sqr_add_c2(a,2,0,c3,c1,c2)
416 $ST r11,`2*$BNSZ`(r3) #r[2]=c3
417 #sqr_add_c2(a,3,0,c1,c2,c3);
428 #sqr_add_c2(a,2,1,c1,c2,c3);
440 $ST r9,`3*$BNSZ`(r3) #r[3]=c1
441 #sqr_add_c(a,2,c2,c3,c1);
447 #sqr_add_c2(a,3,1,c2,c3,c1);
458 $ST r10,`4*$BNSZ`(r3) #r[4]=c2
459 #sqr_add_c2(a,3,2,c3,c1,c2);
470 $ST r11,`5*$BNSZ`(r3) #r[5] = c3
471 #sqr_add_c(a,3,c1,c2,c3);
477 $ST r9,`6*$BNSZ`(r3) #r[6]=c1
478 $ST r10,`7*$BNSZ`(r3) #r[7]=c2
479 bclr BO_ALWAYS,CR0_LT
483 # NOTE: The following label name should be changed to
484 # "bn_sqr_comba8" i.e. remove the first dot
485 # for the gcc compiler. This should be automatically
492 # This is an optimized version of the bn_sqr_comba8 routine.
493 # Tightly uses the adde instruction
496 # void bn_sqr_comba8(BN_ULONG *r, BN_ULONG *a)
500 # Freely use registers r5,r6,r7,r8,r9,r10,r11 as follows:
502 # r5,r6 are the two BN_ULONGs being multiplied.
503 # r7,r8 are the results of the 32x32 giving 64 bit multiply.
504 # r9,r10, r11 are the equivalents of c1,c2, c3.
506 # Possible optimization of loading all 8 longs of a into registers
507 # doesnt provide any speedup
510 xor r0,r0,r0 #set r0 = 0.Used in addze
513 #sqr_add_c(a,0,c1,c2,c3);
515 $UMULL r9,r5,r5 #1st iteration: no carries.
517 $ST r9,`0*$BNSZ`(r3) # r[0]=c1;
518 #sqr_add_c2(a,1,0,c2,c3,c1);
523 addc r10,r7,r10 #add the two register number
524 adde r11,r8,r0 # (r8,r7) to the three register
525 addze r9,r0 # number (r9,r11,r10).NOTE:r0=0
527 addc r10,r7,r10 #add the two register number
528 adde r11,r8,r11 # (r8,r7) to the three register
529 addze r9,r9 # number (r9,r11,r10).
531 $ST r10,`1*$BNSZ`(r3) # r[1]=c2
533 #sqr_add_c(a,1,c3,c1,c2);
539 #sqr_add_c2(a,2,0,c3,c1,c2);
552 $ST r11,`2*$BNSZ`(r3) #r[2]=c3
553 #sqr_add_c2(a,3,0,c1,c2,c3);
554 $LD r6,`3*$BNSZ`(r4) #r6 = a[3]. r5 is already a[0].
565 #sqr_add_c2(a,2,1,c1,c2,c3);
579 $ST r9,`3*$BNSZ`(r3) #r[3]=c1;
580 #sqr_add_c(a,2,c2,c3,c1);
587 #sqr_add_c2(a,3,1,c2,c3,c1);
599 #sqr_add_c2(a,4,0,c2,c3,c1);
612 $ST r10,`4*$BNSZ`(r3) #r[4]=c2;
613 #sqr_add_c2(a,5,0,c3,c1,c2);
625 #sqr_add_c2(a,4,1,c3,c1,c2);
638 #sqr_add_c2(a,3,2,c3,c1,c2);
651 $ST r11,`5*$BNSZ`(r3) #r[5]=c3;
652 #sqr_add_c(a,3,c1,c2,c3);
658 #sqr_add_c2(a,4,2,c1,c2,c3);
670 #sqr_add_c2(a,5,1,c1,c2,c3);
683 #sqr_add_c2(a,6,0,c1,c2,c3);
694 $ST r9,`6*$BNSZ`(r3) #r[6]=c1;
695 #sqr_add_c2(a,7,0,c2,c3,c1);
706 #sqr_add_c2(a,6,1,c2,c3,c1);
718 #sqr_add_c2(a,5,2,c2,c3,c1);
729 #sqr_add_c2(a,4,3,c2,c3,c1);
741 $ST r10,`7*$BNSZ`(r3) #r[7]=c2;
742 #sqr_add_c(a,4,c3,c1,c2);
748 #sqr_add_c2(a,5,3,c3,c1,c2);
758 #sqr_add_c2(a,6,2,c3,c1,c2);
770 #sqr_add_c2(a,7,1,c3,c1,c2);
781 $ST r11,`8*$BNSZ`(r3) #r[8]=c3;
782 #sqr_add_c2(a,7,2,c1,c2,c3);
793 #sqr_add_c2(a,6,3,c1,c2,c3);
804 #sqr_add_c2(a,5,4,c1,c2,c3);
815 $ST r9,`9*$BNSZ`(r3) #r[9]=c1;
816 #sqr_add_c(a,5,c2,c3,c1);
822 #sqr_add_c2(a,6,4,c2,c3,c1);
832 #sqr_add_c2(a,7,3,c2,c3,c1);
843 $ST r10,`10*$BNSZ`(r3) #r[10]=c2;
844 #sqr_add_c2(a,7,4,c3,c1,c2);
854 #sqr_add_c2(a,6,5,c3,c1,c2);
865 $ST r11,`11*$BNSZ`(r3) #r[11]=c3;
866 #sqr_add_c(a,6,c1,c2,c3);
872 #sqr_add_c2(a,7,5,c1,c2,c3)
882 $ST r9,`12*$BNSZ`(r3) #r[12]=c1;
884 #sqr_add_c2(a,7,6,c2,c3,c1)
894 $ST r10,`13*$BNSZ`(r3) #r[13]=c2;
895 #sqr_add_c(a,7,c3,c1,c2);
900 $ST r11,`14*$BNSZ`(r3) #r[14]=c3;
901 $ST r9, `15*$BNSZ`(r3) #r[15]=c1;
904 bclr BO_ALWAYS,CR0_LT
909 # NOTE: The following label name should be changed to
910 # "bn_mul_comba4" i.e. remove the first dot
911 # for the gcc compiler. This should be automatically
918 # This is an optimized version of the bn_mul_comba4 routine.
920 # void bn_mul_comba4(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b)
924 # r6, r7 are the 2 BN_ULONGs being multiplied.
925 # r8, r9 are the results of the 32x32 giving 64 multiply.
926 # r10, r11, r12 are the equivalents of c1, c2, and c3.
928 xor r0,r0,r0 #r0=0. Used in addze below.
929 #mul_add_c(a[0],b[0],c1,c2,c3);
934 $ST r10,`0*$BNSZ`(r3) #r[0]=c1
935 #mul_add_c(a[0],b[1],c2,c3,c1);
942 #mul_add_c(a[1],b[0],c2,c3,c1);
943 $LD r6, `1*$BNSZ`(r4)
944 $LD r7, `0*$BNSZ`(r5)
950 $ST r11,`1*$BNSZ`(r3) #r[1]=c2
951 #mul_add_c(a[2],b[0],c3,c1,c2);
958 #mul_add_c(a[1],b[1],c3,c1,c2);
966 #mul_add_c(a[0],b[2],c3,c1,c2);
974 $ST r12,`2*$BNSZ`(r3) #r[2]=c3
975 #mul_add_c(a[0],b[3],c1,c2,c3);
982 #mul_add_c(a[1],b[2],c1,c2,c3);
990 #mul_add_c(a[2],b[1],c1,c2,c3);
998 #mul_add_c(a[3],b[0],c1,c2,c3);
1000 $LD r7,`0*$BNSZ`(r5)
1006 $ST r10,`3*$BNSZ`(r3) #r[3]=c1
1007 #mul_add_c(a[3],b[1],c2,c3,c1);
1008 $LD r7,`1*$BNSZ`(r5)
1014 #mul_add_c(a[2],b[2],c2,c3,c1);
1015 $LD r6,`2*$BNSZ`(r4)
1016 $LD r7,`2*$BNSZ`(r5)
1022 #mul_add_c(a[1],b[3],c2,c3,c1);
1023 $LD r6,`1*$BNSZ`(r4)
1024 $LD r7,`3*$BNSZ`(r5)
1030 $ST r11,`4*$BNSZ`(r3) #r[4]=c2
1031 #mul_add_c(a[2],b[3],c3,c1,c2);
1032 $LD r6,`2*$BNSZ`(r4)
1038 #mul_add_c(a[3],b[2],c3,c1,c2);
1039 $LD r6,`3*$BNSZ`(r4)
1040 $LD r7,`2*$BNSZ`(r4)
1046 $ST r12,`5*$BNSZ`(r3) #r[5]=c3
1047 #mul_add_c(a[3],b[3],c1,c2,c3);
1048 $LD r7,`3*$BNSZ`(r5)
1054 $ST r10,`6*$BNSZ`(r3) #r[6]=c1
1055 $ST r11,`7*$BNSZ`(r3) #r[7]=c2
1056 bclr BO_ALWAYS,CR0_LT
1060 # NOTE: The following label name should be changed to
1061 # "bn_mul_comba8" i.e. remove the first dot
1062 # for the gcc compiler. This should be automatically
1069 # Optimized version of the bn_mul_comba8 routine.
1071 # void bn_mul_comba8(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b)
1075 # r6, r7 are the 2 BN_ULONGs being multiplied.
1076 # r8, r9 are the results of the 32x32 giving 64 multiply.
1077 # r10, r11, r12 are the equivalents of c1, c2, and c3.
1079 xor r0,r0,r0 #r0=0. Used in addze below.
1081 #mul_add_c(a[0],b[0],c1,c2,c3);
1082 $LD r6,`0*$BNSZ`(r4) #a[0]
1083 $LD r7,`0*$BNSZ`(r5) #b[0]
1086 $ST r10,`0*$BNSZ`(r3) #r[0]=c1;
1087 #mul_add_c(a[0],b[1],c2,c3,c1);
1088 $LD r7,`1*$BNSZ`(r5)
1092 addze r12,r9 # since we didnt set r12 to zero before.
1094 #mul_add_c(a[1],b[0],c2,c3,c1);
1095 $LD r6,`1*$BNSZ`(r4)
1096 $LD r7,`0*$BNSZ`(r5)
1102 $ST r11,`1*$BNSZ`(r3) #r[1]=c2;
1103 #mul_add_c(a[2],b[0],c3,c1,c2);
1104 $LD r6,`2*$BNSZ`(r4)
1110 #mul_add_c(a[1],b[1],c3,c1,c2);
1111 $LD r6,`1*$BNSZ`(r4)
1112 $LD r7,`1*$BNSZ`(r5)
1118 #mul_add_c(a[0],b[2],c3,c1,c2);
1119 $LD r6,`0*$BNSZ`(r4)
1120 $LD r7,`2*$BNSZ`(r5)
1126 $ST r12,`2*$BNSZ`(r3) #r[2]=c3;
1127 #mul_add_c(a[0],b[3],c1,c2,c3);
1128 $LD r7,`3*$BNSZ`(r5)
1134 #mul_add_c(a[1],b[2],c1,c2,c3);
1135 $LD r6,`1*$BNSZ`(r4)
1136 $LD r7,`2*$BNSZ`(r5)
1143 #mul_add_c(a[2],b[1],c1,c2,c3);
1144 $LD r6,`2*$BNSZ`(r4)
1145 $LD r7,`1*$BNSZ`(r5)
1151 #mul_add_c(a[3],b[0],c1,c2,c3);
1152 $LD r6,`3*$BNSZ`(r4)
1153 $LD r7,`0*$BNSZ`(r5)
1159 $ST r10,`3*$BNSZ`(r3) #r[3]=c1;
1160 #mul_add_c(a[4],b[0],c2,c3,c1);
1161 $LD r6,`4*$BNSZ`(r4)
1167 #mul_add_c(a[3],b[1],c2,c3,c1);
1168 $LD r6,`3*$BNSZ`(r4)
1169 $LD r7,`1*$BNSZ`(r5)
1175 #mul_add_c(a[2],b[2],c2,c3,c1);
1176 $LD r6,`2*$BNSZ`(r4)
1177 $LD r7,`2*$BNSZ`(r5)
1183 #mul_add_c(a[1],b[3],c2,c3,c1);
1184 $LD r6,`1*$BNSZ`(r4)
1185 $LD r7,`3*$BNSZ`(r5)
1191 #mul_add_c(a[0],b[4],c2,c3,c1);
1192 $LD r6,`0*$BNSZ`(r4)
1193 $LD r7,`4*$BNSZ`(r5)
1199 $ST r11,`4*$BNSZ`(r3) #r[4]=c2;
1200 #mul_add_c(a[0],b[5],c3,c1,c2);
1201 $LD r7,`5*$BNSZ`(r5)
1207 #mul_add_c(a[1],b[4],c3,c1,c2);
1208 $LD r6,`1*$BNSZ`(r4)
1209 $LD r7,`4*$BNSZ`(r5)
1215 #mul_add_c(a[2],b[3],c3,c1,c2);
1216 $LD r6,`2*$BNSZ`(r4)
1217 $LD r7,`3*$BNSZ`(r5)
1223 #mul_add_c(a[3],b[2],c3,c1,c2);
1224 $LD r6,`3*$BNSZ`(r4)
1225 $LD r7,`2*$BNSZ`(r5)
1231 #mul_add_c(a[4],b[1],c3,c1,c2);
1232 $LD r6,`4*$BNSZ`(r4)
1233 $LD r7,`1*$BNSZ`(r5)
1239 #mul_add_c(a[5],b[0],c3,c1,c2);
1240 $LD r6,`5*$BNSZ`(r4)
1241 $LD r7,`0*$BNSZ`(r5)
1247 $ST r12,`5*$BNSZ`(r3) #r[5]=c3;
1248 #mul_add_c(a[6],b[0],c1,c2,c3);
1249 $LD r6,`6*$BNSZ`(r4)
1255 #mul_add_c(a[5],b[1],c1,c2,c3);
1256 $LD r6,`5*$BNSZ`(r4)
1257 $LD r7,`1*$BNSZ`(r5)
1263 #mul_add_c(a[4],b[2],c1,c2,c3);
1264 $LD r6,`4*$BNSZ`(r4)
1265 $LD r7,`2*$BNSZ`(r5)
1271 #mul_add_c(a[3],b[3],c1,c2,c3);
1272 $LD r6,`3*$BNSZ`(r4)
1273 $LD r7,`3*$BNSZ`(r5)
1279 #mul_add_c(a[2],b[4],c1,c2,c3);
1280 $LD r6,`2*$BNSZ`(r4)
1281 $LD r7,`4*$BNSZ`(r5)
1287 #mul_add_c(a[1],b[5],c1,c2,c3);
1288 $LD r6,`1*$BNSZ`(r4)
1289 $LD r7,`5*$BNSZ`(r5)
1295 #mul_add_c(a[0],b[6],c1,c2,c3);
1296 $LD r6,`0*$BNSZ`(r4)
1297 $LD r7,`6*$BNSZ`(r5)
1303 $ST r10,`6*$BNSZ`(r3) #r[6]=c1;
1304 #mul_add_c(a[0],b[7],c2,c3,c1);
1305 $LD r7,`7*$BNSZ`(r5)
1311 #mul_add_c(a[1],b[6],c2,c3,c1);
1312 $LD r6,`1*$BNSZ`(r4)
1313 $LD r7,`6*$BNSZ`(r5)
1319 #mul_add_c(a[2],b[5],c2,c3,c1);
1320 $LD r6,`2*$BNSZ`(r4)
1321 $LD r7,`5*$BNSZ`(r5)
1327 #mul_add_c(a[3],b[4],c2,c3,c1);
1328 $LD r6,`3*$BNSZ`(r4)
1329 $LD r7,`4*$BNSZ`(r5)
1335 #mul_add_c(a[4],b[3],c2,c3,c1);
1336 $LD r6,`4*$BNSZ`(r4)
1337 $LD r7,`3*$BNSZ`(r5)
1343 #mul_add_c(a[5],b[2],c2,c3,c1);
1344 $LD r6,`5*$BNSZ`(r4)
1345 $LD r7,`2*$BNSZ`(r5)
1351 #mul_add_c(a[6],b[1],c2,c3,c1);
1352 $LD r6,`6*$BNSZ`(r4)
1353 $LD r7,`1*$BNSZ`(r5)
1359 #mul_add_c(a[7],b[0],c2,c3,c1);
1360 $LD r6,`7*$BNSZ`(r4)
1361 $LD r7,`0*$BNSZ`(r5)
1367 $ST r11,`7*$BNSZ`(r3) #r[7]=c2;
1368 #mul_add_c(a[7],b[1],c3,c1,c2);
1369 $LD r7,`1*$BNSZ`(r5)
1375 #mul_add_c(a[6],b[2],c3,c1,c2);
1376 $LD r6,`6*$BNSZ`(r4)
1377 $LD r7,`2*$BNSZ`(r5)
1383 #mul_add_c(a[5],b[3],c3,c1,c2);
1384 $LD r6,`5*$BNSZ`(r4)
1385 $LD r7,`3*$BNSZ`(r5)
1391 #mul_add_c(a[4],b[4],c3,c1,c2);
1392 $LD r6,`4*$BNSZ`(r4)
1393 $LD r7,`4*$BNSZ`(r5)
1399 #mul_add_c(a[3],b[5],c3,c1,c2);
1400 $LD r6,`3*$BNSZ`(r4)
1401 $LD r7,`5*$BNSZ`(r5)
1407 #mul_add_c(a[2],b[6],c3,c1,c2);
1408 $LD r6,`2*$BNSZ`(r4)
1409 $LD r7,`6*$BNSZ`(r5)
1415 #mul_add_c(a[1],b[7],c3,c1,c2);
1416 $LD r6,`1*$BNSZ`(r4)
1417 $LD r7,`7*$BNSZ`(r5)
1423 $ST r12,`8*$BNSZ`(r3) #r[8]=c3;
1424 #mul_add_c(a[2],b[7],c1,c2,c3);
1425 $LD r6,`2*$BNSZ`(r4)
1431 #mul_add_c(a[3],b[6],c1,c2,c3);
1432 $LD r6,`3*$BNSZ`(r4)
1433 $LD r7,`6*$BNSZ`(r5)
1439 #mul_add_c(a[4],b[5],c1,c2,c3);
1440 $LD r6,`4*$BNSZ`(r4)
1441 $LD r7,`5*$BNSZ`(r5)
1447 #mul_add_c(a[5],b[4],c1,c2,c3);
1448 $LD r6,`5*$BNSZ`(r4)
1449 $LD r7,`4*$BNSZ`(r5)
1455 #mul_add_c(a[6],b[3],c1,c2,c3);
1456 $LD r6,`6*$BNSZ`(r4)
1457 $LD r7,`3*$BNSZ`(r5)
1463 #mul_add_c(a[7],b[2],c1,c2,c3);
1464 $LD r6,`7*$BNSZ`(r4)
1465 $LD r7,`2*$BNSZ`(r5)
1471 $ST r10,`9*$BNSZ`(r3) #r[9]=c1;
1472 #mul_add_c(a[7],b[3],c2,c3,c1);
1473 $LD r7,`3*$BNSZ`(r5)
1479 #mul_add_c(a[6],b[4],c2,c3,c1);
1480 $LD r6,`6*$BNSZ`(r4)
1481 $LD r7,`4*$BNSZ`(r5)
1487 #mul_add_c(a[5],b[5],c2,c3,c1);
1488 $LD r6,`5*$BNSZ`(r4)
1489 $LD r7,`5*$BNSZ`(r5)
1495 #mul_add_c(a[4],b[6],c2,c3,c1);
1496 $LD r6,`4*$BNSZ`(r4)
1497 $LD r7,`6*$BNSZ`(r5)
1503 #mul_add_c(a[3],b[7],c2,c3,c1);
1504 $LD r6,`3*$BNSZ`(r4)
1505 $LD r7,`7*$BNSZ`(r5)
1511 $ST r11,`10*$BNSZ`(r3) #r[10]=c2;
1512 #mul_add_c(a[4],b[7],c3,c1,c2);
1513 $LD r6,`4*$BNSZ`(r4)
1519 #mul_add_c(a[5],b[6],c3,c1,c2);
1520 $LD r6,`5*$BNSZ`(r4)
1521 $LD r7,`6*$BNSZ`(r5)
1527 #mul_add_c(a[6],b[5],c3,c1,c2);
1528 $LD r6,`6*$BNSZ`(r4)
1529 $LD r7,`5*$BNSZ`(r5)
1535 #mul_add_c(a[7],b[4],c3,c1,c2);
1536 $LD r6,`7*$BNSZ`(r4)
1537 $LD r7,`4*$BNSZ`(r5)
1543 $ST r12,`11*$BNSZ`(r3) #r[11]=c3;
1544 #mul_add_c(a[7],b[5],c1,c2,c3);
1545 $LD r7,`5*$BNSZ`(r5)
1551 #mul_add_c(a[6],b[6],c1,c2,c3);
1552 $LD r6,`6*$BNSZ`(r4)
1553 $LD r7,`6*$BNSZ`(r5)
1559 #mul_add_c(a[5],b[7],c1,c2,c3);
1560 $LD r6,`5*$BNSZ`(r4)
1561 $LD r7,`7*$BNSZ`(r5)
1567 $ST r10,`12*$BNSZ`(r3) #r[12]=c1;
1568 #mul_add_c(a[6],b[7],c2,c3,c1);
1569 $LD r6,`6*$BNSZ`(r4)
1575 #mul_add_c(a[7],b[6],c2,c3,c1);
1576 $LD r6,`7*$BNSZ`(r4)
1577 $LD r7,`6*$BNSZ`(r5)
1583 $ST r11,`13*$BNSZ`(r3) #r[13]=c2;
1584 #mul_add_c(a[7],b[7],c3,c1,c2);
1585 $LD r7,`7*$BNSZ`(r5)
1590 $ST r12,`14*$BNSZ`(r3) #r[14]=c3;
1591 $ST r10,`15*$BNSZ`(r3) #r[15]=c1;
1592 bclr BO_ALWAYS,CR0_LT
1596 # NOTE: The following label name should be changed to
1597 # "bn_sub_words" i.e. remove the first dot
1598 # for the gcc compiler. This should be automatically
1605 # Handcoded version of bn_sub_words
1607 #BN_ULONG bn_sub_words(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b, int n)
1614 # Note: No loop unrolling done since this is not a performance
1617 xor r0,r0,r0 #set r0 = 0
1619 # check for r6 = 0 AND set carry bit.
1621 subfc. r7,r0,r6 # If r6 is 0 then result is 0.
1622 # if r6 > 0 then result !=0
1623 # In either case carry bit is set.
1624 bc BO_IF,CR0_EQ,Lppcasm_sub_adios
1629 Lppcasm_sub_mainloop:
1632 subfe r6,r8,r7 # r6 = r7+carry bit + onescomplement(r8)
1633 # if carry = 1 this is r7-r8. Else it
1634 # is r7-r8 -1 as we need.
1636 bc BO_dCTR_NZERO,CR0_EQ,Lppcasm_sub_mainloop
1638 subfze r3,r0 # if carry bit is set then r3 = 0 else -1
1639 andi. r3,r3,1 # keep only last bit.
1640 bclr BO_ALWAYS,CR0_LT
1645 # NOTE: The following label name should be changed to
1646 # "bn_add_words" i.e. remove the first dot
1647 # for the gcc compiler. This should be automatically
1654 # Handcoded version of bn_add_words
1656 #BN_ULONG bn_add_words(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b, int n)
1663 # Note: No loop unrolling done since this is not a performance
1668 # check for r6 = 0. Is this needed?
1670 addic. r6,r6,0 #test r6 and clear carry bit.
1671 bc BO_IF,CR0_EQ,Lppcasm_add_adios
1676 Lppcasm_add_mainloop:
1681 bc BO_dCTR_NZERO,CR0_EQ,Lppcasm_add_mainloop
1683 addze r3,r0 #return carry bit.
1684 bclr BO_ALWAYS,CR0_LT
1688 # NOTE: The following label name should be changed to
1689 # "bn_div_words" i.e. remove the first dot
1690 # for the gcc compiler. This should be automatically
1697 # This is a cleaned up version of code generated by
1698 # the AIX compiler. The only optimization is to use
1699 # the PPC instruction to count leading zeros instead
1700 # of call to num_bits_word. Since this was compiled
1701 # only at level -O2 we can possibly squeeze it more?
1707 $UCMPI 0,r5,0 # compare r5 and 0
1708 bc BO_IF_NOT,CR0_EQ,Lppcasm_div1 # proceed if d!=0
1709 li r3,-1 # d=0 return -1
1710 bclr BO_ALWAYS,CR0_LT
1713 $COUNTZ r7,r5 #r7 = num leading 0s in d.
1714 subfic r8,r7,$BITS #r8 = BN_num_bits_word(d)
1716 bc BO_IF,CR0_EQ,Lppcasm_div2 #proceed if (r8==$BITS)
1718 $SHL r10,r9,r8 # r9<<=r8
1720 bc BO_IF,CR0_GT,Lppcasm_div2 #or if (h > (1<<r8))
1721 $UDIV r3,r3,r0 #if not assert(0) divide by 0!
1722 #that's how we signal overflow
1723 bclr BO_ALWAYS,CR0_LT #return. NEVER REACHED.
1725 $UCMP 0,r3,r5 #h>=d?
1726 bc BO_IF,CR0_LT,Lppcasm_div3 #goto Lppcasm_div3 if not
1727 subf r3,r5,r3 #h-=d ;
1728 Lppcasm_div3: #r7 = BN_BITS2-i. so r7=i
1729 cmpi 0,0,r7,0 # is (i == 0)?
1730 bc BO_IF,CR0_EQ,Lppcasm_div4
1731 $SHL r3,r3,r7 # h = (h<< i)
1732 $SHR r8,r4,r8 # r8 = (l >> BN_BITS2 -i)
1733 $SHL r5,r5,r7 # d<<=i
1734 or r3,r3,r8 # h = (h<<i)|(l>>(BN_BITS2-i))
1735 $SHL r4,r4,r7 # l <<=i
1737 $SHRI r9,r5,`$BITS/2` # r9 = dh
1738 # dl will be computed when needed
1739 # as it saves registers.
1741 mtctr r6 #counter will be in count.
1742 Lppcasm_divouterloop:
1743 $SHRI r8,r3,`$BITS/2` #r8 = (h>>BN_BITS4)
1744 $SHRI r11,r4,`$BITS/2` #r11= (l&BN_MASK2h)>>BN_BITS4
1745 # compute here for innerloop.
1746 $UCMP 0,r8,r9 # is (h>>BN_BITS4)==dh
1747 bc BO_IF_NOT,CR0_EQ,Lppcasm_div5 # goto Lppcasm_div5 if not
1750 $CLRU r8,r8,`$BITS/2` #q = BN_MASK2l
1753 $UDIV r8,r3,r9 #q = h/dh
1755 $UMULL r12,r9,r8 #th = q*dh
1756 $CLRU r10,r5,`$BITS/2` #r10=dl
1757 $UMULL r6,r8,r10 #tl = q*dl
1759 Lppcasm_divinnerloop:
1760 subf r10,r12,r3 #t = h -th
1761 $SHRI r7,r10,`$BITS/2` #r7= (t &BN_MASK2H), sort of...
1762 addic. r7,r7,0 #test if r7 == 0. used below.
1763 # now want to compute
1764 # r7 = (t<<BN_BITS4)|((l&BN_MASK2h)>>BN_BITS4)
1765 # the following 2 instructions do that
1766 $SHLI r7,r10,`$BITS/2` # r7 = (t<<BN_BITS4)
1767 or r7,r7,r11 # r7|=((l&BN_MASK2h)>>BN_BITS4)
1768 $UCMP 1,r6,r7 # compare (tl <= r7)
1769 bc BO_IF_NOT,CR0_EQ,Lppcasm_divinnerexit
1770 bc BO_IF_NOT,CR1_FEX,Lppcasm_divinnerexit
1772 subf r12,r9,r12 #th -=dh
1773 $CLRU r10,r5,`$BITS/2` #r10=dl. t is no longer needed in loop.
1774 subf r6,r10,r6 #tl -=dl
1775 b Lppcasm_divinnerloop
1776 Lppcasm_divinnerexit:
1777 $SHRI r10,r6,`$BITS/2` #t=(tl>>BN_BITS4)
1778 $SHLI r11,r6,`$BITS/2` #tl=(tl<<BN_BITS4)&BN_MASK2h;
1779 $UCMP 1,r4,r11 # compare l and tl
1780 add r12,r12,r10 # th+=t
1781 bc BO_IF_NOT,CR1_FX,Lppcasm_div7 # if (l>=tl) goto Lppcasm_div7
1782 addi r12,r12,1 # th++
1784 subf r11,r11,r4 #r11=l-tl
1785 $UCMP 1,r3,r12 #compare h and th
1786 bc BO_IF_NOT,CR1_FX,Lppcasm_div8 #if (h>=th) goto Lppcasm_div8
1790 subf r12,r12,r3 #r12 = h-th
1791 $SHLI r4,r11,`$BITS/2` #l=(l&BN_MASK2l)<<BN_BITS4
1793 # h = ((h<<BN_BITS4)|(l>>BN_BITS4))&BN_MASK2
1794 # the following 2 instructions will do this.
1795 $INSR r11,r12,`$BITS/2`,`$BITS/2` # r11 is the value we want rotated $BITS/2.
1796 $ROTL r3,r11,`$BITS/2` # rotate by $BITS/2 and store in r3
1797 bc BO_dCTR_ZERO,CR0_EQ,Lppcasm_div9#if (count==0) break ;
1798 $SHLI r0,r8,`$BITS/2` #ret =q<<BN_BITS4
1799 b Lppcasm_divouterloop
1802 bclr BO_ALWAYS,CR0_LT
1806 # NOTE: The following label name should be changed to
1807 # "bn_sqr_words" i.e. remove the first dot
1808 # for the gcc compiler. This should be automatically
1814 # Optimized version of bn_sqr_words
1816 # void bn_sqr_words(BN_ULONG *r, BN_ULONG *a, int n)
1825 # No unrolling done here. Not performance critical.
1827 addic. r5,r5,0 #test r5.
1828 bc BO_IF,CR0_EQ,Lppcasm_sqr_adios
1832 Lppcasm_sqr_mainloop:
1833 #sqr(r[0],r[1],a[0]);
1839 bc BO_dCTR_NZERO,CR0_EQ,Lppcasm_sqr_mainloop
1841 bclr BO_ALWAYS,CR0_LT
1846 # NOTE: The following label name should be changed to
1847 # "bn_mul_words" i.e. remove the first dot
1848 # for the gcc compiler. This should be automatically
1855 # BN_ULONG bn_mul_words(BN_ULONG *rp, BN_ULONG *ap, int num, BN_ULONG w)
1862 xor r12,r12,r12 # used for carry
1863 rlwinm. r7,r5,30,2,31 # num >> 2
1864 bc BO_IF,CR0_EQ,Lppcasm_mw_REM
1867 #mul(rp[0],ap[0],w,c1);
1868 $LD r8,`0*$BNSZ`(r4)
1872 #addze r10,r10 #carry is NOT ignored.
1873 #will be taken care of
1874 #in second spin below
1876 $ST r9,`0*$BNSZ`(r3)
1877 #mul(rp[1],ap[1],w,c1);
1878 $LD r8,`1*$BNSZ`(r4)
1883 $ST r11,`1*$BNSZ`(r3)
1884 #mul(rp[2],ap[2],w,c1);
1885 $LD r8,`2*$BNSZ`(r4)
1890 $ST r9,`2*$BNSZ`(r3)
1891 #mul_add(rp[3],ap[3],w,c1);
1892 $LD r8,`3*$BNSZ`(r4)
1896 addze r12,r12 #this spin we collect carry into
1898 $ST r11,`3*$BNSZ`(r3)
1900 addi r3,r3,`4*$BNSZ`
1901 addi r4,r4,`4*$BNSZ`
1902 bc BO_dCTR_NZERO,CR0_EQ,Lppcasm_mw_LOOP
1906 bc BO_IF,CR0_EQ,Lppcasm_mw_OVER
1907 #mul(rp[0],ap[0],w,c1);
1908 $LD r8,`0*$BNSZ`(r4)
1913 $ST r9,`0*$BNSZ`(r3)
1918 bc BO_IF,CR0_EQ,Lppcasm_mw_OVER
1921 #mul(rp[1],ap[1],w,c1);
1922 $LD r8,`1*$BNSZ`(r4)
1927 $ST r9,`1*$BNSZ`(r3)
1932 bc BO_IF,CR0_EQ,Lppcasm_mw_OVER
1934 #mul_add(rp[2],ap[2],w,c1);
1935 $LD r8,`2*$BNSZ`(r4)
1940 $ST r9,`2*$BNSZ`(r3)
1945 bclr BO_ALWAYS,CR0_LT
1949 # NOTE: The following label name should be changed to
1950 # "bn_mul_add_words" i.e. remove the first dot
1951 # for the gcc compiler. This should be automatically
1958 # BN_ULONG bn_mul_add_words(BN_ULONG *rp, BN_ULONG *ap, int num, BN_ULONG w)
1965 # empirical evidence suggests that unrolled version performs best!!
1967 xor r0,r0,r0 #r0 = 0
1968 xor r12,r12,r12 #r12 = 0 . used for carry
1969 rlwinm. r7,r5,30,2,31 # num >> 2
1970 bc BO_IF,CR0_EQ,Lppcasm_maw_leftover # if (num < 4) go LPPCASM_maw_leftover
1972 Lppcasm_maw_mainloop:
1973 #mul_add(rp[0],ap[0],w,c1);
1974 $LD r8,`0*$BNSZ`(r4)
1975 $LD r11,`0*$BNSZ`(r3)
1978 addc r9,r9,r12 #r12 is carry.
1982 #the above instruction addze
1983 #is NOT needed. Carry will NOT
1984 #be ignored. It's not affected
1985 #by multiply and will be collected
1987 $ST r9,`0*$BNSZ`(r3)
1989 #mul_add(rp[1],ap[1],w,c1);
1990 $LD r8,`1*$BNSZ`(r4)
1991 $LD r9,`1*$BNSZ`(r3)
1994 adde r11,r11,r10 #r10 is carry.
1998 $ST r11,`1*$BNSZ`(r3)
2000 #mul_add(rp[2],ap[2],w,c1);
2001 $LD r8,`2*$BNSZ`(r4)
2003 $LD r11,`2*$BNSZ`(r3)
2009 $ST r9,`2*$BNSZ`(r3)
2011 #mul_add(rp[3],ap[3],w,c1);
2012 $LD r8,`3*$BNSZ`(r4)
2014 $LD r9,`3*$BNSZ`(r3)
2020 $ST r11,`3*$BNSZ`(r3)
2021 addi r3,r3,`4*$BNSZ`
2022 addi r4,r4,`4*$BNSZ`
2023 bc BO_dCTR_NZERO,CR0_EQ,Lppcasm_maw_mainloop
2025 Lppcasm_maw_leftover:
2027 bc BO_IF,CR0_EQ,Lppcasm_maw_adios
2030 #mul_add(rp[0],ap[0],w,c1);
2042 bc BO_dCTR_ZERO,CR0_EQ,Lppcasm_maw_adios
2043 #mul_add(rp[1],ap[1],w,c1);
2054 bc BO_dCTR_ZERO,CR0_EQ,Lppcasm_maw_adios
2055 #mul_add(rp[2],ap[2],w,c1);
2068 bclr BO_ALWAYS,CR0_LT
2072 $data =~ s/\`([^\`]*)\`/eval $1/gem;
2074 # if some assembler chokes on some simplified mnemonic,
2075 # this is the spot to fix it up, e.g.:
2076 # GNU as doesn't seem to accept cmplw, 32-bit unsigned compare
2077 $data =~ s/^(\s*)cmplw(\s+)([^,]+),(.*)/$1cmpl$2$3,0,$4/gm;
2078 # assembler X doesn't accept li, load immediate value
2079 #$data =~ s/^(\s*)li(\s+)([^,]+),(.*)/$1addi$2$3,0,$4/gm;