xref: /aosp_15_r20/external/boringssl/src/crypto/fipsmodule/bn/asm/x86-mont.pl (revision 8fb009dc861624b67b6cdb62ea21f0f22d0c584b)
1#! /usr/bin/env perl
2# Copyright 2005-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 <[email protected]> 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# October 2005
18#
19# This is a "teaser" code, as it can be improved in several ways...
20# First of all non-SSE2 path should be implemented (yes, for now it
21# performs Montgomery multiplication/convolution only on SSE2-capable
22# CPUs such as P4, others fall down to original code). Then inner loop
23# can be unrolled and modulo-scheduled to improve ILP and possibly
24# moved to 128-bit XMM register bank (though it would require input
25# rearrangement and/or increase bus bandwidth utilization). Dedicated
26# squaring procedure should give further performance improvement...
27# Yet, for being draft, the code improves rsa512 *sign* benchmark by
28# 110%(!), rsa1024 one - by 70% and rsa4096 - by 20%:-)
29
30# December 2006
31#
32# Modulo-scheduling SSE2 loops results in further 15-20% improvement.
33# Integer-only code [being equipped with dedicated squaring procedure]
34# gives ~40% on rsa512 sign benchmark...
35
36$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
37push(@INC,"${dir}","${dir}../../../perlasm");
38require "x86asm.pl";
39
40$output = pop;
41open STDOUT,">$output";
42
43&asm_init($ARGV[0]);
44
45$sse2=1;
46
47&function_begin("bn_mul_mont");
48
49$i="edx";
50$j="ecx";
51$ap="esi";	$tp="esi";		# overlapping variables!!!
52$rp="edi";	$bp="edi";		# overlapping variables!!!
53$np="ebp";
54$num="ebx";
55
56$_num=&DWP(4*0,"esp");			# stack top layout
57$_rp=&DWP(4*1,"esp");
58$_ap=&DWP(4*2,"esp");
59$_bp=&DWP(4*3,"esp");
60$_np=&DWP(4*4,"esp");
61$_n0=&DWP(4*5,"esp");	$_n0q=&QWP(4*5,"esp");
62$_sp=&DWP(4*6,"esp");
63$_bpend=&DWP(4*7,"esp");
64$frame=32;				# size of above frame rounded up to 16n
65
66	&xor	("eax","eax");
67	&mov	("edi",&wparam(5));	# int num
68	&cmp	("edi",4);
69	&jl	(&label("just_leave"));
70
71	&lea	("esi",&wparam(0));	# put aside pointer to argument block
72	&lea	("edx",&wparam(1));	# load ap
73	&add	("edi",2);		# extra two words on top of tp
74	&neg	("edi");
75	&lea	("ebp",&DWP(-$frame,"esp","edi",4));	# future alloca($frame+4*(num+2))
76	&neg	("edi");
77
78	# minimize cache contention by arranging 2K window between stack
79	# pointer and ap argument [np is also position sensitive vector,
80	# but it's assumed to be near ap, as it's allocated at ~same
81	# time].
82	&mov	("eax","ebp");
83	&sub	("eax","edx");
84	&and	("eax",2047);
85	&sub	("ebp","eax");		# this aligns sp and ap modulo 2048
86
87	&xor	("edx","ebp");
88	&and	("edx",2048);
89	&xor	("edx",2048);
90	&sub	("ebp","edx");		# this splits them apart modulo 4096
91
92	&and	("ebp",-64);		# align to cache line
93
94	# An OS-agnostic version of __chkstk.
95	#
96	# Some OSes (Windows) insist on stack being "wired" to
97	# physical memory in strictly sequential manner, i.e. if stack
98	# allocation spans two pages, then reference to farmost one can
99	# be punishable by SEGV. But page walking can do good even on
100	# other OSes, because it guarantees that villain thread hits
101	# the guard page before it can make damage to innocent one...
102	&mov	("eax","esp");
103	&sub	("eax","ebp");
104	&and	("eax",-4096);
105	&mov	("edx","esp");		# saved stack pointer!
106	&lea	("esp",&DWP(0,"ebp","eax"));
107	&mov	("eax",&DWP(0,"esp"));
108	&cmp	("esp","ebp");
109	&ja	(&label("page_walk"));
110	&jmp	(&label("page_walk_done"));
111
112&set_label("page_walk",16);
113	&lea	("esp",&DWP(-4096,"esp"));
114	&mov	("eax",&DWP(0,"esp"));
115	&cmp	("esp","ebp");
116	&ja	(&label("page_walk"));
117&set_label("page_walk_done");
118
119	################################# load argument block...
120	&mov	("eax",&DWP(0*4,"esi"));# BN_ULONG *rp
121	&mov	("ebx",&DWP(1*4,"esi"));# const BN_ULONG *ap
122	&mov	("ecx",&DWP(2*4,"esi"));# const BN_ULONG *bp
123	&mov	("ebp",&DWP(3*4,"esi"));# const BN_ULONG *np
124	&mov	("esi",&DWP(4*4,"esi"));# const BN_ULONG *n0
125	#&mov	("edi",&DWP(5*4,"esi"));# int num
126
127	&mov	("esi",&DWP(0,"esi"));	# pull n0[0]
128	&mov	($_rp,"eax");		# ... save a copy of argument block
129	&mov	($_ap,"ebx");
130	&mov	($_bp,"ecx");
131	&mov	($_np,"ebp");
132	&mov	($_n0,"esi");
133	&lea	($num,&DWP(-3,"edi"));	# num=num-1 to assist modulo-scheduling
134	#&mov	($_num,$num);		# redundant as $num is not reused
135	&mov	($_sp,"edx");		# saved stack pointer!
136
137if($sse2) {
138$acc0="mm0";	# mmx register bank layout
139$acc1="mm1";
140$car0="mm2";
141$car1="mm3";
142$mul0="mm4";
143$mul1="mm5";
144$temp="mm6";
145$mask="mm7";
146
147	&mov	("eax",-1);
148	&movd	($mask,"eax");		# mask 32 lower bits
149
150	&mov	($ap,$_ap);		# load input pointers
151	&mov	($bp,$_bp);
152	&mov	($np,$_np);
153
154	&xor	($i,$i);		# i=0
155	&xor	($j,$j);		# j=0
156
157	&movd	($mul0,&DWP(0,$bp));		# bp[0]
158	&movd	($mul1,&DWP(0,$ap));		# ap[0]
159	&movd	($car1,&DWP(0,$np));		# np[0]
160
161	&pmuludq($mul1,$mul0);			# ap[0]*bp[0]
162	&movq	($car0,$mul1);
163	&movq	($acc0,$mul1);			# I wish movd worked for
164	&pand	($acc0,$mask);			# inter-register transfers
165
166	&pmuludq($mul1,$_n0q);			# *=n0
167
168	&pmuludq($car1,$mul1);			# "t[0]"*np[0]*n0
169	&paddq	($car1,$acc0);
170
171	&movd	($acc1,&DWP(4,$np));		# np[1]
172	&movd	($acc0,&DWP(4,$ap));		# ap[1]
173
174	&psrlq	($car0,32);
175	&psrlq	($car1,32);
176
177	&inc	($j);				# j++
178&set_label("1st",16);
179	&pmuludq($acc0,$mul0);			# ap[j]*bp[0]
180	&pmuludq($acc1,$mul1);			# np[j]*m1
181	&paddq	($car0,$acc0);			# +=c0
182	&paddq	($car1,$acc1);			# +=c1
183
184	&movq	($acc0,$car0);
185	&pand	($acc0,$mask);
186	&movd	($acc1,&DWP(4,$np,$j,4));	# np[j+1]
187	&paddq	($car1,$acc0);			# +=ap[j]*bp[0];
188	&movd	($acc0,&DWP(4,$ap,$j,4));	# ap[j+1]
189	&psrlq	($car0,32);
190	&movd	(&DWP($frame-4,"esp",$j,4),$car1);	# tp[j-1]=
191	&psrlq	($car1,32);
192
193	&lea	($j,&DWP(1,$j));
194	&cmp	($j,$num);
195	&jl	(&label("1st"));
196
197	&pmuludq($acc0,$mul0);			# ap[num-1]*bp[0]
198	&pmuludq($acc1,$mul1);			# np[num-1]*m1
199	&paddq	($car0,$acc0);			# +=c0
200	&paddq	($car1,$acc1);			# +=c1
201
202	&movq	($acc0,$car0);
203	&pand	($acc0,$mask);
204	&paddq	($car1,$acc0);			# +=ap[num-1]*bp[0];
205	&movd	(&DWP($frame-4,"esp",$j,4),$car1);	# tp[num-2]=
206
207	&psrlq	($car0,32);
208	&psrlq	($car1,32);
209
210	&paddq	($car1,$car0);
211	&movq	(&QWP($frame,"esp",$num,4),$car1);	# tp[num].tp[num-1]
212
213	&inc	($i);				# i++
214&set_label("outer");
215	&xor	($j,$j);			# j=0
216
217	&movd	($mul0,&DWP(0,$bp,$i,4));	# bp[i]
218	&movd	($mul1,&DWP(0,$ap));		# ap[0]
219	&movd	($temp,&DWP($frame,"esp"));	# tp[0]
220	&movd	($car1,&DWP(0,$np));		# np[0]
221	&pmuludq($mul1,$mul0);			# ap[0]*bp[i]
222
223	&paddq	($mul1,$temp);			# +=tp[0]
224	&movq	($acc0,$mul1);
225	&movq	($car0,$mul1);
226	&pand	($acc0,$mask);
227
228	&pmuludq($mul1,$_n0q);			# *=n0
229
230	&pmuludq($car1,$mul1);
231	&paddq	($car1,$acc0);
232
233	&movd	($temp,&DWP($frame+4,"esp"));	# tp[1]
234	&movd	($acc1,&DWP(4,$np));		# np[1]
235	&movd	($acc0,&DWP(4,$ap));		# ap[1]
236
237	&psrlq	($car0,32);
238	&psrlq	($car1,32);
239	&paddq	($car0,$temp);			# +=tp[1]
240
241	&inc	($j);				# j++
242	&dec	($num);
243&set_label("inner");
244	&pmuludq($acc0,$mul0);			# ap[j]*bp[i]
245	&pmuludq($acc1,$mul1);			# np[j]*m1
246	&paddq	($car0,$acc0);			# +=c0
247	&paddq	($car1,$acc1);			# +=c1
248
249	&movq	($acc0,$car0);
250	&movd	($temp,&DWP($frame+4,"esp",$j,4));# tp[j+1]
251	&pand	($acc0,$mask);
252	&movd	($acc1,&DWP(4,$np,$j,4));	# np[j+1]
253	&paddq	($car1,$acc0);			# +=ap[j]*bp[i]+tp[j]
254	&movd	($acc0,&DWP(4,$ap,$j,4));	# ap[j+1]
255	&psrlq	($car0,32);
256	&movd	(&DWP($frame-4,"esp",$j,4),$car1);# tp[j-1]=
257	&psrlq	($car1,32);
258	&paddq	($car0,$temp);			# +=tp[j+1]
259
260	&dec	($num);
261	&lea	($j,&DWP(1,$j));		# j++
262	&jnz	(&label("inner"));
263
264	&mov	($num,$j);
265	&pmuludq($acc0,$mul0);			# ap[num-1]*bp[i]
266	&pmuludq($acc1,$mul1);			# np[num-1]*m1
267	&paddq	($car0,$acc0);			# +=c0
268	&paddq	($car1,$acc1);			# +=c1
269
270	&movq	($acc0,$car0);
271	&pand	($acc0,$mask);
272	&paddq	($car1,$acc0);			# +=ap[num-1]*bp[i]+tp[num-1]
273	&movd	(&DWP($frame-4,"esp",$j,4),$car1);	# tp[num-2]=
274	&psrlq	($car0,32);
275	&psrlq	($car1,32);
276
277	&movd	($temp,&DWP($frame+4,"esp",$num,4));	# += tp[num]
278	&paddq	($car1,$car0);
279	&paddq	($car1,$temp);
280	&movq	(&QWP($frame,"esp",$num,4),$car1);	# tp[num].tp[num-1]
281
282	&lea	($i,&DWP(1,$i));		# i++
283	&cmp	($i,$num);
284	&jle	(&label("outer"));
285
286	&emms	();				# done with mmx bank
287	&jmp	(&label("common_tail"));
288}
289
290&set_label("common_tail",16);
291	&mov	($np,$_np);			# load modulus pointer
292	&mov	($rp,$_rp);			# load result pointer
293	&lea	($tp,&DWP($frame,"esp"));	# [$ap and $bp are zapped]
294
295	&mov	("eax",&DWP(0,$tp));		# tp[0]
296	&mov	($j,$num);			# j=num-1
297	&xor	($i,$i);			# i=0 and clear CF!
298
299&set_label("sub",16);
300	&sbb	("eax",&DWP(0,$np,$i,4));
301	&mov	(&DWP(0,$rp,$i,4),"eax");	# rp[i]=tp[i]-np[i]
302	&dec	($j);				# doesn't affect CF!
303	&mov	("eax",&DWP(4,$tp,$i,4));	# tp[i+1]
304	&lea	($i,&DWP(1,$i));		# i++
305	&jge	(&label("sub"));
306
307	&sbb	("eax",0);			# handle upmost overflow bit
308	&mov	("edx",-1);
309	&xor	("edx","eax");
310	&jmp	(&label("copy"));
311
312&set_label("copy",16);				# conditional copy
313	&mov	($tp,&DWP($frame,"esp",$num,4));
314	&mov	($np,&DWP(0,$rp,$num,4));
315	&mov	(&DWP($frame,"esp",$num,4),$j);	# zap temporary vector
316	&and	($tp,"eax");
317	&and	($np,"edx");
318	&or	($np,$tp);
319	&mov	(&DWP(0,$rp,$num,4),$np);
320	&dec	($num);
321	&jge	(&label("copy"));
322
323	&mov	("esp",$_sp);		# pull saved stack pointer
324	&mov	("eax",1);
325&set_label("just_leave");
326&function_end("bn_mul_mont");
327
328&asciz("Montgomery Multiplication for x86, CRYPTOGAMS by <appro\@openssl.org>");
329
330&asm_finish();
331
332close STDOUT or die "error closing STDOUT: $!";
333