Reserve for assembler implementation of RC4_set_key and implement x86 one.
[openssl.git] / crypto / rc4 / asm / rc4-586.pl
1 #!/usr/local/bin/perl
2
3 # At some point it became apparent that the original SSLeay RC4
4 # assembler implementation performs suboptimaly on latest IA-32
5 # microarchitectures. After re-tuning performance has changed as
6 # following:
7 #
8 # Pentium       +0%
9 # Pentium III   +17%
10 # AMD           +52%(*)
11 # P4            +180%(**)
12 #
13 # (*)   This number is actually a trade-off:-) It's possible to
14 #       achieve +72%, but at the cost of -48% off PIII performance.
15 #       In other words code performing further 13% faster on AMD
16 #       would perform almost 2 times slower on Intel PIII...
17 #       For reference! This code delivers ~80% of rc4-amd64.pl
18 #       performance on the same Opteron machine.
19 # (**)  This number requires compressed key schedule set up by
20 #       RC4_set_key and therefore doesn't apply to 0.9.7 [option for
21 #       compressed key schedule is implemented in 0.9.8 and later,
22 #       see commentary section in rc4_skey.c for further details].
23 #
24 #                                       <appro@fy.chalmers.se>
25
26 push(@INC,"perlasm","../../perlasm");
27 require "x86asm.pl";
28
29 &asm_init($ARGV[0],"rc4-586.pl");
30
31 $x="eax";
32 $y="ebx";
33 $tx="ecx";
34 $ty="edx";
35 $in="esi";
36 $out="edi";
37 $d="ebp";
38
39 sub RC4_loop
40         {
41         local($n,$p,$char)=@_;
42
43         &comment("Round $n");
44
45         if ($char)
46                 {
47                 if ($p >= 0)
48                         {
49                          &mov($ty,      &swtmp(2));
50                         &cmp($ty,       $in);
51                          &jbe(&label("finished"));
52                         &inc($in);
53                         }
54                 else
55                         {
56                         &add($ty,       8);
57                          &inc($in);
58                         &cmp($ty,       $in);
59                          &jb(&label("finished"));
60                         &mov(&swtmp(2), $ty);
61                         }
62                 }
63         # Moved out
64         # &mov( $tx,            &DWP(0,$d,$x,4)) if $p < 0;
65
66         &add(   &LB($y),        &LB($tx));
67         &mov(   $ty,            &DWP(0,$d,$y,4));
68          # XXX
69         &mov(   &DWP(0,$d,$x,4),$ty);
70          &add(  $ty,            $tx);
71         &mov(   &DWP(0,$d,$y,4),$tx);
72          &and(  $ty,            0xff);
73          &inc(  &LB($x));                       # NEXT ROUND
74         &mov(   $tx,            &DWP(0,$d,$x,4)) if $p < 1; # NEXT ROUND
75          &mov(  $ty,            &DWP(0,$d,$ty,4));
76
77         if (!$char)
78                 {
79                 #moved up into last round
80                 if ($p >= 1)
81                         {
82                         &add(   $out,   8)
83                         }
84                 &movb(  &BP($n,"esp","",0),     &LB($ty));
85                 }
86         else
87                 {
88                 # Note in+=8 has occured
89                 &movb(  &HB($ty),       &BP(-1,$in,"",0));
90                  # XXX
91                 &xorb(&LB($ty),         &HB($ty));
92                  # XXX
93                 &movb(&BP($n,$out,"",0),&LB($ty));
94                 }
95         }
96
97
98 &function_begin_B("RC4");
99         {
100         local($name)=@_;
101
102
103         &mov($ty,&wparam(1));           # len
104         &cmp($ty,0);
105         &jne(&label("proceed"));
106         &ret();
107         &set_label("proceed");
108
109         &comment("");
110
111         &push("ebp");
112          &push("ebx");
113         &push("esi");
114          &xor(  $x,     $x);            # avoid partial register stalls
115         &push("edi");
116          &xor(  $y,     $y);            # avoid partial register stalls
117         &mov(   $d,     &wparam(0));    # key
118          &mov(  $in,    &wparam(2));
119
120         &movb(  &LB($x),        &BP(0,$d,"",1));
121          &movb( &LB($y),        &BP(4,$d,"",1));
122
123         &mov(   $out,   &wparam(3));
124          &inc(  &LB($x));
125
126         &stack_push(3); # 3 temp variables
127          &add(  $d,     8);
128
129         # detect compressed schedule, see commentary section in rc4_skey.c...
130         # in 0.9.7 context ~50 bytes below RC4_CHAR label remain redundant,
131         # as compressed key schedule is set up in 0.9.8 and later.
132         &cmp(&DWP(256,$d),-1);
133         &je(&label("RC4_CHAR"));
134
135          &lea(  $ty,    &DWP(-8,$ty,$in));
136
137         # check for 0 length input
138
139          &mov(  &swtmp(2),      $ty);   # this is now address to exit at
140         &mov(   $tx,    &DWP(0,$d,$x,4));
141
142          &cmp(  $ty,    $in);
143         &jb(    &label("end")); # less than 8 bytes
144
145         &set_label("start");
146
147         # filling DELAY SLOT
148         &add(   $in,    8);
149
150         &RC4_loop(0,-1,0);
151         &RC4_loop(1,0,0);
152         &RC4_loop(2,0,0);
153         &RC4_loop(3,0,0);
154         &RC4_loop(4,0,0);
155         &RC4_loop(5,0,0);
156         &RC4_loop(6,0,0);
157         &RC4_loop(7,1,0);
158         
159         &comment("apply the cipher text");
160         # xor the cipher data with input
161
162         #&add(  $out,   8); #moved up into last round
163
164         &mov(   $tx,    &swtmp(0));
165          &mov(  $ty,    &DWP(-8,$in,"",0));
166         &xor(   $tx,    $ty);
167          &mov(  $ty,    &DWP(-4,$in,"",0)); 
168         &mov(   &DWP(-8,$out,"",0),     $tx);
169          &mov(  $tx,    &swtmp(1));
170         &xor(   $tx,    $ty);
171          &mov(  $ty,    &swtmp(2));     # load end ptr;
172         &mov(   &DWP(-4,$out,"",0),     $tx);
173          &mov(  $tx,            &DWP(0,$d,$x,4));
174         &cmp($in,       $ty);
175          &jbe(&label("start"));
176
177         &set_label("end");
178
179         # There is quite a bit of extra crap in RC4_loop() for this
180         # first round
181         &RC4_loop(0,-1,1);
182         &RC4_loop(1,0,1);
183         &RC4_loop(2,0,1);
184         &RC4_loop(3,0,1);
185         &RC4_loop(4,0,1);
186         &RC4_loop(5,0,1);
187         &RC4_loop(6,1,1);
188
189         &jmp(&label("finished"));
190
191         &align(16);
192         # this is essentially Intel P4 specific codepath, see rc4_skey.c,
193         # and is engaged in 0.9.8 and later context...
194         &set_label("RC4_CHAR");
195
196         &lea    ($ty,&DWP(0,$in,$ty));
197         &mov    (&swtmp(2),$ty);
198         &movz   ($tx,&BP(0,$d,$x));
199
200         # strangely enough unrolled loop performs over 20% slower...
201         &set_label("RC4_CHAR_loop");
202                 &add    (&LB($y),&LB($tx));
203                 &movz   ($ty,&BP(0,$d,$y));
204                 &movb   (&BP(0,$d,$y),&LB($tx));
205                 &movb   (&BP(0,$d,$x),&LB($ty));
206                 &add    (&LB($ty),&LB($tx));
207                 &movz   ($ty,&BP(0,$d,$ty));
208                 &add    (&LB($x),1);
209                 &xorb   (&LB($ty),&BP(0,$in));
210                 &lea    ($in,&BP(1,$in));
211                 &movz   ($tx,&BP(0,$d,$x));
212                 &cmp    ($in,&swtmp(2));
213                 &movb   (&BP(0,$out),&LB($ty));
214                 &lea    ($out,&BP(1,$out));
215         &jb     (&label("RC4_CHAR_loop"));
216
217         &set_label("finished");
218         &dec(   $x);
219          &stack_pop(3);
220         &movb(  &BP(-4,$d,"",0),&LB($y));
221          &movb( &BP(-8,$d,"",0),&LB($x));
222 }
223 &function_end("RC4");
224
225 ########################################################################
226
227 $inp="esi";
228 $out="edi";
229 $idi="ebp";
230 $ido="ecx";
231 $idx="edx";
232
233 &external_label("OPENSSL_ia32cap_P");
234
235 # void RC4_set_key(RC4_KEY *key,int len,const unsigned char *data);
236 &function_begin("RC4_set_key");
237         &mov    ($out,&wparam(0));              # load key
238         &mov    ($idi,&wparam(1));              # load len
239         &mov    ($inp,&wparam(2));              # load data
240         &picmeup($idx,"OPENSSL_ia32cap_P");
241
242         &lea    ($out,&DWP(2*4,$out));          # &key->data
243         &lea    ($inp,&DWP(0,$inp,$idi));       # $inp to point at the end
244         &neg    ($idi);
245         &xor    ("eax","eax");
246         &mov    (&DWP(-4,$out),$idi);           # borrow key->y
247
248         &bt     (&DWP(0,$idx),20);              # check for bit#20
249         &jc     (&label("c1stloop"));
250
251 &set_label("w1stloop",16);
252         &mov    (&DWP(0,$out,"eax",4),"eax");   # key->data[i]=i;
253         &add    (&LB("eax"),1);                 # i++;
254         &jnc    (&label("w1stloop"));
255
256         &xor    ($ido,$ido);
257         &xor    ($idx,$idx);
258
259 &set_label("w2ndloop",16);
260         &mov    ("eax",&DWP(0,$out,$ido,4));
261         &add    (&LB($idx),&BP(0,$inp,$idi));
262         &add    (&LB($idx),&LB("eax"));
263         &add    ($idi,1);
264         &mov    ("ebx",&DWP(0,$out,$idx,4));
265         &jnz    (&label("wnowrap"));
266           &mov  ($idi,&DWP(-4,$out));
267         &set_label("wnowrap");
268         &mov    (&DWP(0,$out,$idx,4),"eax");
269         &mov    (&DWP(0,$out,$ido,4),"ebx");
270         &add    (&LB($ido),1);
271         &jnc    (&label("w2ndloop"));
272 &jmp    (&label("exit"));
273
274 &set_label("c1stloop",16);
275         &mov    (&BP(0,$out,"eax"),&LB("eax")); # key->data[i]=i;
276         &add    (&LB("eax"),1);                 # i++;
277         &jnc    (&label("c1stloop"));
278
279         &xor    ($ido,$ido);
280         &xor    ($idx,$idx);
281         &xor    ("ebx","ebx");
282
283 &set_label("c2ndloop",16);
284         &mov    (&LB("eax"),&BP(0,$out,$ido));
285         &add    (&LB($idx),&BP(0,$inp,$idi));
286         &add    (&LB($idx),&LB("eax"));
287         &add    ($idi,1);
288         &mov    (&LB("ebx"),&BP(0,$out,$idx));
289         &jnz    (&label("cnowrap"));
290           &mov  ($idi,&DWP(-4,$out));
291         &set_label("cnowrap");
292         &mov    (&BP(0,$out,$idx),&LB("eax"));
293         &mov    (&BP(0,$out,$ido),&LB("ebx"));
294         &add    (&LB($ido),1);
295         &jnc    (&label("c2ndloop"));
296
297         &mov    (&DWP(256,$out),-1);            # mark schedule as compressed
298
299 &set_label("exit");
300         &xor    ("eax","eax");
301         &mov    (&DWP(-8,$out),"eax");          # key->x=0;
302         &mov    (&DWP(-4,$out),"eax");          # key->y=0;
303 &function_end("RC4_set_key");
304
305 # const char *RC4_options(void);
306 &function_begin_B("RC4_options");
307         &call   (&label("pic_point"));
308 &set_label("pic_point");
309         &blindpop("eax");
310         &lea    ("eax",&DWP(&label("opts")."-".&label("pic_point"),"eax"));
311         &picmeup("edx","OPENSSL_ia32cap_P");
312         &bt     (&DWP(0,"edx"),20);
313         &jnc    (&label("skip"));
314           &add  ("eax",12);
315         &set_label("skip");
316         &ret    ();
317 &set_label("opts",64);
318 &asciz  ("rc4(8x,int)");
319 &asciz  ("rc4(1x,char)");
320 &asciz  ("RC4 for x86, OpenSSL project");       # RC4_version
321 &align  (64);
322 &function_end_B("RC4_options");
323
324 &asm_finish();
325