18c2ecf20Sopenharmony_ci#!/usr/bin/env perl
28c2ecf20Sopenharmony_ci# SPDX-License-Identifier: GPL-2.0
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci# PowerPC assembler distiller by <appro>.
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_cimy $flavour = shift;
78c2ecf20Sopenharmony_cimy $output = shift;
88c2ecf20Sopenharmony_ciopen STDOUT,">$output" || die "can't open $output: $!";
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_cimy %GLOBALS;
118c2ecf20Sopenharmony_cimy $dotinlocallabels=($flavour=~/linux/)?1:0;
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci################################################################
148c2ecf20Sopenharmony_ci# directives which need special treatment on different platforms
158c2ecf20Sopenharmony_ci################################################################
168c2ecf20Sopenharmony_cimy $globl = sub {
178c2ecf20Sopenharmony_ci    my $junk = shift;
188c2ecf20Sopenharmony_ci    my $name = shift;
198c2ecf20Sopenharmony_ci    my $global = \$GLOBALS{$name};
208c2ecf20Sopenharmony_ci    my $ret;
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci    $name =~ s|^[\.\_]||;
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci    SWITCH: for ($flavour) {
258c2ecf20Sopenharmony_ci	/aix/		&& do { $name = ".$name";
268c2ecf20Sopenharmony_ci				last;
278c2ecf20Sopenharmony_ci			      };
288c2ecf20Sopenharmony_ci	/osx/		&& do { $name = "_$name";
298c2ecf20Sopenharmony_ci				last;
308c2ecf20Sopenharmony_ci			      };
318c2ecf20Sopenharmony_ci	/linux/
328c2ecf20Sopenharmony_ci			&& do {	$ret = "_GLOBAL($name)";
338c2ecf20Sopenharmony_ci				last;
348c2ecf20Sopenharmony_ci			      };
358c2ecf20Sopenharmony_ci    }
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci    $ret = ".globl	$name\nalign 5\n$name:" if (!$ret);
388c2ecf20Sopenharmony_ci    $$global = $name;
398c2ecf20Sopenharmony_ci    $ret;
408c2ecf20Sopenharmony_ci};
418c2ecf20Sopenharmony_cimy $text = sub {
428c2ecf20Sopenharmony_ci    my $ret = ($flavour =~ /aix/) ? ".csect\t.text[PR],7" : ".text";
438c2ecf20Sopenharmony_ci    $ret = ".abiversion	2\n".$ret	if ($flavour =~ /linux.*64le/);
448c2ecf20Sopenharmony_ci    $ret;
458c2ecf20Sopenharmony_ci};
468c2ecf20Sopenharmony_cimy $machine = sub {
478c2ecf20Sopenharmony_ci    my $junk = shift;
488c2ecf20Sopenharmony_ci    my $arch = shift;
498c2ecf20Sopenharmony_ci    if ($flavour =~ /osx/)
508c2ecf20Sopenharmony_ci    {	$arch =~ s/\"//g;
518c2ecf20Sopenharmony_ci	$arch = ($flavour=~/64/) ? "ppc970-64" : "ppc970" if ($arch eq "any");
528c2ecf20Sopenharmony_ci    }
538c2ecf20Sopenharmony_ci    ".machine	$arch";
548c2ecf20Sopenharmony_ci};
558c2ecf20Sopenharmony_cimy $size = sub {
568c2ecf20Sopenharmony_ci    if ($flavour =~ /linux/)
578c2ecf20Sopenharmony_ci    {	shift;
588c2ecf20Sopenharmony_ci	my $name = shift; $name =~ s|^[\.\_]||;
598c2ecf20Sopenharmony_ci	my $ret  = ".size	$name,.-".($flavour=~/64$/?".":"").$name;
608c2ecf20Sopenharmony_ci	$ret .= "\n.size	.$name,.-.$name" if ($flavour=~/64$/);
618c2ecf20Sopenharmony_ci	$ret;
628c2ecf20Sopenharmony_ci    }
638c2ecf20Sopenharmony_ci    else
648c2ecf20Sopenharmony_ci    {	"";	}
658c2ecf20Sopenharmony_ci};
668c2ecf20Sopenharmony_cimy $asciz = sub {
678c2ecf20Sopenharmony_ci    shift;
688c2ecf20Sopenharmony_ci    my $line = join(",",@_);
698c2ecf20Sopenharmony_ci    if ($line =~ /^"(.*)"$/)
708c2ecf20Sopenharmony_ci    {	".byte	" . join(",",unpack("C*",$1),0) . "\n.align	2";	}
718c2ecf20Sopenharmony_ci    else
728c2ecf20Sopenharmony_ci    {	"";	}
738c2ecf20Sopenharmony_ci};
748c2ecf20Sopenharmony_cimy $quad = sub {
758c2ecf20Sopenharmony_ci    shift;
768c2ecf20Sopenharmony_ci    my @ret;
778c2ecf20Sopenharmony_ci    my ($hi,$lo);
788c2ecf20Sopenharmony_ci    for (@_) {
798c2ecf20Sopenharmony_ci	if (/^0x([0-9a-f]*?)([0-9a-f]{1,8})$/io)
808c2ecf20Sopenharmony_ci	{  $hi=$1?"0x$1":"0"; $lo="0x$2";  }
818c2ecf20Sopenharmony_ci	elsif (/^([0-9]+)$/o)
828c2ecf20Sopenharmony_ci	{  $hi=$1>>32; $lo=$1&0xffffffff;  } # error-prone with 32-bit perl
838c2ecf20Sopenharmony_ci	else
848c2ecf20Sopenharmony_ci	{  $hi=undef; $lo=$_; }
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	if (defined($hi))
878c2ecf20Sopenharmony_ci	{  push(@ret,$flavour=~/le$/o?".long\t$lo,$hi":".long\t$hi,$lo");  }
888c2ecf20Sopenharmony_ci	else
898c2ecf20Sopenharmony_ci	{  push(@ret,".quad	$lo");  }
908c2ecf20Sopenharmony_ci    }
918c2ecf20Sopenharmony_ci    join("\n",@ret);
928c2ecf20Sopenharmony_ci};
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci################################################################
958c2ecf20Sopenharmony_ci# simplified mnemonics not handled by at least one assembler
968c2ecf20Sopenharmony_ci################################################################
978c2ecf20Sopenharmony_cimy $cmplw = sub {
988c2ecf20Sopenharmony_ci    my $f = shift;
998c2ecf20Sopenharmony_ci    my $cr = 0; $cr = shift if ($#_>1);
1008c2ecf20Sopenharmony_ci    # Some out-of-date 32-bit GNU assembler just can't handle cmplw...
1018c2ecf20Sopenharmony_ci    ($flavour =~ /linux.*32/) ?
1028c2ecf20Sopenharmony_ci	"	.long	".sprintf "0x%x",31<<26|$cr<<23|$_[0]<<16|$_[1]<<11|64 :
1038c2ecf20Sopenharmony_ci	"	cmplw	".join(',',$cr,@_);
1048c2ecf20Sopenharmony_ci};
1058c2ecf20Sopenharmony_cimy $bdnz = sub {
1068c2ecf20Sopenharmony_ci    my $f = shift;
1078c2ecf20Sopenharmony_ci    my $bo = $f=~/[\+\-]/ ? 16+9 : 16;	# optional "to be taken" hint
1088c2ecf20Sopenharmony_ci    "	bc	$bo,0,".shift;
1098c2ecf20Sopenharmony_ci} if ($flavour!~/linux/);
1108c2ecf20Sopenharmony_cimy $bltlr = sub {
1118c2ecf20Sopenharmony_ci    my $f = shift;
1128c2ecf20Sopenharmony_ci    my $bo = $f=~/\-/ ? 12+2 : 12;	# optional "not to be taken" hint
1138c2ecf20Sopenharmony_ci    ($flavour =~ /linux/) ?		# GNU as doesn't allow most recent hints
1148c2ecf20Sopenharmony_ci	"	.long	".sprintf "0x%x",19<<26|$bo<<21|16<<1 :
1158c2ecf20Sopenharmony_ci	"	bclr	$bo,0";
1168c2ecf20Sopenharmony_ci};
1178c2ecf20Sopenharmony_cimy $bnelr = sub {
1188c2ecf20Sopenharmony_ci    my $f = shift;
1198c2ecf20Sopenharmony_ci    my $bo = $f=~/\-/ ? 4+2 : 4;	# optional "not to be taken" hint
1208c2ecf20Sopenharmony_ci    ($flavour =~ /linux/) ?		# GNU as doesn't allow most recent hints
1218c2ecf20Sopenharmony_ci	"	.long	".sprintf "0x%x",19<<26|$bo<<21|2<<16|16<<1 :
1228c2ecf20Sopenharmony_ci	"	bclr	$bo,2";
1238c2ecf20Sopenharmony_ci};
1248c2ecf20Sopenharmony_cimy $beqlr = sub {
1258c2ecf20Sopenharmony_ci    my $f = shift;
1268c2ecf20Sopenharmony_ci    my $bo = $f=~/-/ ? 12+2 : 12;	# optional "not to be taken" hint
1278c2ecf20Sopenharmony_ci    ($flavour =~ /linux/) ?		# GNU as doesn't allow most recent hints
1288c2ecf20Sopenharmony_ci	"	.long	".sprintf "0x%X",19<<26|$bo<<21|2<<16|16<<1 :
1298c2ecf20Sopenharmony_ci	"	bclr	$bo,2";
1308c2ecf20Sopenharmony_ci};
1318c2ecf20Sopenharmony_ci# GNU assembler can't handle extrdi rA,rS,16,48, or when sum of last two
1328c2ecf20Sopenharmony_ci# arguments is 64, with "operand out of range" error.
1338c2ecf20Sopenharmony_cimy $extrdi = sub {
1348c2ecf20Sopenharmony_ci    my ($f,$ra,$rs,$n,$b) = @_;
1358c2ecf20Sopenharmony_ci    $b = ($b+$n)&63; $n = 64-$n;
1368c2ecf20Sopenharmony_ci    "	rldicl	$ra,$rs,$b,$n";
1378c2ecf20Sopenharmony_ci};
1388c2ecf20Sopenharmony_cimy $vmr = sub {
1398c2ecf20Sopenharmony_ci    my ($f,$vx,$vy) = @_;
1408c2ecf20Sopenharmony_ci    "	vor	$vx,$vy,$vy";
1418c2ecf20Sopenharmony_ci};
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci# Some ABIs specify vrsave, special-purpose register #256, as reserved
1448c2ecf20Sopenharmony_ci# for system use.
1458c2ecf20Sopenharmony_cimy $no_vrsave = ($flavour =~ /linux-ppc64le/);
1468c2ecf20Sopenharmony_cimy $mtspr = sub {
1478c2ecf20Sopenharmony_ci    my ($f,$idx,$ra) = @_;
1488c2ecf20Sopenharmony_ci    if ($idx == 256 && $no_vrsave) {
1498c2ecf20Sopenharmony_ci	"	or	$ra,$ra,$ra";
1508c2ecf20Sopenharmony_ci    } else {
1518c2ecf20Sopenharmony_ci	"	mtspr	$idx,$ra";
1528c2ecf20Sopenharmony_ci    }
1538c2ecf20Sopenharmony_ci};
1548c2ecf20Sopenharmony_cimy $mfspr = sub {
1558c2ecf20Sopenharmony_ci    my ($f,$rd,$idx) = @_;
1568c2ecf20Sopenharmony_ci    if ($idx == 256 && $no_vrsave) {
1578c2ecf20Sopenharmony_ci	"	li	$rd,-1";
1588c2ecf20Sopenharmony_ci    } else {
1598c2ecf20Sopenharmony_ci	"	mfspr	$rd,$idx";
1608c2ecf20Sopenharmony_ci    }
1618c2ecf20Sopenharmony_ci};
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci# PowerISA 2.06 stuff
1648c2ecf20Sopenharmony_cisub vsxmem_op {
1658c2ecf20Sopenharmony_ci    my ($f, $vrt, $ra, $rb, $op) = @_;
1668c2ecf20Sopenharmony_ci    "	.long	".sprintf "0x%X",(31<<26)|($vrt<<21)|($ra<<16)|($rb<<11)|($op*2+1);
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci# made-up unaligned memory reference AltiVec/VMX instructions
1698c2ecf20Sopenharmony_cimy $lvx_u	= sub {	vsxmem_op(@_, 844); };	# lxvd2x
1708c2ecf20Sopenharmony_cimy $stvx_u	= sub {	vsxmem_op(@_, 972); };	# stxvd2x
1718c2ecf20Sopenharmony_cimy $lvdx_u	= sub {	vsxmem_op(@_, 588); };	# lxsdx
1728c2ecf20Sopenharmony_cimy $stvdx_u	= sub {	vsxmem_op(@_, 716); };	# stxsdx
1738c2ecf20Sopenharmony_cimy $lvx_4w	= sub { vsxmem_op(@_, 780); };	# lxvw4x
1748c2ecf20Sopenharmony_cimy $stvx_4w	= sub { vsxmem_op(@_, 908); };	# stxvw4x
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci# PowerISA 2.07 stuff
1778c2ecf20Sopenharmony_cisub vcrypto_op {
1788c2ecf20Sopenharmony_ci    my ($f, $vrt, $vra, $vrb, $op) = @_;
1798c2ecf20Sopenharmony_ci    "	.long	".sprintf "0x%X",(4<<26)|($vrt<<21)|($vra<<16)|($vrb<<11)|$op;
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_cimy $vcipher	= sub { vcrypto_op(@_, 1288); };
1828c2ecf20Sopenharmony_cimy $vcipherlast	= sub { vcrypto_op(@_, 1289); };
1838c2ecf20Sopenharmony_cimy $vncipher	= sub { vcrypto_op(@_, 1352); };
1848c2ecf20Sopenharmony_cimy $vncipherlast= sub { vcrypto_op(@_, 1353); };
1858c2ecf20Sopenharmony_cimy $vsbox	= sub { vcrypto_op(@_, 0, 1480); };
1868c2ecf20Sopenharmony_cimy $vshasigmad	= sub { my ($st,$six)=splice(@_,-2); vcrypto_op(@_, $st<<4|$six, 1730); };
1878c2ecf20Sopenharmony_cimy $vshasigmaw	= sub { my ($st,$six)=splice(@_,-2); vcrypto_op(@_, $st<<4|$six, 1666); };
1888c2ecf20Sopenharmony_cimy $vpmsumb	= sub { vcrypto_op(@_, 1032); };
1898c2ecf20Sopenharmony_cimy $vpmsumd	= sub { vcrypto_op(@_, 1224); };
1908c2ecf20Sopenharmony_cimy $vpmsubh	= sub { vcrypto_op(@_, 1096); };
1918c2ecf20Sopenharmony_cimy $vpmsumw	= sub { vcrypto_op(@_, 1160); };
1928c2ecf20Sopenharmony_cimy $vaddudm	= sub { vcrypto_op(@_, 192);  };
1938c2ecf20Sopenharmony_cimy $vadduqm	= sub { vcrypto_op(@_, 256);  };
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_cimy $mtsle	= sub {
1968c2ecf20Sopenharmony_ci    my ($f, $arg) = @_;
1978c2ecf20Sopenharmony_ci    "	.long	".sprintf "0x%X",(31<<26)|($arg<<21)|(147*2);
1988c2ecf20Sopenharmony_ci};
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ciprint "#include <asm/ppc_asm.h>\n" if $flavour =~ /linux/;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ciwhile($line=<>) {
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci    $line =~ s|[#!;].*$||;	# get rid of asm-style comments...
2058c2ecf20Sopenharmony_ci    $line =~ s|/\*.*\*/||;	# ... and C-style comments...
2068c2ecf20Sopenharmony_ci    $line =~ s|^\s+||;		# ... and skip white spaces in beginning...
2078c2ecf20Sopenharmony_ci    $line =~ s|\s+$||;		# ... and at the end
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci    {
2108c2ecf20Sopenharmony_ci	$line =~ s|\b\.L(\w+)|L$1|g;	# common denominator for Locallabel
2118c2ecf20Sopenharmony_ci	$line =~ s|\bL(\w+)|\.L$1|g	if ($dotinlocallabels);
2128c2ecf20Sopenharmony_ci    }
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci    {
2158c2ecf20Sopenharmony_ci	$line =~ s|^\s*(\.?)(\w+)([\.\+\-]?)\s*||;
2168c2ecf20Sopenharmony_ci	my $c = $1; $c = "\t" if ($c eq "");
2178c2ecf20Sopenharmony_ci	my $mnemonic = $2;
2188c2ecf20Sopenharmony_ci	my $f = $3;
2198c2ecf20Sopenharmony_ci	my $opcode = eval("\$$mnemonic");
2208c2ecf20Sopenharmony_ci	$line =~ s/\b(c?[rf]|v|vs)([0-9]+)\b/$2/g if ($c ne "." and $flavour !~ /osx/);
2218c2ecf20Sopenharmony_ci	if (ref($opcode) eq 'CODE') { $line = &$opcode($f,split(',',$line)); }
2228c2ecf20Sopenharmony_ci	elsif ($mnemonic)           { $line = $c.$mnemonic.$f."\t".$line; }
2238c2ecf20Sopenharmony_ci    }
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci    print $line if ($line);
2268c2ecf20Sopenharmony_ci    print "\n";
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ciclose STDOUT;
230