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