1#!/usr/bin/env perl 2# SPDX-License-Identifier: GPL-2.0 3 4# PowerPC assembler distiller by <appro>. 5 6my $flavour = shift; 7my $output = shift; 8open STDOUT,">$output" || die "can't open $output: $!"; 9 10my %GLOBALS; 11my $dotinlocallabels=($flavour=~/linux/)?1:0; 12my $elfv2abi=(($flavour =~ /linux-ppc64le/) or ($flavour =~ /linux-ppc64-elfv2/))?1:0; 13my $dotfunctions=($elfv2abi=~1)?0:1; 14 15################################################################ 16# directives which need special treatment on different platforms 17################################################################ 18my $globl = sub { 19 my $junk = shift; 20 my $name = shift; 21 my $global = \$GLOBALS{$name}; 22 my $ret; 23 24 $name =~ s|^[\.\_]||; 25 26 SWITCH: for ($flavour) { 27 /aix/ && do { $name = ".$name"; 28 last; 29 }; 30 /osx/ && do { $name = "_$name"; 31 last; 32 }; 33 /linux/ 34 && do { $ret = "_GLOBAL($name)"; 35 last; 36 }; 37 } 38 39 $ret = ".globl $name\nalign 5\n$name:" if (!$ret); 40 $$global = $name; 41 $ret; 42}; 43my $text = sub { 44 my $ret = ($flavour =~ /aix/) ? ".csect\t.text[PR],7" : ".text"; 45 $ret = ".abiversion 2\n".$ret if ($elfv2abi); 46 $ret; 47}; 48my $machine = sub { 49 my $junk = shift; 50 my $arch = shift; 51 if ($flavour =~ /osx/) 52 { $arch =~ s/\"//g; 53 $arch = ($flavour=~/64/) ? "ppc970-64" : "ppc970" if ($arch eq "any"); 54 } 55 ".machine $arch"; 56}; 57my $size = sub { 58 if ($flavour =~ /linux/) 59 { shift; 60 my $name = shift; $name =~ s|^[\.\_]||; 61 my $ret = ".size $name,.-".($dotfunctions?".":"").$name; 62 $ret .= "\n.size .$name,.-.$name" if ($dotfunctions); 63 $ret; 64 } 65 else 66 { ""; } 67}; 68my $asciz = sub { 69 shift; 70 my $line = join(",",@_); 71 if ($line =~ /^"(.*)"$/) 72 { ".byte " . join(",",unpack("C*",$1),0) . "\n.align 2"; } 73 else 74 { ""; } 75}; 76my $quad = sub { 77 shift; 78 my @ret; 79 my ($hi,$lo); 80 for (@_) { 81 if (/^0x([0-9a-f]*?)([0-9a-f]{1,8})$/io) 82 { $hi=$1?"0x$1":"0"; $lo="0x$2"; } 83 elsif (/^([0-9]+)$/o) 84 { $hi=$1>>32; $lo=$1&0xffffffff; } # error-prone with 32-bit perl 85 else 86 { $hi=undef; $lo=$_; } 87 88 if (defined($hi)) 89 { push(@ret,$flavour=~/le$/o?".long\t$lo,$hi":".long\t$hi,$lo"); } 90 else 91 { push(@ret,".quad $lo"); } 92 } 93 join("\n",@ret); 94}; 95 96################################################################ 97# simplified mnemonics not handled by at least one assembler 98################################################################ 99my $cmplw = sub { 100 my $f = shift; 101 my $cr = 0; $cr = shift if ($#_>1); 102 # Some out-of-date 32-bit GNU assembler just can't handle cmplw... 103 ($flavour =~ /linux.*32/) ? 104 " .long ".sprintf "0x%x",31<<26|$cr<<23|$_[0]<<16|$_[1]<<11|64 : 105 " cmplw ".join(',',$cr,@_); 106}; 107my $bdnz = sub { 108 my $f = shift; 109 my $bo = $f=~/[\+\-]/ ? 16+9 : 16; # optional "to be taken" hint 110 " bc $bo,0,".shift; 111} if ($flavour!~/linux/); 112my $bltlr = sub { 113 my $f = shift; 114 my $bo = $f=~/\-/ ? 12+2 : 12; # optional "not to be taken" hint 115 ($flavour =~ /linux/) ? # GNU as doesn't allow most recent hints 116 " .long ".sprintf "0x%x",19<<26|$bo<<21|16<<1 : 117 " bclr $bo,0"; 118}; 119my $bnelr = sub { 120 my $f = shift; 121 my $bo = $f=~/\-/ ? 4+2 : 4; # optional "not to be taken" hint 122 ($flavour =~ /linux/) ? # GNU as doesn't allow most recent hints 123 " .long ".sprintf "0x%x",19<<26|$bo<<21|2<<16|16<<1 : 124 " bclr $bo,2"; 125}; 126my $beqlr = sub { 127 my $f = shift; 128 my $bo = $f=~/-/ ? 12+2 : 12; # optional "not to be taken" hint 129 ($flavour =~ /linux/) ? # GNU as doesn't allow most recent hints 130 " .long ".sprintf "0x%X",19<<26|$bo<<21|2<<16|16<<1 : 131 " bclr $bo,2"; 132}; 133# GNU assembler can't handle extrdi rA,rS,16,48, or when sum of last two 134# arguments is 64, with "operand out of range" error. 135my $extrdi = sub { 136 my ($f,$ra,$rs,$n,$b) = @_; 137 $b = ($b+$n)&63; $n = 64-$n; 138 " rldicl $ra,$rs,$b,$n"; 139}; 140my $vmr = sub { 141 my ($f,$vx,$vy) = @_; 142 " vor $vx,$vy,$vy"; 143}; 144 145# Some ABIs specify vrsave, special-purpose register #256, as reserved 146# for system use. 147my $no_vrsave = ($elfv2abi); 148my $mtspr = sub { 149 my ($f,$idx,$ra) = @_; 150 if ($idx == 256 && $no_vrsave) { 151 " or $ra,$ra,$ra"; 152 } else { 153 " mtspr $idx,$ra"; 154 } 155}; 156my $mfspr = sub { 157 my ($f,$rd,$idx) = @_; 158 if ($idx == 256 && $no_vrsave) { 159 " li $rd,-1"; 160 } else { 161 " mfspr $rd,$idx"; 162 } 163}; 164 165# PowerISA 2.06 stuff 166sub vsxmem_op { 167 my ($f, $vrt, $ra, $rb, $op) = @_; 168 " .long ".sprintf "0x%X",(31<<26)|($vrt<<21)|($ra<<16)|($rb<<11)|($op*2+1); 169} 170# made-up unaligned memory reference AltiVec/VMX instructions 171my $lvx_u = sub { vsxmem_op(@_, 844); }; # lxvd2x 172my $stvx_u = sub { vsxmem_op(@_, 972); }; # stxvd2x 173my $lvdx_u = sub { vsxmem_op(@_, 588); }; # lxsdx 174my $stvdx_u = sub { vsxmem_op(@_, 716); }; # stxsdx 175my $lvx_4w = sub { vsxmem_op(@_, 780); }; # lxvw4x 176my $stvx_4w = sub { vsxmem_op(@_, 908); }; # stxvw4x 177 178# PowerISA 2.07 stuff 179sub vcrypto_op { 180 my ($f, $vrt, $vra, $vrb, $op) = @_; 181 " .long ".sprintf "0x%X",(4<<26)|($vrt<<21)|($vra<<16)|($vrb<<11)|$op; 182} 183my $vcipher = sub { vcrypto_op(@_, 1288); }; 184my $vcipherlast = sub { vcrypto_op(@_, 1289); }; 185my $vncipher = sub { vcrypto_op(@_, 1352); }; 186my $vncipherlast= sub { vcrypto_op(@_, 1353); }; 187my $vsbox = sub { vcrypto_op(@_, 0, 1480); }; 188my $vshasigmad = sub { my ($st,$six)=splice(@_,-2); vcrypto_op(@_, $st<<4|$six, 1730); }; 189my $vshasigmaw = sub { my ($st,$six)=splice(@_,-2); vcrypto_op(@_, $st<<4|$six, 1666); }; 190my $vpmsumb = sub { vcrypto_op(@_, 1032); }; 191my $vpmsumd = sub { vcrypto_op(@_, 1224); }; 192my $vpmsubh = sub { vcrypto_op(@_, 1096); }; 193my $vpmsumw = sub { vcrypto_op(@_, 1160); }; 194my $vaddudm = sub { vcrypto_op(@_, 192); }; 195my $vadduqm = sub { vcrypto_op(@_, 256); }; 196 197my $mtsle = sub { 198 my ($f, $arg) = @_; 199 " .long ".sprintf "0x%X",(31<<26)|($arg<<21)|(147*2); 200}; 201 202print "#include <asm/ppc_asm.h>\n" if $flavour =~ /linux/; 203 204while($line=<>) { 205 206 $line =~ s|[#!;].*$||; # get rid of asm-style comments... 207 $line =~ s|/\*.*\*/||; # ... and C-style comments... 208 $line =~ s|^\s+||; # ... and skip white spaces in beginning... 209 $line =~ s|\s+$||; # ... and at the end 210 211 { 212 $line =~ s|\b\.L(\w+)|L$1|g; # common denominator for Locallabel 213 $line =~ s|\bL(\w+)|\.L$1|g if ($dotinlocallabels); 214 } 215 216 { 217 $line =~ s|^\s*(\.?)(\w+)([\.\+\-]?)\s*||; 218 my $c = $1; $c = "\t" if ($c eq ""); 219 my $mnemonic = $2; 220 my $f = $3; 221 my $opcode = eval("\$$mnemonic"); 222 $line =~ s/\b(c?[rf]|v|vs)([0-9]+)\b/$2/g if ($c ne "." and $flavour !~ /osx/); 223 if (ref($opcode) eq 'CODE') { $line = &$opcode($f,split(',',$line)); } 224 elsif ($mnemonic) { $line = $c.$mnemonic.$f."\t".$line; } 225 } 226 227 print $line if ($line); 228 print "\n"; 229} 230 231close STDOUT; 232