1#! /usr/bin/env perl 2# Copyright 2006-2021 The OpenSSL Project Authors. All Rights Reserved. 3# 4# Licensed under the Apache License 2.0 (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 9my $flavour = shift; 10my $output = shift; 11open STDOUT,">$output" || die "can't open $output: $!"; 12 13my %GLOBALS; 14my %TYPES; 15my $dotinlocallabels=($flavour=~/linux/)?1:0; 16 17################################################################ 18# directives which need special treatment on different platforms 19################################################################ 20my $type = sub { 21 my ($dir,$name,$type) = @_; 22 23 $TYPES{$name} = $type; 24 if ($flavour =~ /linux/) { 25 $name =~ s|^\.||; 26 ".type $name,$type"; 27 } else { 28 ""; 29 } 30}; 31my $globl = sub { 32 my $junk = shift; 33 my $name = shift; 34 my $global = \$GLOBALS{$name}; 35 my $type = \$TYPES{$name}; 36 my $ret; 37 38 $name =~ s|^\.||; 39 40 SWITCH: for ($flavour) { 41 /aix/ && do { if (!$$type) { 42 $$type = "\@function"; 43 } 44 if ($$type =~ /function/) { 45 $name = ".$name"; 46 } 47 last; 48 }; 49 /osx/ && do { $name = "_$name"; 50 last; 51 }; 52 /linux.*(32|64(le|v2))/ 53 && do { $ret .= ".globl $name"; 54 if (!$$type) { 55 $ret .= "\n.type $name,\@function"; 56 $$type = "\@function"; 57 } 58 last; 59 }; 60 /linux.*64/ && do { $ret .= ".globl $name"; 61 if (!$$type) { 62 $ret .= "\n.type $name,\@function"; 63 $$type = "\@function"; 64 } 65 if ($$type =~ /function/) { 66 $ret .= "\n.section \".opd\",\"aw\""; 67 $ret .= "\n.align 3"; 68 $ret .= "\n$name:"; 69 $ret .= "\n.quad .$name,.TOC.\@tocbase,0"; 70 $ret .= "\n.previous"; 71 $name = ".$name"; 72 } 73 last; 74 }; 75 } 76 77 $ret = ".globl $name" if (!$ret); 78 $$global = $name; 79 $ret; 80}; 81my $text = sub { 82 my $ret = ($flavour =~ /aix/) ? ".csect\t.text[PR],7" : ".text"; 83 $ret = ".abiversion 2\n".$ret if ($flavour =~ /linux.*64(le|v2)/); 84 $ret; 85}; 86my $p2align = sub { 87 my $ret = ($flavour =~ /aix64-as/) ? "" : ".p2align $line"; 88 $ret; 89}; 90my $machine = sub { 91 my $junk = shift; 92 my $arch = shift; 93 if ($flavour =~ /osx/) 94 { $arch =~ s/\"//g; 95 $arch = ($flavour=~/64/) ? "ppc970-64" : "ppc970" if ($arch eq "any"); 96 } 97 ".machine $arch"; 98}; 99my $size = sub { 100 if ($flavour =~ /linux/) 101 { shift; 102 my $name = shift; 103 my $real = $GLOBALS{$name} ? \$GLOBALS{$name} : \$name; 104 my $ret = ".size $$real,.-$$real"; 105 $name =~ s|^\.||; 106 if ($$real ne $name) { 107 $ret .= "\n.size $name,.-$$real"; 108 } 109 $ret; 110 } 111 else 112 { ""; } 113}; 114my $asciz = sub { 115 shift; 116 my $line = join(",",@_); 117 if ($line =~ /^"(.*)"$/) 118 { ".byte " . join(",",unpack("C*",$1),0) . "\n.align 2"; } 119 else 120 { ""; } 121}; 122my $quad = sub { 123 shift; 124 my @ret; 125 my ($hi,$lo); 126 for (@_) { 127 if (/^0x([0-9a-f]*?)([0-9a-f]{1,8})$/io) 128 { $hi=$1?"0x$1":"0"; $lo="0x$2"; } 129 elsif (/^([0-9]+)$/o) 130 { $hi=$1>>32; $lo=$1&0xffffffff; } # error-prone with 32-bit perl 131 else 132 { $hi=undef; $lo=$_; } 133 134 if (defined($hi)) 135 { push(@ret,$flavour=~/le$/o?".long\t$lo,$hi":".long\t$hi,$lo"); } 136 else 137 { push(@ret,".quad $lo"); } 138 } 139 join("\n",@ret); 140}; 141 142################################################################ 143# vector register number hacking 144################################################################ 145 146# It is convenient to be able to set a variable like: 147# my $foo = "v33"; 148# and use this in different contexts where: 149# * a VSR (Vector-Scaler Register) number (i.e. "v33") is required 150# * a VR (Vector Register) number (i.e. "v1") is required 151# Map VSR numbering to VR number for certain vector instructions. 152 153# vs<N> -> v<N-32> if N > 32 154sub vsr2vr1 { 155 my $in = shift; 156 157 my $n = int($in); 158 if ($n >= 32) { 159 $n -= 32; 160 } 161 162 return "$n"; 163} 164# As above for first $num register args, returns list 165sub _vsr2vr { 166 my $num = shift; 167 my @rest = @_; 168 my @subst = splice(@rest, 0, $num); 169 170 @subst = map { vsr2vr1($_); } @subst; 171 172 return (@subst, @rest); 173} 174# As above but 1st arg ($f) is extracted and reinserted after 175# processing so that it can be ignored by a code generation function 176# that consumes the result 177sub vsr2vr_args { 178 my $num = shift; 179 my $f = shift; 180 181 my @out = _vsr2vr($num, @_); 182 183 return ($f, @out); 184} 185# As above but 1st arg is mnemonic, return formatted instruction 186sub vsr2vr { 187 my $mnemonic = shift; 188 my $num = shift; 189 my $f = shift; 190 191 my @out = _vsr2vr($num, @_); 192 193 " ${mnemonic}${f} " . join(",", @out); 194} 195 196# ISA 2.03 197my $vsel = sub { vsr2vr("vsel", 4, @_); }; 198my $vsl = sub { vsr2vr("vsl", 3, @_); }; 199my $vspltisb = sub { vsr2vr("vspltisb", 1, @_); }; 200my $vspltisw = sub { vsr2vr("vspltisw", 1, @_); }; 201my $vsr = sub { vsr2vr("vsr", 3, @_); }; 202my $vsro = sub { vsr2vr("vsro", 3, @_); }; 203 204# ISA 3.0 205my $lxsd = sub { vsr2vr("lxsd", 1, @_); }; 206 207################################################################ 208# simplified mnemonics not handled by at least one assembler 209################################################################ 210my $cmplw = sub { 211 my $f = shift; 212 my $cr = 0; $cr = shift if ($#_>1); 213 # Some out-of-date 32-bit GNU assembler just can't handle cmplw... 214 ($flavour =~ /linux.*32/) ? 215 " .long ".sprintf "0x%x",31<<26|$cr<<23|$_[0]<<16|$_[1]<<11|64 : 216 " cmplw ".join(',',$cr,@_); 217}; 218my $bdnz = sub { 219 my $f = shift; 220 my $bo = $f=~/[\+\-]/ ? 16+9 : 16; # optional "to be taken" hint 221 " bc $bo,0,".shift; 222} if ($flavour!~/linux/); 223my $bltlr = sub { 224 my $f = shift; 225 my $bo = $f=~/\-/ ? 12+2 : 12; # optional "not to be taken" hint 226 ($flavour =~ /linux/) ? # GNU as doesn't allow most recent hints 227 " .long ".sprintf "0x%x",19<<26|$bo<<21|16<<1 : 228 " bclr $bo,0"; 229}; 230my $bnelr = sub { 231 my $f = shift; 232 my $bo = $f=~/\-/ ? 4+2 : 4; # optional "not to be taken" hint 233 ($flavour =~ /linux/) ? # GNU as doesn't allow most recent hints 234 " .long ".sprintf "0x%x",19<<26|$bo<<21|2<<16|16<<1 : 235 " bclr $bo,2"; 236}; 237my $beqlr = sub { 238 my $f = shift; 239 my $bo = $f=~/-/ ? 12+2 : 12; # optional "not to be taken" hint 240 ($flavour =~ /linux/) ? # GNU as doesn't allow most recent hints 241 " .long ".sprintf "0x%X",19<<26|$bo<<21|2<<16|16<<1 : 242 " bclr $bo,2"; 243}; 244# GNU assembler can't handle extrdi rA,rS,16,48, or when sum of last two 245# arguments is 64, with "operand out of range" error. 246my $extrdi = sub { 247 my ($f,$ra,$rs,$n,$b) = @_; 248 $b = ($b+$n)&63; $n = 64-$n; 249 " rldicl $ra,$rs,$b,$n"; 250}; 251my $vmr = sub { 252 my ($f,$vx,$vy) = @_; 253 " vor $vx,$vy,$vy"; 254}; 255 256# Some ABIs specify vrsave, special-purpose register #256, as reserved 257# for system use. 258my $no_vrsave = ($flavour =~ /aix|linux64(le|v2)/); 259my $mtspr = sub { 260 my ($f,$idx,$ra) = @_; 261 if ($idx == 256 && $no_vrsave) { 262 " or $ra,$ra,$ra"; 263 } else { 264 " mtspr $idx,$ra"; 265 } 266}; 267my $mfspr = sub { 268 my ($f,$rd,$idx) = @_; 269 if ($idx == 256 && $no_vrsave) { 270 " li $rd,-1"; 271 } else { 272 " mfspr $rd,$idx"; 273 } 274}; 275 276# PowerISA 2.06 stuff 277sub vsxmem_op { 278 my ($f, $vrt, $ra, $rb, $op) = @_; 279 " .long ".sprintf "0x%X",(31<<26)|($vrt<<21)|($ra<<16)|($rb<<11)|($op*2+1); 280} 281# made-up unaligned memory reference AltiVec/VMX instructions 282my $lvx_u = sub { vsxmem_op(@_, 844); }; # lxvd2x 283my $stvx_u = sub { vsxmem_op(@_, 972); }; # stxvd2x 284my $lvdx_u = sub { vsxmem_op(@_, 588); }; # lxsdx 285my $stvdx_u = sub { vsxmem_op(@_, 716); }; # stxsdx 286my $lvx_4w = sub { vsxmem_op(@_, 780); }; # lxvw4x 287my $stvx_4w = sub { vsxmem_op(@_, 908); }; # stxvw4x 288my $lvx_splt = sub { vsxmem_op(@_, 332); }; # lxvdsx 289# VSX instruction[s] masqueraded as made-up AltiVec/VMX 290my $vpermdi = sub { # xxpermdi 291 my ($f, $vrt, $vra, $vrb, $dm) = @_; 292 $dm = oct($dm) if ($dm =~ /^0/); 293 " .long ".sprintf "0x%X",(60<<26)|($vrt<<21)|($vra<<16)|($vrb<<11)|($dm<<8)|(10<<3)|7; 294}; 295 296# PowerISA 2.07 stuff 297sub vcrypto_op { 298 my ($f, $vrt, $vra, $vrb, $op) = vsr2vr_args(3, @_); 299 " .long ".sprintf "0x%X",(4<<26)|($vrt<<21)|($vra<<16)|($vrb<<11)|$op; 300} 301sub vfour { 302 my ($f, $vrt, $vra, $vrb, $vrc, $op) = @_; 303 " .long ".sprintf "0x%X",(4<<26)|($vrt<<21)|($vra<<16)|($vrb<<11)|($vrc<<6)|$op; 304}; 305sub vfour_vsr { 306 my ($f, $vrt, $vra, $vrb, $vrc, $op) = vsr2vr_args(4, @_); 307 " .long ".sprintf "0x%X",(4<<26)|($vrt<<21)|($vra<<16)|($vrb<<11)|($vrc<<6)|$op; 308}; 309 310my $vcipher = sub { vcrypto_op(@_, 1288); }; 311my $vcipherlast = sub { vcrypto_op(@_, 1289); }; 312my $vncipher = sub { vcrypto_op(@_, 1352); }; 313my $vncipherlast= sub { vcrypto_op(@_, 1353); }; 314my $vsbox = sub { vcrypto_op(@_, 0, 1480); }; 315my $vshasigmad = sub { my ($st,$six)=splice(@_,-2); vcrypto_op(@_, $st<<4|$six, 1730); }; 316my $vshasigmaw = sub { my ($st,$six)=splice(@_,-2); vcrypto_op(@_, $st<<4|$six, 1666); }; 317my $vpmsumb = sub { vcrypto_op(@_, 1032); }; 318my $vpmsumd = sub { vcrypto_op(@_, 1224); }; 319my $vpmsubh = sub { vcrypto_op(@_, 1096); }; 320my $vpmsumw = sub { vcrypto_op(@_, 1160); }; 321# These are not really crypto, but vcrypto_op template works 322my $vaddudm = sub { vcrypto_op(@_, 192); }; 323my $vadduqm = sub { vcrypto_op(@_, 256); }; 324my $vmuleuw = sub { vcrypto_op(@_, 648); }; 325my $vmulouw = sub { vcrypto_op(@_, 136); }; 326my $vrld = sub { vcrypto_op(@_, 196); }; 327my $vsld = sub { vcrypto_op(@_, 1476); }; 328my $vsrd = sub { vcrypto_op(@_, 1732); }; 329my $vsubudm = sub { vcrypto_op(@_, 1216); }; 330my $vaddcuq = sub { vcrypto_op(@_, 320); }; 331my $vaddeuqm = sub { vfour_vsr(@_,60); }; 332my $vaddecuq = sub { vfour_vsr(@_,61); }; 333my $vmrgew = sub { vfour_vsr(@_,0,1932); }; 334my $vmrgow = sub { vfour_vsr(@_,0,1676); }; 335 336my $mtsle = sub { 337 my ($f, $arg) = @_; 338 " .long ".sprintf "0x%X",(31<<26)|($arg<<21)|(147*2); 339}; 340 341# VSX instructions masqueraded as AltiVec/VMX 342my $mtvrd = sub { 343 my ($f, $vrt, $ra) = @_; 344 " .long ".sprintf "0x%X",(31<<26)|($vrt<<21)|($ra<<16)|(179<<1)|1; 345}; 346my $mtvrwz = sub { 347 my ($f, $vrt, $ra) = @_; 348 " .long ".sprintf "0x%X",(31<<26)|($vrt<<21)|($ra<<16)|(243<<1)|1; 349}; 350my $lvwzx_u = sub { vsxmem_op(@_, 12); }; # lxsiwzx 351my $stvwx_u = sub { vsxmem_op(@_, 140); }; # stxsiwx 352 353# PowerISA 3.0 stuff 354my $maddhdu = sub { vfour(@_,49); }; 355my $maddld = sub { vfour(@_,51); }; 356my $darn = sub { 357 my ($f, $rt, $l) = @_; 358 " .long ".sprintf "0x%X",(31<<26)|($rt<<21)|($l<<16)|(755<<1); 359}; 360my $iseleq = sub { 361 my ($f, $rt, $ra, $rb) = @_; 362 " .long ".sprintf "0x%X",(31<<26)|($rt<<21)|($ra<<16)|($rb<<11)|(2<<6)|30; 363}; 364# VSX instruction[s] masqueraded as made-up AltiVec/VMX 365my $vspltib = sub { # xxspltib 366 my ($f, $vrt, $imm8) = @_; 367 $imm8 = oct($imm8) if ($imm8 =~ /^0/); 368 $imm8 &= 0xff; 369 " .long ".sprintf "0x%X",(60<<26)|($vrt<<21)|($imm8<<11)|(360<<1)|1; 370}; 371 372# PowerISA 3.0B stuff 373my $addex = sub { 374 my ($f, $rt, $ra, $rb, $cy) = @_; # only cy==0 is specified in 3.0B 375 " .long ".sprintf "0x%X",(31<<26)|($rt<<21)|($ra<<16)|($rb<<11)|($cy<<9)|(170<<1); 376}; 377my $vmsumudm = sub { vfour_vsr(@_, 35); }; 378 379while($line=<>) { 380 381 $line =~ s|[#!;].*$||; # get rid of asm-style comments... 382 $line =~ s|/\*.*\*/||; # ... and C-style comments... 383 $line =~ s|^\s+||; # ... and skip whitespaces in beginning... 384 $line =~ s|\s+$||; # ... and at the end 385 386 { 387 $line =~ s|\.L(\w+)|L$1|g; # common denominator for Locallabel 388 $line =~ s|\bL(\w+)|\.L$1|g if ($dotinlocallabels); 389 } 390 391 { 392 $line =~ s|(^[\.\w]+)\:\s*||; 393 my $label = $1; 394 if ($label) { 395 my $xlated = ($GLOBALS{$label} or $label); 396 print "$xlated:"; 397 if ($flavour =~ /linux.*64(le|v2)/) { 398 if ($TYPES{$label} =~ /function/) { 399 printf "\n.localentry %s,0\n",$xlated; 400 } 401 } 402 } 403 } 404 405 { 406 $line =~ s|^\s*(\.?)(\w+)([\.\+\-]?)\s*||; 407 my $c = $1; $c = "\t" if ($c eq ""); 408 my $mnemonic = $2; 409 my $f = $3; 410 my $opcode = eval("\$$mnemonic"); 411 $line =~ s/\b(c?[rf]|v|vs)([0-9]+)\b/$2/g if ($c ne "." and $flavour !~ /osx/); 412 if (ref($opcode) eq 'CODE') { $line = &$opcode($f,split(/,\s*/,$line)); } 413 elsif ($mnemonic) { $line = $c.$mnemonic.$f."\t".$line; } 414 } 415 416 print $line if ($line); 417 print "\n"; 418} 419 420close STDOUT or die "error closing STDOUT: $!"; 421