Home | History | Annotate | Download | only in asm
      1 #!/usr/bin/env perl
      2 
      3 # ====================================================================
      4 # Written by Andy Polyakov <appro (at] fy.chalmers.se> for the OpenSSL
      5 # project. The module is, however, dual licensed under OpenSSL and
      6 # CRYPTOGAMS licenses depending on where you obtain it. For further
      7 # details see http://www.openssl.org/~appro/cryptogams/.
      8 # ====================================================================
      9 
     10 # October 2005
     11 #
     12 # This is a "teaser" code, as it can be improved in several ways...
     13 # First of all non-SSE2 path should be implemented (yes, for now it
     14 # performs Montgomery multiplication/convolution only on SSE2-capable
     15 # CPUs such as P4, others fall down to original code). Then inner loop
     16 # can be unrolled and modulo-scheduled to improve ILP and possibly
     17 # moved to 128-bit XMM register bank (though it would require input
     18 # rearrangement and/or increase bus bandwidth utilization). Dedicated
     19 # squaring procedure should give further performance improvement...
     20 # Yet, for being draft, the code improves rsa512 *sign* benchmark by
     21 # 110%(!), rsa1024 one - by 70% and rsa4096 - by 20%:-)
     22 
     23 # December 2006
     24 #
     25 # Modulo-scheduling SSE2 loops results in further 15-20% improvement.
     26 # Integer-only code [being equipped with dedicated squaring procedure]
     27 # gives ~40% on rsa512 sign benchmark...
     28 
     29 $0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
     30 push(@INC,"${dir}","${dir}../../../perlasm");
     31 require "x86asm.pl";
     32 
     33 $output = pop;
     34 open STDOUT,">$output";
     35 
     36 &asm_init($ARGV[0]);
     37 
     38 $sse2=0;
     39 for (@ARGV) { $sse2=1 if (/-DOPENSSL_IA32_SSE2/); }
     40 
     41 &external_label("OPENSSL_ia32cap_P") if ($sse2);
     42 
     43 &function_begin("bn_mul_mont");
     44 
     45 $i="edx";
     46 $j="ecx";
     47 $ap="esi";	$tp="esi";		# overlapping variables!!!
     48 $rp="edi";	$bp="edi";		# overlapping variables!!!
     49 $np="ebp";
     50 $num="ebx";
     51 
     52 $_num=&DWP(4*0,"esp");			# stack top layout
     53 $_rp=&DWP(4*1,"esp");
     54 $_ap=&DWP(4*2,"esp");
     55 $_bp=&DWP(4*3,"esp");
     56 $_np=&DWP(4*4,"esp");
     57 $_n0=&DWP(4*5,"esp");	$_n0q=&QWP(4*5,"esp");
     58 $_sp=&DWP(4*6,"esp");
     59 $_bpend=&DWP(4*7,"esp");
     60 $frame=32;				# size of above frame rounded up to 16n
     61 
     62 	&xor	("eax","eax");
     63 	&mov	("edi",&wparam(5));	# int num
     64 	&cmp	("edi",4);
     65 	&jl	(&label("just_leave"));
     66 
     67 	&lea	("esi",&wparam(0));	# put aside pointer to argument block
     68 	&lea	("edx",&wparam(1));	# load ap
     69 	&add	("edi",2);		# extra two words on top of tp
     70 	&neg	("edi");
     71 	&lea	("ebp",&DWP(-$frame,"esp","edi",4));	# future alloca($frame+4*(num+2))
     72 	&neg	("edi");
     73 
     74 	# minimize cache contention by arraning 2K window between stack
     75 	# pointer and ap argument [np is also position sensitive vector,
     76 	# but it's assumed to be near ap, as it's allocated at ~same
     77 	# time].
     78 	&mov	("eax","ebp");
     79 	&sub	("eax","edx");
     80 	&and	("eax",2047);
     81 	&sub	("ebp","eax");		# this aligns sp and ap modulo 2048
     82 
     83 	&xor	("edx","ebp");
     84 	&and	("edx",2048);
     85 	&xor	("edx",2048);
     86 	&sub	("ebp","edx");		# this splits them apart modulo 4096
     87 
     88 	&and	("ebp",-64);		# align to cache line
     89 
     90 	# An OS-agnostic version of __chkstk.
     91 	#
     92 	# Some OSes (Windows) insist on stack being "wired" to
     93 	# physical memory in strictly sequential manner, i.e. if stack
     94 	# allocation spans two pages, then reference to farmost one can
     95 	# be punishable by SEGV. But page walking can do good even on
     96 	# other OSes, because it guarantees that villain thread hits
     97 	# the guard page before it can make damage to innocent one...
     98 	&mov	("eax","esp");
     99 	&sub	("eax","ebp");
    100 	&and	("eax",-4096);
    101 	&mov	("edx","esp");		# saved stack pointer!
    102 	&lea	("esp",&DWP(0,"ebp","eax"));
    103 	&mov	("eax",&DWP(0,"esp"));
    104 	&cmp	("esp","ebp");
    105 	&ja	(&label("page_walk"));
    106 	&jmp	(&label("page_walk_done"));
    107 
    108 &set_label("page_walk",16);
    109 	&lea	("esp",&DWP(-4096,"esp"));
    110 	&mov	("eax",&DWP(0,"esp"));
    111 	&cmp	("esp","ebp");
    112 	&ja	(&label("page_walk"));
    113 &set_label("page_walk_done");
    114 
    115 	################################# load argument block...
    116 	&mov	("eax",&DWP(0*4,"esi"));# BN_ULONG *rp
    117 	&mov	("ebx",&DWP(1*4,"esi"));# const BN_ULONG *ap
    118 	&mov	("ecx",&DWP(2*4,"esi"));# const BN_ULONG *bp
    119 	&mov	("ebp",&DWP(3*4,"esi"));# const BN_ULONG *np
    120 	&mov	("esi",&DWP(4*4,"esi"));# const BN_ULONG *n0
    121 	#&mov	("edi",&DWP(5*4,"esi"));# int num
    122 
    123 	&mov	("esi",&DWP(0,"esi"));	# pull n0[0]
    124 	&mov	($_rp,"eax");		# ... save a copy of argument block
    125 	&mov	($_ap,"ebx");
    126 	&mov	($_bp,"ecx");
    127 	&mov	($_np,"ebp");
    128 	&mov	($_n0,"esi");
    129 	&lea	($num,&DWP(-3,"edi"));	# num=num-1 to assist modulo-scheduling
    130 	#&mov	($_num,$num);		# redundant as $num is not reused
    131 	&mov	($_sp,"edx");		# saved stack pointer!
    132 
    134 if($sse2) {
    135 $acc0="mm0";	# mmx register bank layout
    136 $acc1="mm1";
    137 $car0="mm2";
    138 $car1="mm3";
    139 $mul0="mm4";
    140 $mul1="mm5";
    141 $temp="mm6";
    142 $mask="mm7";
    143 
    144 	&picmeup("eax","OPENSSL_ia32cap_P");
    145 	&bt	(&DWP(0,"eax"),26);
    146 	&jnc	(&label("non_sse2"));
    147 
    148 	&mov	("eax",-1);
    149 	&movd	($mask,"eax");		# mask 32 lower bits
    150 
    151 	&mov	($ap,$_ap);		# load input pointers
    152 	&mov	($bp,$_bp);
    153 	&mov	($np,$_np);
    154 
    155 	&xor	($i,$i);		# i=0
    156 	&xor	($j,$j);		# j=0
    157 
    158 	&movd	($mul0,&DWP(0,$bp));		# bp[0]
    159 	&movd	($mul1,&DWP(0,$ap));		# ap[0]
    160 	&movd	($car1,&DWP(0,$np));		# np[0]
    161 
    162 	&pmuludq($mul1,$mul0);			# ap[0]*bp[0]
    163 	&movq	($car0,$mul1);
    164 	&movq	($acc0,$mul1);			# I wish movd worked for
    165 	&pand	($acc0,$mask);			# inter-register transfers
    166 
    167 	&pmuludq($mul1,$_n0q);			# *=n0
    168 
    169 	&pmuludq($car1,$mul1);			# "t[0]"*np[0]*n0
    170 	&paddq	($car1,$acc0);
    171 
    172 	&movd	($acc1,&DWP(4,$np));		# np[1]
    173 	&movd	($acc0,&DWP(4,$ap));		# ap[1]
    174 
    175 	&psrlq	($car0,32);
    176 	&psrlq	($car1,32);
    177 
    178 	&inc	($j);				# j++
    179 &set_label("1st",16);
    180 	&pmuludq($acc0,$mul0);			# ap[j]*bp[0]
    181 	&pmuludq($acc1,$mul1);			# np[j]*m1
    182 	&paddq	($car0,$acc0);			# +=c0
    183 	&paddq	($car1,$acc1);			# +=c1
    184 
    185 	&movq	($acc0,$car0);
    186 	&pand	($acc0,$mask);
    187 	&movd	($acc1,&DWP(4,$np,$j,4));	# np[j+1]
    188 	&paddq	($car1,$acc0);			# +=ap[j]*bp[0];
    189 	&movd	($acc0,&DWP(4,$ap,$j,4));	# ap[j+1]
    190 	&psrlq	($car0,32);
    191 	&movd	(&DWP($frame-4,"esp",$j,4),$car1);	# tp[j-1]=
    192 	&psrlq	($car1,32);
    193 
    194 	&lea	($j,&DWP(1,$j));
    195 	&cmp	($j,$num);
    196 	&jl	(&label("1st"));
    197 
    198 	&pmuludq($acc0,$mul0);			# ap[num-1]*bp[0]
    199 	&pmuludq($acc1,$mul1);			# np[num-1]*m1
    200 	&paddq	($car0,$acc0);			# +=c0
    201 	&paddq	($car1,$acc1);			# +=c1
    202 
    203 	&movq	($acc0,$car0);
    204 	&pand	($acc0,$mask);
    205 	&paddq	($car1,$acc0);			# +=ap[num-1]*bp[0];
    206 	&movd	(&DWP($frame-4,"esp",$j,4),$car1);	# tp[num-2]=
    207 
    208 	&psrlq	($car0,32);
    209 	&psrlq	($car1,32);
    210 
    211 	&paddq	($car1,$car0);
    212 	&movq	(&QWP($frame,"esp",$num,4),$car1);	# tp[num].tp[num-1]
    213 
    215 	&inc	($i);				# i++
    216 &set_label("outer");
    217 	&xor	($j,$j);			# j=0
    218 
    219 	&movd	($mul0,&DWP(0,$bp,$i,4));	# bp[i]
    220 	&movd	($mul1,&DWP(0,$ap));		# ap[0]
    221 	&movd	($temp,&DWP($frame,"esp"));	# tp[0]
    222 	&movd	($car1,&DWP(0,$np));		# np[0]
    223 	&pmuludq($mul1,$mul0);			# ap[0]*bp[i]
    224 
    225 	&paddq	($mul1,$temp);			# +=tp[0]
    226 	&movq	($acc0,$mul1);
    227 	&movq	($car0,$mul1);
    228 	&pand	($acc0,$mask);
    229 
    230 	&pmuludq($mul1,$_n0q);			# *=n0
    231 
    232 	&pmuludq($car1,$mul1);
    233 	&paddq	($car1,$acc0);
    234 
    235 	&movd	($temp,&DWP($frame+4,"esp"));	# tp[1]
    236 	&movd	($acc1,&DWP(4,$np));		# np[1]
    237 	&movd	($acc0,&DWP(4,$ap));		# ap[1]
    238 
    239 	&psrlq	($car0,32);
    240 	&psrlq	($car1,32);
    241 	&paddq	($car0,$temp);			# +=tp[1]
    242 
    243 	&inc	($j);				# j++
    244 	&dec	($num);
    245 &set_label("inner");
    246 	&pmuludq($acc0,$mul0);			# ap[j]*bp[i]
    247 	&pmuludq($acc1,$mul1);			# np[j]*m1
    248 	&paddq	($car0,$acc0);			# +=c0
    249 	&paddq	($car1,$acc1);			# +=c1
    250 
    251 	&movq	($acc0,$car0);
    252 	&movd	($temp,&DWP($frame+4,"esp",$j,4));# tp[j+1]
    253 	&pand	($acc0,$mask);
    254 	&movd	($acc1,&DWP(4,$np,$j,4));	# np[j+1]
    255 	&paddq	($car1,$acc0);			# +=ap[j]*bp[i]+tp[j]
    256 	&movd	($acc0,&DWP(4,$ap,$j,4));	# ap[j+1]
    257 	&psrlq	($car0,32);
    258 	&movd	(&DWP($frame-4,"esp",$j,4),$car1);# tp[j-1]=
    259 	&psrlq	($car1,32);
    260 	&paddq	($car0,$temp);			# +=tp[j+1]
    261 
    262 	&dec	($num);
    263 	&lea	($j,&DWP(1,$j));		# j++
    264 	&jnz	(&label("inner"));
    265 
    266 	&mov	($num,$j);
    267 	&pmuludq($acc0,$mul0);			# ap[num-1]*bp[i]
    268 	&pmuludq($acc1,$mul1);			# np[num-1]*m1
    269 	&paddq	($car0,$acc0);			# +=c0
    270 	&paddq	($car1,$acc1);			# +=c1
    271 
    272 	&movq	($acc0,$car0);
    273 	&pand	($acc0,$mask);
    274 	&paddq	($car1,$acc0);			# +=ap[num-1]*bp[i]+tp[num-1]
    275 	&movd	(&DWP($frame-4,"esp",$j,4),$car1);	# tp[num-2]=
    276 	&psrlq	($car0,32);
    277 	&psrlq	($car1,32);
    278 
    279 	&movd	($temp,&DWP($frame+4,"esp",$num,4));	# += tp[num]
    280 	&paddq	($car1,$car0);
    281 	&paddq	($car1,$temp);
    282 	&movq	(&QWP($frame,"esp",$num,4),$car1);	# tp[num].tp[num-1]
    283 
    284 	&lea	($i,&DWP(1,$i));		# i++
    285 	&cmp	($i,$num);
    286 	&jle	(&label("outer"));
    287 
    288 	&emms	();				# done with mmx bank
    289 	&jmp	(&label("common_tail"));
    290 
    291 &set_label("non_sse2",16);
    292 }
    293 
    295 if (0) {
    296 	&mov	("esp",$_sp);
    297 	&xor	("eax","eax");	# signal "not fast enough [yet]"
    298 	&jmp	(&label("just_leave"));
    299 	# While the below code provides competitive performance for
    300 	# all key lengths on modern Intel cores, it's still more
    301 	# than 10% slower for 4096-bit key elsewhere:-( "Competitive"
    302 	# means compared to the original integer-only assembler.
    303 	# 512-bit RSA sign is better by ~40%, but that's about all
    304 	# one can say about all CPUs...
    305 } else {
    306 $inp="esi";	# integer path uses these registers differently
    307 $word="edi";
    308 $carry="ebp";
    309 
    310 	&mov	($inp,$_ap);
    311 	&lea	($carry,&DWP(1,$num));
    312 	&mov	($word,$_bp);
    313 	&xor	($j,$j);				# j=0
    314 	&mov	("edx",$inp);
    315 	&and	($carry,1);				# see if num is even
    316 	&sub	("edx",$word);				# see if ap==bp
    317 	&lea	("eax",&DWP(4,$word,$num,4));		# &bp[num]
    318 	&or	($carry,"edx");
    319 	&mov	($word,&DWP(0,$word));			# bp[0]
    320 	&jz	(&label("bn_sqr_mont"));
    321 	&mov	($_bpend,"eax");
    322 	&mov	("eax",&DWP(0,$inp));
    323 	&xor	("edx","edx");
    324 
    325 &set_label("mull",16);
    326 	&mov	($carry,"edx");
    327 	&mul	($word);				# ap[j]*bp[0]
    328 	&add	($carry,"eax");
    329 	&lea	($j,&DWP(1,$j));
    330 	&adc	("edx",0);
    331 	&mov	("eax",&DWP(0,$inp,$j,4));		# ap[j+1]
    332 	&cmp	($j,$num);
    333 	&mov	(&DWP($frame-4,"esp",$j,4),$carry);	# tp[j]=
    334 	&jl	(&label("mull"));
    335 
    336 	&mov	($carry,"edx");
    337 	&mul	($word);				# ap[num-1]*bp[0]
    338 	 &mov	($word,$_n0);
    339 	&add	("eax",$carry);
    340 	 &mov	($inp,$_np);
    341 	&adc	("edx",0);
    342 	 &imul	($word,&DWP($frame,"esp"));		# n0*tp[0]
    343 
    344 	&mov	(&DWP($frame,"esp",$num,4),"eax");	# tp[num-1]=
    345 	&xor	($j,$j);
    346 	&mov	(&DWP($frame+4,"esp",$num,4),"edx");	# tp[num]=
    347 	&mov	(&DWP($frame+8,"esp",$num,4),$j);	# tp[num+1]=
    348 
    349 	&mov	("eax",&DWP(0,$inp));			# np[0]
    350 	&mul	($word);				# np[0]*m
    351 	&add	("eax",&DWP($frame,"esp"));		# +=tp[0]
    352 	&mov	("eax",&DWP(4,$inp));			# np[1]
    353 	&adc	("edx",0);
    354 	&inc	($j);
    355 
    356 	&jmp	(&label("2ndmadd"));
    357 
    360 &set_label("1stmadd",16);
    361 	&mov	($carry,"edx");
    362 	&mul	($word);				# ap[j]*bp[i]
    363 	&add	($carry,&DWP($frame,"esp",$j,4));	# +=tp[j]
    364 	&lea	($j,&DWP(1,$j));
    365 	&adc	("edx",0);
    366 	&add	($carry,"eax");
    367 	&mov	("eax",&DWP(0,$inp,$j,4));		# ap[j+1]
    368 	&adc	("edx",0);
    369 	&cmp	($j,$num);
    370 	&mov	(&DWP($frame-4,"esp",$j,4),$carry);	# tp[j]=
    371 	&jl	(&label("1stmadd"));
    372 
    373 	&mov	($carry,"edx");
    374 	&mul	($word);				# ap[num-1]*bp[i]
    375 	&add	("eax",&DWP($frame,"esp",$num,4));	# +=tp[num-1]
    376 	 &mov	($word,$_n0);
    377 	&adc	("edx",0);
    378 	 &mov	($inp,$_np);
    379 	&add	($carry,"eax");
    380 	&adc	("edx",0);
    381 	 &imul	($word,&DWP($frame,"esp"));		# n0*tp[0]
    382 
    383 	&xor	($j,$j);
    384 	&add	("edx",&DWP($frame+4,"esp",$num,4));	# carry+=tp[num]
    385 	&mov	(&DWP($frame,"esp",$num,4),$carry);	# tp[num-1]=
    386 	&adc	($j,0);
    387 	 &mov	("eax",&DWP(0,$inp));			# np[0]
    388 	&mov	(&DWP($frame+4,"esp",$num,4),"edx");	# tp[num]=
    389 	&mov	(&DWP($frame+8,"esp",$num,4),$j);	# tp[num+1]=
    390 
    391 	&mul	($word);				# np[0]*m
    392 	&add	("eax",&DWP($frame,"esp"));		# +=tp[0]
    393 	&mov	("eax",&DWP(4,$inp));			# np[1]
    394 	&adc	("edx",0);
    395 	&mov	($j,1);
    396 
    398 &set_label("2ndmadd",16);
    399 	&mov	($carry,"edx");
    400 	&mul	($word);				# np[j]*m
    401 	&add	($carry,&DWP($frame,"esp",$j,4));	# +=tp[j]
    402 	&lea	($j,&DWP(1,$j));
    403 	&adc	("edx",0);
    404 	&add	($carry,"eax");
    405 	&mov	("eax",&DWP(0,$inp,$j,4));		# np[j+1]
    406 	&adc	("edx",0);
    407 	&cmp	($j,$num);
    408 	&mov	(&DWP($frame-8,"esp",$j,4),$carry);	# tp[j-1]=
    409 	&jl	(&label("2ndmadd"));
    410 
    411 	&mov	($carry,"edx");
    412 	&mul	($word);				# np[j]*m
    413 	&add	($carry,&DWP($frame,"esp",$num,4));	# +=tp[num-1]
    414 	&adc	("edx",0);
    415 	&add	($carry,"eax");
    416 	&adc	("edx",0);
    417 	&mov	(&DWP($frame-4,"esp",$num,4),$carry);	# tp[num-2]=
    418 
    419 	&xor	("eax","eax");
    420 	 &mov	($j,$_bp);				# &bp[i]
    421 	&add	("edx",&DWP($frame+4,"esp",$num,4));	# carry+=tp[num]
    422 	&adc	("eax",&DWP($frame+8,"esp",$num,4));	# +=tp[num+1]
    423 	 &lea	($j,&DWP(4,$j));
    424 	&mov	(&DWP($frame,"esp",$num,4),"edx");	# tp[num-1]=
    425 	 &cmp	($j,$_bpend);
    426 	&mov	(&DWP($frame+4,"esp",$num,4),"eax");	# tp[num]=
    427 	&je	(&label("common_tail"));
    428 
    429 	&mov	($word,&DWP(0,$j));			# bp[i+1]
    430 	&mov	($inp,$_ap);
    431 	&mov	($_bp,$j);				# &bp[++i]
    432 	&xor	($j,$j);
    433 	&xor	("edx","edx");
    434 	&mov	("eax",&DWP(0,$inp));
    435 	&jmp	(&label("1stmadd"));
    436 
    438 &set_label("bn_sqr_mont",16);
    439 $sbit=$num;
    440 	&mov	($_num,$num);
    441 	&mov	($_bp,$j);				# i=0
    442 
    443 	&mov	("eax",$word);				# ap[0]
    444 	&mul	($word);				# ap[0]*ap[0]
    445 	&mov	(&DWP($frame,"esp"),"eax");		# tp[0]=
    446 	&mov	($sbit,"edx");
    447 	&shr	("edx",1);
    448 	&and	($sbit,1);
    449 	&inc	($j);
    450 &set_label("sqr",16);
    451 	&mov	("eax",&DWP(0,$inp,$j,4));		# ap[j]
    452 	&mov	($carry,"edx");
    453 	&mul	($word);				# ap[j]*ap[0]
    454 	&add	("eax",$carry);
    455 	&lea	($j,&DWP(1,$j));
    456 	&adc	("edx",0);
    457 	&lea	($carry,&DWP(0,$sbit,"eax",2));
    458 	&shr	("eax",31);
    459 	&cmp	($j,$_num);
    460 	&mov	($sbit,"eax");
    461 	&mov	(&DWP($frame-4,"esp",$j,4),$carry);	# tp[j]=
    462 	&jl	(&label("sqr"));
    463 
    464 	&mov	("eax",&DWP(0,$inp,$j,4));		# ap[num-1]
    465 	&mov	($carry,"edx");
    466 	&mul	($word);				# ap[num-1]*ap[0]
    467 	&add	("eax",$carry);
    468 	 &mov	($word,$_n0);
    469 	&adc	("edx",0);
    470 	 &mov	($inp,$_np);
    471 	&lea	($carry,&DWP(0,$sbit,"eax",2));
    472 	 &imul	($word,&DWP($frame,"esp"));		# n0*tp[0]
    473 	&shr	("eax",31);
    474 	&mov	(&DWP($frame,"esp",$j,4),$carry);	# tp[num-1]=
    475 
    476 	&lea	($carry,&DWP(0,"eax","edx",2));
    477 	 &mov	("eax",&DWP(0,$inp));			# np[0]
    478 	&shr	("edx",31);
    479 	&mov	(&DWP($frame+4,"esp",$j,4),$carry);	# tp[num]=
    480 	&mov	(&DWP($frame+8,"esp",$j,4),"edx");	# tp[num+1]=
    481 
    482 	&mul	($word);				# np[0]*m
    483 	&add	("eax",&DWP($frame,"esp"));		# +=tp[0]
    484 	&mov	($num,$j);
    485 	&adc	("edx",0);
    486 	&mov	("eax",&DWP(4,$inp));			# np[1]
    487 	&mov	($j,1);
    488 
    491 &set_label("3rdmadd",16);
    492 	&mov	($carry,"edx");
    493 	&mul	($word);				# np[j]*m
    494 	&add	($carry,&DWP($frame,"esp",$j,4));	# +=tp[j]
    495 	&adc	("edx",0);
    496 	&add	($carry,"eax");
    497 	&mov	("eax",&DWP(4,$inp,$j,4));		# np[j+1]
    498 	&adc	("edx",0);
    499 	&mov	(&DWP($frame-4,"esp",$j,4),$carry);	# tp[j-1]=
    500 
    501 	&mov	($carry,"edx");
    502 	&mul	($word);				# np[j+1]*m
    503 	&add	($carry,&DWP($frame+4,"esp",$j,4));	# +=tp[j+1]
    504 	&lea	($j,&DWP(2,$j));
    505 	&adc	("edx",0);
    506 	&add	($carry,"eax");
    507 	&mov	("eax",&DWP(0,$inp,$j,4));		# np[j+2]
    508 	&adc	("edx",0);
    509 	&cmp	($j,$num);
    510 	&mov	(&DWP($frame-8,"esp",$j,4),$carry);	# tp[j]=
    511 	&jl	(&label("3rdmadd"));
    512 
    513 	&mov	($carry,"edx");
    514 	&mul	($word);				# np[j]*m
    515 	&add	($carry,&DWP($frame,"esp",$num,4));	# +=tp[num-1]
    516 	&adc	("edx",0);
    517 	&add	($carry,"eax");
    518 	&adc	("edx",0);
    519 	&mov	(&DWP($frame-4,"esp",$num,4),$carry);	# tp[num-2]=
    520 
    521 	&mov	($j,$_bp);				# i
    522 	&xor	("eax","eax");
    523 	&mov	($inp,$_ap);
    524 	&add	("edx",&DWP($frame+4,"esp",$num,4));	# carry+=tp[num]
    525 	&adc	("eax",&DWP($frame+8,"esp",$num,4));	# +=tp[num+1]
    526 	&mov	(&DWP($frame,"esp",$num,4),"edx");	# tp[num-1]=
    527 	&cmp	($j,$num);
    528 	&mov	(&DWP($frame+4,"esp",$num,4),"eax");	# tp[num]=
    529 	&je	(&label("common_tail"));
    530 
    532 	&mov	($word,&DWP(4,$inp,$j,4));		# ap[i]
    533 	&lea	($j,&DWP(1,$j));
    534 	&mov	("eax",$word);
    535 	&mov	($_bp,$j);				# ++i
    536 	&mul	($word);				# ap[i]*ap[i]
    537 	&add	("eax",&DWP($frame,"esp",$j,4));	# +=tp[i]
    538 	&adc	("edx",0);
    539 	&mov	(&DWP($frame,"esp",$j,4),"eax");	# tp[i]=
    540 	&xor	($carry,$carry);
    541 	&cmp	($j,$num);
    542 	&lea	($j,&DWP(1,$j));
    543 	&je	(&label("sqrlast"));
    544 
    545 	&mov	($sbit,"edx");				# zaps $num
    546 	&shr	("edx",1);
    547 	&and	($sbit,1);
    548 &set_label("sqradd",16);
    549 	&mov	("eax",&DWP(0,$inp,$j,4));		# ap[j]
    550 	&mov	($carry,"edx");
    551 	&mul	($word);				# ap[j]*ap[i]
    552 	&add	("eax",$carry);
    553 	&lea	($carry,&DWP(0,"eax","eax"));
    554 	&adc	("edx",0);
    555 	&shr	("eax",31);
    556 	&add	($carry,&DWP($frame,"esp",$j,4));	# +=tp[j]
    557 	&lea	($j,&DWP(1,$j));
    558 	&adc	("eax",0);
    559 	&add	($carry,$sbit);
    560 	&adc	("eax",0);
    561 	&cmp	($j,$_num);
    562 	&mov	(&DWP($frame-4,"esp",$j,4),$carry);	# tp[j]=
    563 	&mov	($sbit,"eax");
    564 	&jle	(&label("sqradd"));
    565 
    566 	&mov	($carry,"edx");
    567 	&add	("edx","edx");
    568 	&shr	($carry,31);
    569 	&add	("edx",$sbit);
    570 	&adc	($carry,0);
    571 &set_label("sqrlast");
    572 	&mov	($word,$_n0);
    573 	&mov	($inp,$_np);
    574 	&imul	($word,&DWP($frame,"esp"));		# n0*tp[0]
    575 
    576 	&add	("edx",&DWP($frame,"esp",$j,4));	# +=tp[num]
    577 	&mov	("eax",&DWP(0,$inp));			# np[0]
    578 	&adc	($carry,0);
    579 	&mov	(&DWP($frame,"esp",$j,4),"edx");	# tp[num]=
    580 	&mov	(&DWP($frame+4,"esp",$j,4),$carry);	# tp[num+1]=
    581 
    582 	&mul	($word);				# np[0]*m
    583 	&add	("eax",&DWP($frame,"esp"));		# +=tp[0]
    584 	&lea	($num,&DWP(-1,$j));
    585 	&adc	("edx",0);
    586 	&mov	($j,1);
    587 	&mov	("eax",&DWP(4,$inp));			# np[1]
    588 
    589 	&jmp	(&label("3rdmadd"));
    590 }
    591 
    593 &set_label("common_tail",16);
    594 	&mov	($np,$_np);			# load modulus pointer
    595 	&mov	($rp,$_rp);			# load result pointer
    596 	&lea	($tp,&DWP($frame,"esp"));	# [$ap and $bp are zapped]
    597 
    598 	&mov	("eax",&DWP(0,$tp));		# tp[0]
    599 	&mov	($j,$num);			# j=num-1
    600 	&xor	($i,$i);			# i=0 and clear CF!
    601 
    602 &set_label("sub",16);
    603 	&sbb	("eax",&DWP(0,$np,$i,4));
    604 	&mov	(&DWP(0,$rp,$i,4),"eax");	# rp[i]=tp[i]-np[i]
    605 	&dec	($j);				# doesn't affect CF!
    606 	&mov	("eax",&DWP(4,$tp,$i,4));	# tp[i+1]
    607 	&lea	($i,&DWP(1,$i));		# i++
    608 	&jge	(&label("sub"));
    609 
    610 	&sbb	("eax",0);			# handle upmost overflow bit
    611 	&and	($tp,"eax");
    612 	&not	("eax");
    613 	&mov	($np,$rp);
    614 	&and	($np,"eax");
    615 	&or	($tp,$np);			# tp=carry?tp:rp
    616 
    617 &set_label("copy",16);				# copy or in-place refresh
    618 	&mov	("eax",&DWP(0,$tp,$num,4));
    619 	&mov	(&DWP(0,$rp,$num,4),"eax");	# rp[i]=tp[i]
    620 	&mov	(&DWP($frame,"esp",$num,4),$j);	# zap temporary vector
    621 	&dec	($num);
    622 	&jge	(&label("copy"));
    623 
    624 	&mov	("esp",$_sp);		# pull saved stack pointer
    625 	&mov	("eax",1);
    626 &set_label("just_leave");
    627 &function_end("bn_mul_mont");
    628 
    629 &asciz("Montgomery Multiplication for x86, CRYPTOGAMS by <appro\@openssl.org>");
    630 
    631 &asm_finish();
    632 
    633 close STDOUT;
    634