94775d3f6c83c163206b27e78754029ff6e19d28
[openssl.git] / crypto / bn / asm / via-mont.pl
1 #! /usr/bin/env perl
2 # Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
3 #
4 # Licensed under the OpenSSL license (the "License").  You may not use
5 # this file except in compliance with the License.  You can obtain a copy
6 # in the file LICENSE in the source distribution or at
7 # https://www.openssl.org/source/license.html
8
9 #
10 # ====================================================================
11 # Written by Andy Polyakov <appro@fy.chalmers.se> for the OpenSSL
12 # project. The module is, however, dual licensed under OpenSSL and
13 # CRYPTOGAMS licenses depending on where you obtain it. For further
14 # details see http://www.openssl.org/~appro/cryptogams/.
15 # ====================================================================
16 #
17 # Wrapper around 'rep montmul', VIA-specific instruction accessing
18 # PadLock Montgomery Multiplier. The wrapper is designed as drop-in
19 # replacement for OpenSSL bn_mul_mont [first implemented in 0.9.9].
20 #
21 # Below are interleaved outputs from 'openssl speed rsa dsa' for 4
22 # different software configurations on 1.5GHz VIA Esther processor.
23 # Lines marked with "software integer" denote performance of hand-
24 # coded integer-only assembler found in OpenSSL 0.9.7. "Software SSE2"
25 # refers to hand-coded SSE2 Montgomery multiplication procedure found
26 # OpenSSL 0.9.9. "Hardware VIA SDK" refers to padlock_pmm routine from
27 # Padlock SDK 2.0.1 available for download from VIA, which naturally
28 # utilizes the magic 'repz montmul' instruction. And finally "hardware
29 # this" refers to *this* implementation which also uses 'repz montmul'
30 #
31 #                   sign    verify    sign/s verify/s
32 # rsa  512 bits 0.001720s 0.000140s    581.4   7149.7   software integer
33 # rsa  512 bits 0.000690s 0.000086s   1450.3  11606.0   software SSE2
34 # rsa  512 bits 0.006136s 0.000201s    163.0   4974.5   hardware VIA SDK
35 # rsa  512 bits 0.000712s 0.000050s   1404.9  19858.5   hardware this
36 #
37 # rsa 1024 bits 0.008518s 0.000413s    117.4   2420.8   software integer
38 # rsa 1024 bits 0.004275s 0.000277s    233.9   3609.7   software SSE2
39 # rsa 1024 bits 0.012136s 0.000260s     82.4   3844.5   hardware VIA SDK
40 # rsa 1024 bits 0.002522s 0.000116s    396.5   8650.9   hardware this
41 #
42 # rsa 2048 bits 0.050101s 0.001371s     20.0    729.6   software integer
43 # rsa 2048 bits 0.030273s 0.001008s     33.0    991.9   software SSE2
44 # rsa 2048 bits 0.030833s 0.000976s     32.4   1025.1   hardware VIA SDK
45 # rsa 2048 bits 0.011879s 0.000342s     84.2   2921.7   hardware this
46 #
47 # rsa 4096 bits 0.327097s 0.004859s      3.1    205.8   software integer
48 # rsa 4096 bits 0.229318s 0.003859s      4.4    259.2   software SSE2
49 # rsa 4096 bits 0.233953s 0.003274s      4.3    305.4   hardware VIA SDK
50 # rsa 4096 bits 0.070493s 0.001166s     14.2    857.6   hardware this
51 #
52 # dsa  512 bits 0.001342s 0.001651s    745.2    605.7   software integer
53 # dsa  512 bits 0.000844s 0.000987s   1185.3   1013.1   software SSE2
54 # dsa  512 bits 0.001902s 0.002247s    525.6    444.9   hardware VIA SDK
55 # dsa  512 bits 0.000458s 0.000524s   2182.2   1909.1   hardware this
56 #
57 # dsa 1024 bits 0.003964s 0.004926s    252.3    203.0   software integer
58 # dsa 1024 bits 0.002686s 0.003166s    372.3    315.8   software SSE2
59 # dsa 1024 bits 0.002397s 0.002823s    417.1    354.3   hardware VIA SDK
60 # dsa 1024 bits 0.000978s 0.001170s   1022.2    855.0   hardware this
61 #
62 # dsa 2048 bits 0.013280s 0.016518s     75.3     60.5   software integer
63 # dsa 2048 bits 0.009911s 0.011522s    100.9     86.8   software SSE2
64 # dsa 2048 bits 0.009542s 0.011763s    104.8     85.0   hardware VIA SDK
65 # dsa 2048 bits 0.002884s 0.003352s    346.8    298.3   hardware this
66 #
67 # To give you some other reference point here is output for 2.4GHz P4
68 # running hand-coded SSE2 bn_mul_mont found in 0.9.9, i.e. "software
69 # SSE2" in above terms.
70 #
71 # rsa  512 bits 0.000407s 0.000047s   2454.2  21137.0
72 # rsa 1024 bits 0.002426s 0.000141s    412.1   7100.0
73 # rsa 2048 bits 0.015046s 0.000491s     66.5   2034.9
74 # rsa 4096 bits 0.109770s 0.002379s      9.1    420.3
75 # dsa  512 bits 0.000438s 0.000525s   2281.1   1904.1
76 # dsa 1024 bits 0.001346s 0.001595s    742.7    627.0
77 # dsa 2048 bits 0.004745s 0.005582s    210.7    179.1
78 #
79 # Conclusions:
80 # - VIA SDK leaves a *lot* of room for improvement (which this
81 #   implementation successfully fills:-);
82 # - 'rep montmul' gives up to >3x performance improvement depending on
83 #   key length;
84 # - in terms of absolute performance it delivers approximately as much
85 #   as modern out-of-order 32-bit cores [again, for longer keys].
86
87 $0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
88 push(@INC,"${dir}","${dir}../../perlasm");
89 require "x86asm.pl";
90
91 $output = pop;
92 open STDOUT,">$output";
93
94 &asm_init($ARGV[0]);
95
96 # int bn_mul_mont(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp, const BN_ULONG *np,const BN_ULONG *n0, int num);
97 $func="bn_mul_mont_padlock";
98
99 $pad=16*1;      # amount of reserved bytes on top of every vector
100
101 # stack layout
102 $mZeroPrime=&DWP(0,"esp");              # these are specified by VIA
103 $A=&DWP(4,"esp");
104 $B=&DWP(8,"esp");
105 $T=&DWP(12,"esp");
106 $M=&DWP(16,"esp");
107 $scratch=&DWP(20,"esp");
108 $rp=&DWP(24,"esp");                     # these are mine
109 $sp=&DWP(28,"esp");
110 # &DWP(32,"esp")                        # 32 byte scratch area
111 # &DWP(64+(4*$num+$pad)*0,"esp")        # padded tp[num]
112 # &DWP(64+(4*$num+$pad)*1,"esp")        # padded copy of ap[num]
113 # &DWP(64+(4*$num+$pad)*2,"esp")        # padded copy of bp[num]
114 # &DWP(64+(4*$num+$pad)*3,"esp")        # padded copy of np[num]
115 # Note that SDK suggests to unconditionally allocate 2K per vector. This
116 # has quite an impact on performance. It naturally depends on key length,
117 # but to give an example 1024 bit private RSA key operations suffer >30%
118 # penalty. I allocate only as much as actually required...
119
120 &function_begin($func);
121         &xor    ("eax","eax");
122         &mov    ("ecx",&wparam(5));     # num
123         # meet VIA's limitations for num [note that the specification
124         # expresses them in bits, while we work with amount of 32-bit words]
125         &test   ("ecx",3);
126         &jnz    (&label("leave"));      # num % 4 != 0
127         &cmp    ("ecx",8);
128         &jb     (&label("leave"));      # num < 8
129         &cmp    ("ecx",1024);
130         &ja     (&label("leave"));      # num > 1024
131
132         &pushf  ();
133         &cld    ();
134
135         &mov    ("edi",&wparam(0));     # rp
136         &mov    ("eax",&wparam(1));     # ap
137         &mov    ("ebx",&wparam(2));     # bp
138         &mov    ("edx",&wparam(3));     # np
139         &mov    ("esi",&wparam(4));     # n0
140         &mov    ("esi",&DWP(0,"esi"));  # *n0
141
142         &lea    ("ecx",&DWP($pad,"","ecx",4));  # ecx becomes vector size in bytes
143         &lea    ("ebp",&DWP(64,"","ecx",4));    # allocate 4 vectors + 64 bytes
144         &neg    ("ebp");
145         &add    ("ebp","esp");
146         &and    ("ebp",-64);            # align to cache-line
147         &xchg   ("ebp","esp");          # alloca
148
149         &mov    ($rp,"edi");            # save rp
150         &mov    ($sp,"ebp");            # save esp
151
152         &mov    ($mZeroPrime,"esi");
153         &lea    ("esi",&DWP(64,"esp")); # tp
154         &mov    ($T,"esi");
155         &lea    ("edi",&DWP(32,"esp")); # scratch area
156         &mov    ($scratch,"edi");
157         &mov    ("esi","eax");
158
159         &lea    ("ebp",&DWP(-$pad,"ecx"));
160         &shr    ("ebp",2);              # restore original num value in ebp
161
162         &xor    ("eax","eax");
163
164         &mov    ("ecx","ebp");
165         &lea    ("ecx",&DWP((32+$pad)/4,"ecx"));# padded tp + scratch
166         &data_byte(0xf3,0xab);          # rep stosl, bzero
167
168         &mov    ("ecx","ebp");
169         &lea    ("edi",&DWP(64+$pad,"esp","ecx",4));# pointer to ap copy
170         &mov    ($A,"edi");
171         &data_byte(0xf3,0xa5);          # rep movsl, memcpy
172         &mov    ("ecx",$pad/4);
173         &data_byte(0xf3,0xab);          # rep stosl, bzero pad
174         # edi points at the end of padded ap copy...
175
176         &mov    ("ecx","ebp");
177         &mov    ("esi","ebx");
178         &mov    ($B,"edi");
179         &data_byte(0xf3,0xa5);          # rep movsl, memcpy
180         &mov    ("ecx",$pad/4);
181         &data_byte(0xf3,0xab);          # rep stosl, bzero pad
182         # edi points at the end of padded bp copy...
183
184         &mov    ("ecx","ebp");
185         &mov    ("esi","edx");
186         &mov    ($M,"edi");
187         &data_byte(0xf3,0xa5);          # rep movsl, memcpy
188         &mov    ("ecx",$pad/4);
189         &data_byte(0xf3,0xab);          # rep stosl, bzero pad
190         # edi points at the end of padded np copy...
191
192         # let magic happen...
193         &mov    ("ecx","ebp");
194         &mov    ("esi","esp");
195         &shl    ("ecx",5);              # convert word counter to bit counter
196         &align  (4);
197         &data_byte(0xf3,0x0f,0xa6,0xc0);# rep montmul
198
199         &mov    ("ecx","ebp");
200         &lea    ("esi",&DWP(64,"esp"));         # tp
201         # edi still points at the end of padded np copy...
202         &neg    ("ebp");
203         &lea    ("ebp",&DWP(-$pad,"edi","ebp",4));      # so just "rewind"
204         &mov    ("edi",$rp);                    # restore rp
205         &xor    ("edx","edx");                  # i=0 and clear CF
206
207 &set_label("sub",8);
208         &mov    ("eax",&DWP(0,"esi","edx",4));
209         &sbb    ("eax",&DWP(0,"ebp","edx",4));
210         &mov    (&DWP(0,"edi","edx",4),"eax");  # rp[i]=tp[i]-np[i]
211         &lea    ("edx",&DWP(1,"edx"));          # i++
212         &loop   (&label("sub"));                # doesn't affect CF!
213
214         &mov    ("eax",&DWP(0,"esi","edx",4));  # upmost overflow bit
215         &sbb    ("eax",0);
216         &and    ("esi","eax");
217         &not    ("eax");
218         &mov    ("ebp","edi");
219         &and    ("ebp","eax");
220         &or     ("esi","ebp");                  # tp=carry?tp:rp
221
222         &mov    ("ecx","edx");                  # num
223         &xor    ("edx","edx");                  # i=0
224
225 &set_label("copy",8);
226         &mov    ("eax",&DWP(0,"esi","edx",4));
227         &mov    (&DWP(64,"esp","edx",4),"ecx"); # zap tp
228         &mov    (&DWP(0,"edi","edx",4),"eax");
229         &lea    ("edx",&DWP(1,"edx"));          # i++
230         &loop   (&label("copy"));
231
232         &mov    ("ebp",$sp);
233         &xor    ("eax","eax");
234
235         &mov    ("ecx",64/4);
236         &mov    ("edi","esp");          # zap frame including scratch area
237         &data_byte(0xf3,0xab);          # rep stosl, bzero
238
239         # zap copies of ap, bp and np
240         &lea    ("edi",&DWP(64+$pad,"esp","edx",4));# pointer to ap
241         &lea    ("ecx",&DWP(3*$pad/4,"edx","edx",2));
242         &data_byte(0xf3,0xab);          # rep stosl, bzero
243
244         &mov    ("esp","ebp");
245         &inc    ("eax");                # signal "done"
246         &popf   ();
247 &set_label("leave");
248 &function_end($func);
249
250 &asciz("Padlock Montgomery Multiplication, CRYPTOGAMS by <appro\@openssl.org>");
251
252 &asm_finish();
253
254 close STDOUT;