1#! /usr/bin/env perl 2# Copyright 2006-2022 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 my ($prefix, $reg) = ($in =~ m/(\D*)(\d+)/); 157 158 my $n = int($reg); 159 if ($n >= 32) { 160 $n -= 32; 161 } 162 163 return "${prefix}${n}"; 164} 165# As above for first $num register args, returns list 166sub _vsr2vr { 167 my $num = shift; 168 my @rest = @_; 169 my @subst = splice(@rest, 0, $num); 170 171 @subst = map { vsr2vr1($_); } @subst; 172 173 return (@subst, @rest); 174} 175# As above but 1st arg ($f) is extracted and reinserted after 176# processing so that it can be ignored by a code generation function 177# that consumes the result 178sub vsr2vr_args { 179 my $num = shift; 180 my $f = shift; 181 182 my @out = _vsr2vr($num, @_); 183 184 return ($f, @out); 185} 186# As above but 1st arg is mnemonic, return formatted instruction 187sub vsr2vr { 188 my $mnemonic = shift; 189 my $num = shift; 190 my $f = shift; 191 192 my @out = _vsr2vr($num, @_); 193 194 " ${mnemonic}${f} " . join(",", @out); 195} 196 197# ISA 2.03 198my $vsel = sub { vsr2vr("vsel", 4, @_); }; 199my $vsl = sub { vsr2vr("vsl", 3, @_); }; 200my $vspltisb = sub { vsr2vr("vspltisb", 1, @_); }; 201my $vspltisw = sub { vsr2vr("vspltisw", 1, @_); }; 202my $vsr = sub { vsr2vr("vsr", 3, @_); }; 203my $vsro = sub { vsr2vr("vsro", 3, @_); }; 204 205# ISA 3.0 206my $lxsd = sub { vsr2vr("lxsd", 1, @_); }; 207 208################################################################ 209# simplified mnemonics not handled by at least one assembler 210################################################################ 211my $cmplw = sub { 212 my $f = shift; 213 my $cr = 0; $cr = shift if ($#_>1); 214 # Some out-of-date 32-bit GNU assembler just can't handle cmplw... 215 ($flavour =~ /linux.*32/) ? 216 " .long ".sprintf "0x%x",31<<26|$cr<<23|$_[0]<<16|$_[1]<<11|64 : 217 " cmplw ".join(',',$cr,@_); 218}; 219my $bdnz = sub { 220 my $f = shift; 221 my $bo = $f=~/[\+\-]/ ? 16+9 : 16; # optional "to be taken" hint 222 " bc $bo,0,".shift; 223} if ($flavour!~/linux/); 224my $bltlr = sub { 225 my $f = shift; 226 my $bo = $f=~/\-/ ? 12+2 : 12; # optional "not to be taken" hint 227 ($flavour =~ /linux/) ? # GNU as doesn't allow most recent hints 228 " .long ".sprintf "0x%x",19<<26|$bo<<21|16<<1 : 229 " bclr $bo,0"; 230}; 231my $bnelr = sub { 232 my $f = shift; 233 my $bo = $f=~/\-/ ? 4+2 : 4; # optional "not to be taken" hint 234 ($flavour =~ /linux/) ? # GNU as doesn't allow most recent hints 235 " .long ".sprintf "0x%x",19<<26|$bo<<21|2<<16|16<<1 : 236 " bclr $bo,2"; 237}; 238my $beqlr = sub { 239 my $f = shift; 240 my $bo = $f=~/-/ ? 12+2 : 12; # optional "not to be taken" hint 241 ($flavour =~ /linux/) ? # GNU as doesn't allow most recent hints 242 " .long ".sprintf "0x%X",19<<26|$bo<<21|2<<16|16<<1 : 243 " bclr $bo,2"; 244}; 245# GNU assembler can't handle extrdi rA,rS,16,48, or when sum of last two 246# arguments is 64, with "operand out of range" error. 247my $extrdi = sub { 248 my ($f,$ra,$rs,$n,$b) = @_; 249 $b = ($b+$n)&63; $n = 64-$n; 250 " rldicl $ra,$rs,$b,$n"; 251}; 252my $vmr = sub { 253 my ($f,$vx,$vy) = @_; 254 " vor $vx,$vy,$vy"; 255}; 256 257# Some ABIs specify vrsave, special-purpose register #256, as reserved 258# for system use. 259my $no_vrsave = ($flavour =~ /aix|linux64(le|v2)/); 260my $mtspr = sub { 261 my ($f,$idx,$ra) = @_; 262 if ($idx == 256 && $no_vrsave) { 263 " or $ra,$ra,$ra"; 264 } else { 265 " mtspr $idx,$ra"; 266 } 267}; 268my $mfspr = sub { 269 my ($f,$rd,$idx) = @_; 270 if ($idx == 256 && $no_vrsave) { 271 " li $rd,-1"; 272 } else { 273 " mfspr $rd,$idx"; 274 } 275}; 276 277# PowerISA 2.06 stuff 278sub vsxmem_op { 279 my ($f, $vrt, $ra, $rb, $op) = @_; 280 " .long ".sprintf "0x%X",(31<<26)|($vrt<<21)|($ra<<16)|($rb<<11)|($op*2+1); 281} 282# made-up unaligned memory reference AltiVec/VMX instructions 283my $lvx_u = sub { vsxmem_op(@_, 844); }; # lxvd2x 284my $stvx_u = sub { vsxmem_op(@_, 972); }; # stxvd2x 285my $lvdx_u = sub { vsxmem_op(@_, 588); }; # lxsdx 286my $stvdx_u = sub { vsxmem_op(@_, 716); }; # stxsdx 287my $lvx_4w = sub { vsxmem_op(@_, 780); }; # lxvw4x 288my $stvx_4w = sub { vsxmem_op(@_, 908); }; # stxvw4x 289my $lvx_splt = sub { vsxmem_op(@_, 332); }; # lxvdsx 290# VSX instruction[s] masqueraded as made-up AltiVec/VMX 291my $vpermdi = sub { # xxpermdi 292 my ($f, $vrt, $vra, $vrb, $dm) = @_; 293 $dm = oct($dm) if ($dm =~ /^0/); 294 " .long ".sprintf "0x%X",(60<<26)|($vrt<<21)|($vra<<16)|($vrb<<11)|($dm<<8)|(10<<3)|7; 295}; 296my $vxxlor = sub { # xxlor 297 my ($f, $vrt, $vra, $vrb) = @_; 298 " .long ".sprintf "0x%X",(60<<26)|($vrt<<21)|($vra<<16)|($vrb<<11)|(146<<3)|6; 299}; 300my $vxxlorc = sub { # xxlor 301 my ($f, $vrt, $vra, $vrb) = @_; 302 " .long ".sprintf "0x%X",(60<<26)|($vrt<<21)|($vra<<16)|($vrb<<11)|(146<<3)|1; 303}; 304 305# PowerISA 2.07 stuff 306sub vcrypto_op { 307 my ($f, $vrt, $vra, $vrb, $op) = vsr2vr_args(3, @_); 308 " .long ".sprintf "0x%X",(4<<26)|($vrt<<21)|($vra<<16)|($vrb<<11)|$op; 309} 310sub vfour { 311 my ($f, $vrt, $vra, $vrb, $vrc, $op) = @_; 312 " .long ".sprintf "0x%X",(4<<26)|($vrt<<21)|($vra<<16)|($vrb<<11)|($vrc<<6)|$op; 313}; 314sub vfour_vsr { 315 my ($f, $vrt, $vra, $vrb, $vrc, $op) = vsr2vr_args(4, @_); 316 " .long ".sprintf "0x%X",(4<<26)|($vrt<<21)|($vra<<16)|($vrb<<11)|($vrc<<6)|$op; 317}; 318 319my $vcipher = sub { vcrypto_op(@_, 1288); }; 320my $vcipherlast = sub { vcrypto_op(@_, 1289); }; 321my $vncipher = sub { vcrypto_op(@_, 1352); }; 322my $vncipherlast= sub { vcrypto_op(@_, 1353); }; 323my $vsbox = sub { vcrypto_op(@_, 0, 1480); }; 324my $vshasigmad = sub { my ($st,$six)=splice(@_,-2); vcrypto_op(@_, $st<<4|$six, 1730); }; 325my $vshasigmaw = sub { my ($st,$six)=splice(@_,-2); vcrypto_op(@_, $st<<4|$six, 1666); }; 326my $vpmsumb = sub { vcrypto_op(@_, 1032); }; 327my $vpmsumd = sub { vcrypto_op(@_, 1224); }; 328my $vpmsubh = sub { vcrypto_op(@_, 1096); }; 329my $vpmsumw = sub { vcrypto_op(@_, 1160); }; 330# These are not really crypto, but vcrypto_op template works 331my $vaddudm = sub { vcrypto_op(@_, 192); }; 332my $vadduqm = sub { vcrypto_op(@_, 256); }; 333my $vmuleuw = sub { vcrypto_op(@_, 648); }; 334my $vmulouw = sub { vcrypto_op(@_, 136); }; 335my $vrld = sub { vcrypto_op(@_, 196); }; 336my $vsld = sub { vcrypto_op(@_, 1476); }; 337my $vsrd = sub { vcrypto_op(@_, 1732); }; 338my $vsubudm = sub { vcrypto_op(@_, 1216); }; 339my $vaddcuq = sub { vcrypto_op(@_, 320); }; 340my $vaddeuqm = sub { vfour_vsr(@_,60); }; 341my $vaddecuq = sub { vfour_vsr(@_,61); }; 342my $vmrgew = sub { vfour_vsr(@_,0,1932); }; 343my $vmrgow = sub { vfour_vsr(@_,0,1676); }; 344 345my $mtsle = sub { 346 my ($f, $arg) = @_; 347 " .long ".sprintf "0x%X",(31<<26)|($arg<<21)|(147*2); 348}; 349 350# VSX instructions masqueraded as AltiVec/VMX 351my $mtvrd = sub { 352 my ($f, $vrt, $ra) = @_; 353 " .long ".sprintf "0x%X",(31<<26)|($vrt<<21)|($ra<<16)|(179<<1)|1; 354}; 355my $mtvrwz = sub { 356 my ($f, $vrt, $ra) = @_; 357 " .long ".sprintf "0x%X",(31<<26)|($vrt<<21)|($ra<<16)|(243<<1)|1; 358}; 359my $lvwzx_u = sub { vsxmem_op(@_, 12); }; # lxsiwzx 360my $stvwx_u = sub { vsxmem_op(@_, 140); }; # stxsiwx 361 362# PowerISA 3.0 stuff 363my $maddhdu = sub { vfour(@_,49); }; 364my $maddld = sub { vfour(@_,51); }; 365my $darn = sub { 366 my ($f, $rt, $l) = @_; 367 " .long ".sprintf "0x%X",(31<<26)|($rt<<21)|($l<<16)|(755<<1); 368}; 369my $iseleq = sub { 370 my ($f, $rt, $ra, $rb) = @_; 371 " .long ".sprintf "0x%X",(31<<26)|($rt<<21)|($ra<<16)|($rb<<11)|(2<<6)|30; 372}; 373# VSX instruction[s] masqueraded as made-up AltiVec/VMX 374my $vspltib = sub { # xxspltib 375 my ($f, $vrt, $imm8) = @_; 376 $imm8 = oct($imm8) if ($imm8 =~ /^0/); 377 $imm8 &= 0xff; 378 " .long ".sprintf "0x%X",(60<<26)|($vrt<<21)|($imm8<<11)|(360<<1)|1; 379}; 380 381# PowerISA 3.0B stuff 382my $addex = sub { 383 my ($f, $rt, $ra, $rb, $cy) = @_; # only cy==0 is specified in 3.0B 384 " .long ".sprintf "0x%X",(31<<26)|($rt<<21)|($ra<<16)|($rb<<11)|($cy<<9)|(170<<1); 385}; 386my $vmsumudm = sub { vfour_vsr(@_, 35); }; 387 388# PowerISA 3.1 stuff 389my $brd = sub { 390 my ($f, $ra, $rs) = @_; 391 " .long ".sprintf "0x%X",(31<<26)|($rs<<21)|($ra<<16)|(187<<1); 392}; 393my $vsrq = sub { vcrypto_op(@_, 517); }; 394 395 396 397while($line=<>) { 398 399 $line =~ s|[#!;].*$||; # get rid of asm-style comments... 400 $line =~ s|/\*.*\*/||; # ... and C-style comments... 401 $line =~ s|^\s+||; # ... and skip whitespaces in beginning... 402 $line =~ s|\s+$||; # ... and at the end 403 404 { 405 $line =~ s|\.L(\w+)|L$1|g; # common denominator for Locallabel 406 $line =~ s|\bL(\w+)|\.L$1|g if ($dotinlocallabels); 407 } 408 409 { 410 $line =~ s|(^[\.\w]+)\:\s*||; 411 my $label = $1; 412 if ($label) { 413 my $xlated = ($GLOBALS{$label} or $label); 414 print "$xlated:"; 415 if ($flavour =~ /linux.*64(le|v2)/) { 416 if ($TYPES{$label} =~ /function/) { 417 printf "\n.localentry %s,0\n",$xlated; 418 } 419 } 420 } 421 } 422 423 { 424 $line =~ s|^\s*(\.?)(\w+)([\.\+\-]?)\s*||; 425 my $c = $1; $c = "\t" if ($c eq ""); 426 my $mnemonic = $2; 427 my $f = $3; 428 my $opcode = eval("\$$mnemonic"); 429 $line =~ s/\b(c?[rf]|v|vs)([0-9]+)\b/$2/g if ($c ne "." and $flavour !~ /osx/); 430 if (ref($opcode) eq 'CODE') { $line = &$opcode($f,split(/,\s*/,$line)); } 431 elsif ($mnemonic) { $line = $c.$mnemonic.$f."\t".$line; } 432 } 433 434 print $line if ($line); 435 print "\n"; 436} 437 438close STDOUT or die "error closing STDOUT: $!"; 439