#!/usr/bin/perl
# Copyright (c) 2002-2009 Nokia Corporation and/or its subsidiary(-ies).
# All rights reserved.
# This component and the accompanying materials are made available
# under the terms of "Eclipse Public License v1.0"
# which accompanies this distribution, and is available
# at the URL "http://www.eclipse.org/legal/epl-v10.html".
#
# Initial Contributors:
# Nokia Corporation - initial contribution.
#
# Contributors:
#
# Description:
#
use strict;
use Getopt::Long;
use Cwd;
my $Pwd = cwd;
my $incroot = $Pwd;
if ($Pwd =~ /.\:(.*)$/) {
$incroot = $1;
$incroot =~ s/\//\\/go;
$incroot = "$incroot"."\\";
}
if ($^O == "MSWin32" ) {
my $PATHSEP='\\'
} else {
my $PATHSEP='/'
}
my $commentToken = "//";
my $emitUnimplemented = 1;
my %opts = ();
my $result = GetOptions(\%opts,
"record-emitter",
"suppress-check",
"no-original",
"output:s",
"error-string:s",
"autoflush",
"help",
"lineno",
);
Usage() if(!$result || $opts{'help'} || @ARGV < 1);
my $errorString = "\t>>> CHECK THIS <<<";
my $recordEmitter = $opts{"record-emitter"};
my $plineno = $opts{"lineno"};
my $forceCheck = !$opts{"suppress-check"};
my $printOriginal = !$opts{"no-original"};
my $outfile = $opts{"output"} if $opts{"output"};
my $infile = @ARGV[0];
$errorString = $opts{"error-string"} if $opts{"error-string"};
#my $symbolsfile = "tranasm-symbols.log";
my $symbolsfile = "";
my $savedOut;
# set to false to prevent recording files in \tranlated-files.log
#my $recordFile = 0;
#system "echo $infile >> \\translated-files.log" if $recordFile;
my @unmangledSymbols;
my $recordUnmangledSymbols = $symbolsfile;
if ($outfile) {
open OUT, ">$outfile";
$savedOut = select OUT;
}
$| = $opts{"autoflush"};
my $labelN = 0;
my $labelRoot = "Label";
my $lineno = 0;
my @knownLabels = ();
sub Croak($)
{
my ($msg) = @_;
die "\nERROR: line $.: $msg";
}
sub PrintComment($)
{
my ($comment) = @_;
printf "\t$commentToken$comment" if $comment;
}
sub PrintCheck() { printf "\t$errorString\n" if $forceCheck; }
sub Nl () { printf "\n"; }
# cache for results of unmangling....
my %unmangledSymbols = ();
# cache to say whether symbol was mangled
my %mangledSymbols = ();
my $sourcefile = "\"$infile\"";
my @IncFiles;
sub Unmangle ($)
{
my ($str) = @_;
return $str if ($str =~ /\s*__cpp\(/); # these don't need unmangling
my $res = $unmangledSymbols{$str};
if ($res) {
my $l = $lineno;
if ($mangledSymbols{$str}) {
my $sfile = $sourcefile;
$sfile =~ s/\"//go;
$sfile =~ s/\\\\/${main::PATHSEP}/go;
push @unmangledSymbols, "$incroot$sfile $l: Symbol = $str : Result = $res : Filt = \?\?\? : @IncFiles"
}
return $res;
} else {
return $unmangledSymbols{$str} = UnmangleX($str);
}
}
sub UnmangleX ($)
{
my ($str) = @_;
my $sfile = $sourcefile;
$sfile =~ s/\"//go;
$sfile =~ s/\\\\/${main::PATHSEP}/go;
# recognize non-c++ derived symbols/labels
if ($str =~ /^\s*(__.*)\s*$/) {
$str =~ s/\./_/;
return $str;
}
my $cppfilt = $ENV{CPPFILT} ? $ENV{CPPFILT} : "c++filt";
open UNM, "$cppfilt -s gnu $str|" or die "Error: Tranasm problem running $cppfilt to unmangle symbols.\n";
my $result = <UNM> ;
chop $result;
my $pat = "\^\\s*$result\\s*\$";
if ($str =~ $pat) {
return $str;
}
close UNM;
#strip of any args
if ($result =~ /([^\(]*)\s*\(/) {
my $res = $1;
if ($recordUnmangledSymbols) {
my $l = $lineno;
$mangledSymbols{$str} = 1;
push @unmangledSymbols, "$incroot$sfile $l: Symbol = $str : Result = $res : Filt = $result : @IncFiles";
}
$result = $res;
} else {
# didn't have args so try as a (static) var
my $res = "\&$result";
if ($recordUnmangledSymbols) {
my $l = $lineno;
$mangledSymbols{$str} = 1;
push @unmangledSymbols, "$incroot$sfile $l: Symbol = $str : Result = $res : Filt = $result : @IncFiles";
}
$result = $res;
}
return $result;
}
sub EmitOriginal($$)
{
my ($orig , $emitter) = @_;
$orig =~ /\s*(.*)/;
printf "\t$commentToken Original - $1\n" if ($printOriginal);
printf "\t$commentToken emitted by $emitter\n" if ($recordEmitter);
}
sub EmitUnimplementedOpcode ($$$$)
{
my ($original, $asm) = @_;
if ($emitUnimplemented) {
EmitOriginal ($original, "EmitUnimplementedOpcode");
if ($asm =~ /(\S+)\s+/ or $asm =~ /\.*(\S+)/){
my $opcode = uc $1;
printf "\t$commentToken Translation of opcode $opcode not implemented\n";
printf "\t**** Insert translation here ****\n";
}
else {
UnrecognisedAsmWarning("EmitUnimplementedOpcode", $original);
}
}
}
sub SimpleEmit ($$$$)
{
my ($original, $str, $emitter, $comment) = @_;
PrintCheck();
EmitOriginal ($original, $emitter);
printf "$str";
PrintComment($comment);
Nl();
}
sub UnrecognisedAsmWarning ($$)
{
my ($where, $what) = @_;
printf STDERR "WARNING: line $. unrecognised asm format in $where: $what";
}
sub Count ($$$$) {
my ($str, $c, $start, $end) = @_;
my $total = 0;
my @a = split //, $str;
for (;$start < $end; $start++) {
$total++ if ($a[$start] eq $c);
}
return $total;
}
sub TranslateConstrainedArgs($$) {
my ($args, $constraints) = @_;
my $ins = GetInputConstraints($constraints) if ($constraints);
my @arglist;
my @rl;
my $start = 0;
my $end = length $args;
my $cpos = index $args, ",", $start;
if ($cpos > -1) {
while ($cpos > -1) {
#make sure we got a match number of '('s and ')' $start and $cpos
my $nl = Count($args, '(', $start, $cpos);
my $nr = Count($args, ')', $start, $cpos);
if ($nl == $nr) {
my $arg = substr($args, $start, $cpos - $start);
push @arglist, $arg;
$start = $cpos + 1;
$cpos = index $args, ",", $start;
} else {
$cpos = index $args, ",", $cpos + 1;
}
}
push @arglist, substr($args, $start, $end);
} else {
push @arglist, ChopWhiteSpace($args);
}
foreach (@arglist) {
push @rl, SubstituteConstraint($_, $ins);
}
return join ", ", @rl;
}
sub GetInputConstraints($) {
my ($cs) = @_;
if ($cs =~ /\:\s+\:\s*(.*)/) {
return join "", split '\"i\" ', $1;
} else {
Croak("unrecognized contraints format: $cs\n");
}
}
sub ChopWhiteSpace ($) {
my ($str) = @_;
my @a = split //, $str;
my $n = length($str);
return $str if $n == 0;
while (--$n) {
if ($a[$n] eq ' ') {
next;
} else {
last;
}
}
$n++ unless $a[$n] eq ' ';
return substr $str, 0, $n;
}
sub SubstituteConstraint($$) {
my ($arg, $cs) = @_;
my $u;
$arg = ChopWhiteSpace($arg);
unless ($cs) {
if ($arg =~ /\s*(.*)\s*$/ ) {
$u = $1;
} else {
Croak("Arg not supplied in SubstituteConstraint\n");
}
} elsif ($arg =~ /\%\S+(\d+)/ ) {
my $i = $1;
my @c = split '\,', $cs;
$u = $c[$i];
} elsif ($arg =~ /\s*(.*)\s*$/ ) {
$u = $1;
} else {
Croak("Arg not supplied in SubstituteConstraint\n");
}
my $metau = quotemeta "$u";
if (NeedsImporting($u)) {
print "\timport $u ";
PrintComment("Added by Substitute Constraint");
Nl();
AssertSourceFile();
return "$u";
} elsif ($u =~ /\s*__cpp/) {
return $u;
} elsif (grep /^$metau/, @knownLabels) {
return $u;
} else {
return "__cpp($u)";
}
}
sub RegisterSymbol($)
{
my ($sym) = @_;
return 1 if ($sym =~ /^r1[0-5]\s*$/i);
return 1 if ($sym =~ /^r[0-9]\s*$/i);
return 1 if ($sym =~ /^lr\s*$/i);
return 1 if ($sym =~ /^pc\s*$/i);
return 1 if ($sym =~ /^ip\s*$/i);
return 1 if ($sym =~ /^sp\s*$/i);
return 0;
}
sub NeedsImporting($)
{
my ($sym) = @_;
return 0 if ($sym =~ /\s*0x/i );
return 0 if ($sym =~ /^\s*0\s*/ );
return 0 if ($sym =~ /^\s*\d+\s*/ );
return 0 if ($sym =~ /\s*\(/ );
return 0 if ($sym =~ /\s*__cpp\(/ );
return 0 if RegisterSymbol($sym);
my $unms = Unmangle($sym);
my $pat = quotemeta($unms);
unless ($sym =~ /$pat/) {
return 0;
}
if (($sym =~ /(\w*)/) && (grep /^$1/, @knownLabels) ) {
return 0;
} else {
return 1;
}
}
sub MaybeImportArgs($)
{
my ($args) = @_;
my $arg;
foreach $arg (split /\,/, $args) {
MaybeEmitImport($arg);
}
}
sub GetInputConstraint($$$)
{
# It would have been nice if we could have used split to get at the constraints
# but we can't coz ':' can obviously appear as part of a qualified name. So we have to do it
# by hand.
my ($constraints, $index, $noError) = @_;
# assume constraints look like " : output : input [: sideffects"]
my $i1 = index($constraints, ":"); # output field after this index
Croak("unrecognized contraints format: $constraints\n") if (!$noError and $i1 < 0);
my $i2 = index($constraints, ":", $i1 + 1); # input field after this index
Croak("unrecognized contraints format: $constraints\n") if !$noError and $i2 < 0;
Croak("can't deal with output constraints: $constraints\n")
if !$noError and (substr($constraints, $i1 + 1, $i2 - $i1 - 1) =~ /\S+/);
Croak("can't deal with side effect constraints: $constraints\n")
if (substr($constraints, $i2 + 1) =~ /(\s*\".+\".*\(.*\))\s*\:+/);
if ($i2 > 0
and (length($constraints) - 1) > $i2
and substr($constraints, $i2 + 1) =~ /(\s*\".+\".*\(.*\S+.*\))\s*\:*/) {
return $1;
} else {
return 0;
}
}
sub GetOutputConstraint($$)
{
# It would have been nice if we could have used split to get at the constraints
# but we can't coz ':' can obviously appear as part of a qualified name. So we have to do it
# by hand.
my ($constraints, $index) = @_;
# assume constraints look like " : output : input [: sideffects"]
my $i1 = index($constraints, ":"); # output field after this index
my $i2 = index($constraints, ":", $i1 + 1); # output field after this index
if ($i2 != -1) {
if ( substr($constraints, $i1 + 1, $i2 - $i1 - 1) =~ /\s*(\".*\"\s*\S*.*\))\s*\:*/) {
return $1;
} else {
return 0;
}
} elsif ( substr($constraints, $i1 + 1) =~ /\s*(\".*\"\s*\S*.*\))\s*\:*/) {
return $1;
} else {
return 0;
}
}
# NB: assumes no mangled symbols in constraint expr.
sub CppExprFromConstraint ($)
{
my ($constraints) = @_;
return $constraints if ($constraints =~ /\s*__cpp/);
my $inputExpr;
if ($constraints =~ /\s*\".*\"\s+(.*)/) {
$inputExpr = $1;
} else {
Croak( "Unrecognized constraint pattern @ $lineno: $constraints");
}
unless ($inputExpr =~ /^\(/) {
$inputExpr = "($inputExpr)";
}
my $result = "__cpp$inputExpr";
return $result;
}
sub TranslateConstrainedInputAsmDefault ($$$$)
{
my ($original, $asm, $constraints, $comment) = @_;
# we make some gross assumptions here which appear to hold for the majority of
# our code base namely:
# 1. there is normally only one input operand and
# 2. it is named 'a0'
# This allows us to carry out the simple minded substitution seen below.
my $cppExpr0 = CppExprFromConstraint(GetInputConstraint($constraints, 0, 0));
$asm =~ s/\%a0/$cppExpr0/;
if ($asm =~ /(\w+)\s+(\S+)\s*\,\s*(.+)\s*,?(.+)?/) {
PrintCheck();
EmitOriginal($original, "TranslateConstrainedInputAsmDefault");
EmitAsm($1, $2, $3, $4, $comment);
} else {
UnrecognisedAsmWarning("TranslateConstrainedInputAsmDefault", $original);
}
}
sub TranslateAsmDefault ($$$$)
{
my ($original, $asm, $constraints, $comment) = @_;
if ($constraints) {
TranslateConstrainedInputAsmDefault($original, $asm, $constraints, $comment);
} elsif (@_[1] =~ /(\w+)\s+([^\,]+)\s*\,\s*([^\,]+)\s*,?(.+)?/) {
my $opcode = uc $1;
my $op1 = $2;
my $op2 = $3;
my $op3 = $4;
# deal with hand introduced labels that correspond to a mangled C++ name
if ($op2 =~ /\s*\[[^\,]+\,\s*\#([^\-]+)/) {
my $adr = $1;
my $pattern = quotemeta($adr);
my $unmangledAdr = Unmangle($adr);
$op2 =~ s/$adr/$unmangledAdr/i unless $unmangledAdr =~ /$pattern/;
}
if ($opcode =~ /ldr/i) {
if ($op2 =~ /^(\d+)([fFbB])/) {
my $id = $1;
my $dir = uc $2;
$op2 = "%$dir$id";
}
}
# rename obsolete shift ASL -> LSL
if ($op3 =~ /([^\,]*)\,\s*asl (.*)/i) {
$op3 = "$1, lsl $2";
}
# deal with the likes of #___2PP.KernCSLocked-.-8
if ($op3 =~ /([^\-\s]\.[^\s\-])/ ) {
my $s = "$1";
my $p = quotemeta($1);
$s =~ s/\./\_/;
$op3 =~ s/$p/$s/;
}
PrintCheck();
EmitOriginal ($original, "TranslateAsmDefault");
EmitAsm($opcode, $op1, $op2, $op3, $comment);
} else {
UnrecognisedAsmWarning("TranslateAsmDefault", $original);
}
}
# Work around 'feature' in embedded assembler stemming from the fact that
# 'and' is both asm and a C++ keyword.
sub TranslateConstrainedAnd ($$$$)
{
my ($original, $asm, $constraints, $comment) = @_;
# we make some gross assumptions here which appear to hold for the majority of
# our code base namely:
# 1. there is normally only one input operand and
# 2. it is named 'a0'
# This allows us to carry out the simple minded substitution seen below.
my $cppExpr0 = CppExprFromConstraint(GetInputConstraint($constraints, 0, 0));
$asm =~ s/\%a0/$cppExpr0/;
if ($asm =~ /(\w+)\s+(.+)\s*\,\s*(.+)\s*,?(.+)?/) {
my $opcode = uc $1;
my $op1 = $2;
my $op2 = $3;
my $op3 = $4;
# rename obsolete shift ASL -> LSL
if ($op3 =~ /([^\,]*)\,\s*asl (.*)/) {
$op3 = "$1, lsl $2";
}
PrintCheck();
EmitOriginal ($original, "TranslateConstrainedAnd");
printf "\t$opcode $op1, $op2";
printf ", $op3" if $op3;
PrintComment($comment);
Nl();
} else {
UnrecognisedAsmWarning("TranslateConstrainedAnd", $original);
}
}
sub TranslateAnd ($$$$)
{
my ($original, $asm, $constraints, $comment) = @_;
if ($constraints) {
TranslateConstrainedAnd($original, $asm, $constraints, $comment);
} elsif (@_[1] =~ /(\w+)\s+([^\,]+)\s*\,\s*([^\,]+)\s*,?(.+)?/) {
my $opcode = uc $1;
my $op1 = $2;
my $op2 = $3;
my $op3 = $4;
# rename obsolete shift ASL -> LSL
if ($op3 =~ /([^\,]*)\,\s*asl (.*)/) {
$op3 = "$1, lsl $2";
}
PrintCheck();
EmitOriginal ($original, "TranslateAnd");
printf "\t$opcode $op1, $op2";
printf ", $op3" if $op3;
PrintComment($comment);
Nl();
} else {
UnrecognisedAsmWarning("TranslateAnd", $original);
}
}
# based on TranslateConstrainedInputAsmDefault
sub TranslateConstrainedCoprocessorInsn ($$$$)
{
my ($original, $asm, $constraints, $comment) = @_;
# we make some gross assumptions here which appear to hold for the majority of
# our code base namely:
# 1. there is normally only one input operand and
# 2. it is named 'a0'
# This allows us to carry out the simple minded substitution seen below.
my $cppExpr0 = CppExprFromConstraint(GetInputConstraint($constraints, 0, 0));
$asm =~ s/\%a0/$cppExpr0/;
if ($asm =~ /(\w+)\s+(.+)\s*\,\s*(.+)\s*,?(.+)?/) {
my $opcode = $1;
my $coproc = lc $2;
my $op1 = $3;
my $op2 = $4;
$coproc = "p$coproc" unless $coproc =~ /^p.+/;
PrintCheck();
EmitOriginal($original, "TranslateConstrainedCoprocessorInsn");
EmitAsm($opcode, $coproc, $op1, $op2, $comment);
} else {
UnrecognisedAsmWarning("TranslateConstrainedCoprocessorInsn", $original);
}
}
# based on TranslateAsmDefault
sub TranslateCoprocessorInsn ($$$$)
{
my ($original, $asm, $constraints, $comment) = @_;
if ($constraints) {
TranslateConstrainedCoprocessorInsn($original, $asm, $constraints, $comment);
} elsif (@_[1] =~ /(\w+)\s+(.+)\s*\,\s*(.+)\s*,?(.+)?/) {
my $opcode = $1;
my $coproc = lc $2;
my $op1 = $3;
my $op2 = $4;
$coproc = "p$coproc" unless $coproc =~ /^p.+/;
PrintCheck();
EmitOriginal ($original, "TranslateCoprocessorInsn");
EmitAsm($opcode, $coproc, $op1, $op2, $comment);
} else {
UnrecognisedAsmWarning("TranslateCoprocessorInsn", $original);
}
}
sub TranslateConstrainedSWI ($$$$)
{
my ($original, $asm, $constraints, $comment) = @_;
# we make some gross assumptions here which appear to hold for the majority of
# our code base namely:
# 1. there is normally only one input operand and
# 2. it is named 'a0'
# This allows us to carry out the simple minded substitution seen below.
my $cppExpr0 = CppExprFromConstraint(GetInputConstraint($constraints, 0, 0));
$asm =~ s/\%a0/$cppExpr0/;
if ($asm =~ /(\w+)\s+(.+)/) {
my $opcode = $1;
my $op1 = $2;
PrintCheck();
EmitOriginal($original, "TranslateConstrainedSWI");
$opcode = RequiredCase($opcode);
printf "\t$opcode $op1";
PrintComment($comment);
Nl();
} else {
UnrecognisedAsmWarning("TranslateConstrainedSWI", $original);
}
}
sub TranslateSWI ($$$$)
{
my ($original, $asm, $constraints, $comment) = @_;
if ($constraints) {
TranslateConstrainedSWI($original, $asm, $constraints, $comment);
} elsif (@_[1] =~ /(\w+)\s+(.+)/) {
my $opcode = $1;
my $op1 = $2;
PrintCheck();
EmitOriginal ($original, "TranslateSWI");
$opcode = RequiredCase($opcode);
printf "\t$opcode $op1";
PrintComment($comment);
Nl();
} else {
UnrecognisedAsmWarning("TranslateSWI", $original);
}
}
sub TranslateLabel ($$$$)
{
my ($original, $asm, $constraints, $comment) = @_;
if ( $asm =~ /\s*(\S+)\:/) {
my $label = $1;
$label = Unmangle($label) unless ($label =~ /^(\d+)/);
SimpleEmit($original, $label, "TranslateLabel", $comment);
} else {
UnrecognisedAsmWarning("TranslateLabel", $original);
}
}
sub TranslateConstrainedAdr ($$$$)
{
my ($original, $asm, $constraints, $comment) = @_;
# we make some gross assumptions here which appear to hold for the majority of
# our code base namely:
# 1. there is normally only one input operand and
# 2. it is named 'a0'
# This allows us to carry out the simple minded substitution seen below.
my $cppExpr0 = CppExprFromConstraint(GetInputConstraint($constraints, 0, 0));
$asm =~ s/\%a0/$cppExpr0/;
if ($asm =~ /(\w+)\s+(\S+)\s*\,\s*(.+)\s*,?(.+)?/) {
PrintCheck();
EmitOriginal($original, "TranslateConstrainedAdr");
EmitAsm($1, $2, $3, $4, $comment);
} else {
UnrecognisedAsmWarning("TranslateConstrainedAdr", $original);
}
}
sub TranslateAdr ($$$$)
{
my ($original, $asm, $constraints, $comment) = @_;
if ($constraints) {
TranslateConstrainedAdr($original, $asm, $constraints, $comment);
} elsif (@_[1] =~ /(\w+)\s+([^\,]+)\s*\,\s*([^\,]+)/) {
my $opcode = uc $1;
my $op1 = $2;
my $eadr = $3;
my $op3;
if ($eadr =~ /^(\d+)([fFbB])/) {
my $id = $1;
my $dir = uc $2;
$eadr = "%$dir$id";
} else {
my $unmangledEadr = Unmangle($eadr);
my $pattern = quotemeta($eadr);
$eadr = "__cpp($unmangledEadr)" unless $unmangledEadr =~ /$pattern/;
}
MaybeEmitImport($eadr);
PrintCheck();
EmitOriginal ($original, "TranslateAdr");
EmitAsm($opcode, $op1, $eadr, $op3, $comment);
} else {
UnrecognisedAsmWarning("TranslateAdr", $original);
}
}
sub RequiredCase($)
{
my ($s) = @_;
lc $s;
}
sub TranslateAlign ($$$$)
{
my ($original, $asm, $constraints, $comment) = @_;
if ( $asm =~ /\s*\.align\s+(\S+)/i) {
my $alignment = $1;
my $boundary = "1:SHL:$1";
# my $boundary = "";
my $boundary = "" if ($alignment =~ /\s*0\s*/);
my $directive = RequiredCase("ALIGN");
SimpleEmit($original, "\t$directive $boundary", "TranslateAlign", $comment);
}
else {
UnrecognisedAsmWarning("TranslateAlign", $original);
}
}
sub TranslateSpace ($$$$)
{
my ($original, $asm, $constraints, $comment) = @_;
if ( $asm =~ /\s*\.space\s+(\S+)/i) {
my $directive = RequiredCase("SPACE");
SimpleEmit($original, "\t$directive $1", "TranslateSpace", $comment);
}
else {
UnrecognisedAsmWarning("TranslateSpace", $original);
}
}
sub TranslateByte ($$$$)
{
my ($original, $asm, $constraints, $comment) = @_;
my $directive = RequiredCase("DCB");
$asm =~ /\s*.byte\s+(.*)/i;
my $args = $1;
if ($constraints) {
$args = TranslateConstrainedArgs($args, $constraints);
SimpleEmit($original, "\t$directive $args", "TranslateByte", $comment);
} else {
MaybeImportArgs($args);
SimpleEmit($original, "\t$directive $args", "TranslateByte", $comment);
}
}
sub CppExprList($)
{
my ($arg) = @_;
return $arg if ($arg =~ /\s*__cpp/);
if ($arg =~ /(.*)\,\s*([^\s]*)/) {
my $result = CppExprList($1);
my $expr = $2;
if ($expr =~ /\s*0x\d+/) {
return $result .= ", __cpp($expr)";
} elsif ($expr =~ /\s*(\d+)/) {
my $hex = sprintf("%#.8x", $1);
return $result .= ", __cpp($hex)";
} else {
my $pattern = quotemeta($expr);
my $unmangledExpr = Unmangle($expr);
return ($unmangledExpr =~ /$pattern/) ? $expr : "__cpp(\&$unmangledExpr)";
}
} else {
if ($arg =~ /\s*0x\d+/) {
return " __cpp($arg)";
} elsif ($arg =~ /\s*(\d+)/) {
my $hex = sprintf("%#.8x", $1);
return " __cpp($hex)";
} else {
if ($arg =~ /\s*([^\s]*)/) {
$arg = $1;
my $pattern = quotemeta($arg);
my $unmangledArg = Unmangle($arg);
return ($unmangledArg =~ /$pattern/) ? $arg : "__cpp(\&$unmangledArg)";
}
}
}
}
# Add symbols here that aren't imported if they're 'special'.
my %recognizedSymbols =
(
Followers => 1,
TheScheduler => 1,
TheMonitor => 1,
MonitorStack => 1,
ServerAccept => 1,
ServerReceive => 1,
wordmove => 1,
memcpy => 1,
memcompare => 1,
memclr => 1,
memset => 1,
memmove => 1,
);
sub EmitRecognizedSymbol ($$$$)
{
my ($original, $directive, $sym, $comment) = @_;
return 0 if ($sym =~ /\s*0x/i );
return 0 if ($sym =~ /^\s*0\s*/ );
return 0 if ($sym =~ /^\s*\d+\s*/ );
return 0 if ($sym =~ /\s*__cpp\(/ );
my $unms = Unmangle($sym);
my $pat = quotemeta($unms);
unless ($sym =~ /$pat/) {
SimpleEmit($original, "\t$directive __cpp($unms)", "TranslateWord", $comment);
return 1;
}
if (($sym =~ /(\S*)/) && !(grep /^$1$/, @knownLabels) ) {
SimpleEmit($original, "\timport $sym", "TranslateWord", "// added by Tranasm");
AssertSourceFile();
SimpleEmit($original, "\t$directive $sym", "TranslateWord", $comment);
return 1;
} else {
return 0;
}
}
sub TranslateWord ($$$$)
{
my ($original, $asm, $constraints, $comment) = @_;
my $directive = RequiredCase("DCD");
$asm =~ /\s*.word\s+(.*)/i;
my $args = $1;
if ($constraints) {
$args = TranslateConstrainedArgs($args, $constraints);
SimpleEmit($original, "\t$directive $args", "TranslateWord", $comment);
} else {
MaybeImportArgs($args);
SimpleEmit($original, "\t$directive $args", "TranslateWord", $comment);
}
}
sub TranslateCode ($$$$)
{
my ($original, $asm, $constraints, $comment) = @_;
my $directive = RequiredCase("CODE");
if ( $asm =~ /\s*\.code\s+(\d+)\s*/i) {
SimpleEmit($original, "\t$directive$1", "TranslateCode", $comment);
}
else {
UnrecognisedAsmWarning("TranslateCode", $original);
}
}
sub TranslateGlobal ($$$$)
{
my ($original, $asm, $constraints, $comment) = @_;
my $directive = RequiredCase("EXPORT");
if ( $asm =~ /\s*\.global\s+(\S+)/i) {
SimpleEmit($original, "\t$directive $1", "TranslateGlobal", $comment);
}
else {
UnrecognisedAsmWarning("TranslateGlobal", $original);
}
}
sub TranslateExtern ($$$$)
{
my ($original, $asm, $constraints, $comment) = @_;
my $directive = RequiredCase("IMPORT");
if ( $asm =~ /\s*\.extern\s+(\S+)/i) {
SimpleEmit($original, "\t$directive $1", "TranslateExtern", $comment);
}
else {
UnrecognisedAsmWarning("TranslateExtern", $original);
}
}
sub TranslateNop ($$$$)
{
my ($original, $asm, $constraints, $comment) = @_;
my $directive = RequiredCase("NOP");
SimpleEmit($original, "\t$directive", "TranslateNop", $comment);
}
sub EmitAsm($$$$$)
{
my ($opcode, $op1, $op2, $op3, $comment) = @_;
$opcode = RequiredCase($opcode);
printf "\t%s %s, %s", $opcode, $op1, $op2;
printf(", %s", $op3) if $op3;
PrintComment($comment);
Nl();
}
sub TranslateBranchDefault ($$$$)
{
my ($original, $asm, $constraints, $comment) = @_;
if ($constraints) {
Croak( "TranslateBranchDefault can't deal with Constraint instructions\n E.G. - $original");
} elsif ($asm =~ /(\w+)\s+(.+)\s*$/) {
my $opcode = RequiredCase($1);
my $target = $2;
if ($target =~ /^(\d+)([fFbB])/) {
my $id = $1;
my $dir = uc $2;
$target = "%$dir$id";
} else {
my $unmangledTarget = Unmangle($target);
my $pattern = quotemeta($target);
$target = "__cpp($unmangledTarget)" unless $unmangledTarget =~ /$pattern/;
}
EmitOriginal ($original, "TranslateBranchDefault");
MaybeEmitImport($target);
printf("\t%s %s", $opcode, $target);
PrintComment($comment);
Nl();
} else {
UnrecognisedAsmWarning("TranslateBranchDefault", $original);
}
}
sub TranslatePushPop ($$$$)
{
my ($original, $asm, $constraints, $comment) = @_;
if ($asm =~ /(\w+)\s+(.+)\s*$/) {
my $opcode = RequiredCase($1);
my $registers = $2;
if ($constraints) {
Croak( "TranslatePushPop can't deal with constrained instructions\n E.G. - $original\n");
} else {
EmitOriginal ($original, "TranslatePushPop");
printf "\t$opcode $registers";
PrintComment($comment);
Nl();
}
}
else {
UnrecognisedAsmWarning("TranslatePushPop", $comment);
}
}
sub TranslateConstrainedAsmDefault ($$$$)
{
# Here we assume:
# 1. at most one output constraint
# 2. the output constraint is named '%0'
# 3. at most one input constraint
# 4. the input constraint is named '%a0'
my ($original, $asm, $constraints, $comment) = @_;
my $outputConstraint;
my $inputConstraint;
if ($outputConstraint = GetOutputConstraint($constraints, 0)) {
my $outputCppExpr = CppExprFromConstraint($outputConstraint);
$asm =~ s/\%0/$outputCppExpr/;
}
if ($inputConstraint = GetInputConstraint($constraints, 0, 1)) {
my $inputCppExpr = CppExprFromConstraint($inputConstraint);
$asm =~ s/\%a0/$inputCppExpr/;
}
if ($asm =~ /^\s*(\w+)\s+([^\,]+)\s*\,\s*(.+)\s*,?(.+)?/) {
my $opcode = uc $1;
my $op1 = $2;
my $op2 = $3;
my $op3 = $4;
if ($outputConstraint) {
printf "\t>>> CHECK THIS - output constraints need special attention <<<\n";
} else {
PrintCheck();
}
EmitOriginal($original, "TranslateConstrainedAsmDefault");
$op1 =~ s/cpsr_flg/cpsr_f/i;
$op2 =~ s/asl /lsl /i if $op2;
$op3 =~ s/asl /lsl /i if $op3;
EmitAsm($opcode, $op1, $op2, $op3, $comment);
} else {
UnrecognisedAsmWarning("TranslateConstrainedAsmDefault", $original);
}
}
sub TranslatePotentialOutputConstrainedAsm ($$$$)
{
my ($original, $asm, $constraints, $comment) = @_;
if ($constraints) {
TranslateConstrainedAsmDefault($original, $asm, $constraints, $comment);
} elsif ($asm =~ /(\w+)\s+([^\,]+)\s*\,\s*(.+)\s*,?(.+)?/) {
my $opcode = uc $1;
my $op1 = $2;
my $op2 = $3;
my $op3 = $4;
PrintCheck();
EmitOriginal ($original, "TranslatePotentialOutputConstrainedAsm");
# MSR cpsr,...
$op1 .= "_cxsf" if ($opcode =~ /msr/i and $op1 =~ /[cs]psr\s*$/);
$op1 =~ s/cpsr_flg/cpsr_f/i;
$op2 =~ s/asl /lsl /i if $op2;
$op3 =~ s/asl /lsl /i if $op3;
EmitAsm($opcode, $op1, $op2, $op3, $comment);
} else {
UnrecognisedAsmWarning("TranslatePotentialOutputConstrainedAsm", $original);
}
}
# Here's the table of translator functions
my %opcodeTranslatorMapping =
(
"LABEL:"=>\&TranslateLabel,
".ALIGN"=>\&TranslateAlign,
".BSS"=>\&EmitUnimplementedOpcode,
".BYTE"=>\&TranslateByte,
".CODE"=>\&TranslateCode,
".DATA"=>\&EmitUnimplementedOpcode,
".GLOBAL"=>\&TranslateGlobal,
".EXTERN"=>\&TranslateExtern,
".HWORD"=>\&EmitUnimplementedOpcode,
".LONG"=>\&EmitUnimplementedOpcode,
".SECTION"=>\&EmitUnimplementedOpcode,
".SPACE"=>\&TranslateSpace,
".TEXT"=>\&EmitUnimplementedOpcode,
".WORD"=>\&TranslateWord,
"ADC"=>\&TranslateAsmDefault,
"ADD"=>\&TranslateAsmDefault,
"ADR"=>\&TranslateAdr,
"AND"=>\&TranslateAnd,
"B"=>\&TranslateBranchDefault,
"BEQ"=>\&TranslateBranchDefault,
"BNE"=>\&TranslateBranchDefault,
"BHS"=>\&TranslateBranchDefault,
"BCS"=>\&TranslateBranchDefault,
"BCC"=>\&TranslateBranchDefault,
"BLO"=>\&TranslateBranchDefault,
"BMI"=>\&TranslateBranchDefault,
"BPL"=>\&TranslateBranchDefault,
"BVS"=>\&TranslateBranchDefault,
"BVC"=>\&TranslateBranchDefault,
"BHI"=>\&TranslateBranchDefault,
"BLS"=>\&TranslateBranchDefault,
"BGE"=>\&TranslateBranchDefault,
"BLT"=>\&TranslateBranchDefault,
"BGT"=>\&TranslateBranchDefault,
"BLE"=>\&TranslateBranchDefault,
"BCLR"=>\&TranslateBranchDefault,
"BIC"=>\&TranslateAsmDefault,
"BKPT"=>\&TranslateBranchDefault, # not really a branch but can reuse translator
"BL"=>\&TranslateBranchDefault,
"BLEQ"=>\&TranslateBranchDefault,
"BLNE"=>\&TranslateBranchDefault,
"BLHS"=>\&TranslateBranchDefault,
"BLCS"=>\&TranslateBranchDefault,
"BLCC"=>\&TranslateBranchDefault,
"BLLO"=>\&TranslateBranchDefault,
"BLMI"=>\&TranslateBranchDefault,
"BLPL"=>\&TranslateBranchDefault,
"BLVS"=>\&TranslateBranchDefault,
"BLVC"=>\&TranslateBranchDefault,
"BLHI"=>\&TranslateBranchDefault,
"BLLS"=>\&TranslateBranchDefault,
"BLGE"=>\&TranslateBranchDefault,
"BLLT"=>\&TranslateBranchDefault,
"BLGT"=>\&TranslateBranchDefault,
"BLLE"=>\&TranslateBranchDefault,
"BLX"=>\&TranslateBranchDefault,
"BLXEQ"=>\&TranslateBranchDefault,
"BLXNE"=>\&TranslateBranchDefault,
"BLXHS"=>\&TranslateBranchDefault,
"BLXCS"=>\&TranslateBranchDefault,
"BLXCC"=>\&TranslateBranchDefault,
"BLXLO"=>\&TranslateBranchDefault,
"BLXMI"=>\&TranslateBranchDefault,
"BLXPL"=>\&TranslateBranchDefault,
"BLXVS"=>\&TranslateBranchDefault,
"BLXVC"=>\&TranslateBranchDefault,
"BLXHI"=>\&TranslateBranchDefault,
"BLXLS"=>\&TranslateBranchDefault,
"BLXGE"=>\&TranslateBranchDefault,
"BLXLT"=>\&TranslateBranchDefault,
"BLXGT"=>\&TranslateBranchDefault,
"BLXLE"=>\&TranslateBranchDefault,
"BSET"=>\&EmitUnimplementedOpcode,
"BX"=>\&TranslateBranchDefault,
"BXEQ"=>\&TranslateBranchDefault,
"BXNE"=>\&TranslateBranchDefault,
"BXHS"=>\&TranslateBranchDefault,
"BXCS"=>\&TranslateBranchDefault,
"BXCC"=>\&TranslateBranchDefault,
"BXLO"=>\&TranslateBranchDefault,
"BXMI"=>\&TranslateBranchDefault,
"BXPL"=>\&TranslateBranchDefault,
"BXVS"=>\&TranslateBranchDefault,
"BXVC"=>\&TranslateBranchDefault,
"BXHI"=>\&TranslateBranchDefault,
"BXLS"=>\&TranslateBranchDefault,
"BXGE"=>\&TranslateBranchDefault,
"BXLT"=>\&TranslateBranchDefault,
"BXGT"=>\&TranslateBranchDefault,
"BXLE"=>\&TranslateBranchDefault,
"CDP"=>\&TranslateAsmDefault,
"CLZ"=>\&TranslateAsmDefault,
"CMN"=>\&TranslateAsmDefault,
"CMP"=>\&TranslateAsmDefault,
"EOR"=>\&TranslateAsmDefault,
"LDC"=>\&TranslateAsmDefault,
"LDM"=>\&TranslateAsmDefault,
"LDR"=>\&TranslateAsmDefault,
"LDRB"=>\&TranslateAsmDefault,
"LSL"=>\&EmitUnimplementedOpcode,
"LSR"=>\&EmitUnimplementedOpcode,
"MCR"=>\&TranslateCoprocessorInsn,
"MLA"=>\&TranslateAsmDefault,
"MOV"=>\&TranslatePotentialOutputConstrainedAsm,
"MRC"=>\&TranslateCoprocessorInsn,
"MRS"=>\&TranslatePotentialOutputConstrainedAsm,
"MSR"=>\&TranslatePotentialOutputConstrainedAsm,
"MUL"=>\&TranslateAsmDefault,
"MVN"=>\&TranslateAsmDefault,
"NOP"=>\&TranslateNop,
"ORR"=>\&TranslateAsmDefault,
"POP"=>\&TranslatePushPop,
"PUSH"=>\&TranslatePushPop,
"RSB"=>\&TranslateAsmDefault,
"RSC"=>\&TranslateAsmDefault,
"SBC"=>\&TranslateAsmDefault,
"SMLAL"=>\&TranslateAsmDefault,
"STC"=>\&TranslateAsmDefault,
"STM"=>\&TranslateAsmDefault,
"STR"=>\&TranslateAsmDefault,
"SUB"=>\&TranslateAsmDefault,
"SWI"=>\&TranslateSWI,
"SWP"=>\&TranslateAsmDefault,
"TEQ"=>\&TranslateAsmDefault,
"TST"=>\&TranslateAsmDefault,
"UMLAL"=>\&TranslateAsmDefault,
"UMULL"=>\&TranslateAsmDefault,
"UMULLEQ"=>\&TranslateAsmDefault,
"UMULLNE"=>\&TranslateAsmDefault,
"UMULLCS"=>\&TranslateAsmDefault,
"UMULLCC"=>\&TranslateAsmDefault,
"UMULLHS"=>\&TranslateAsmDefault,
"UMULLLO"=>\&TranslateAsmDefault,
"UMULLMI"=>\&TranslateAsmDefault,
"UMULLPL"=>\&TranslateAsmDefault,
"UMULLVS"=>\&TranslateAsmDefault,
"UMULLVC"=>\&TranslateAsmDefault,
"UMULLHI"=>\&TranslateAsmDefault,
"UMULLLS"=>\&TranslateAsmDefault,
"UMULLGE"=>\&TranslateAsmDefault,
"UMULLLT"=>\&TranslateAsmDefault,
"UMULLGT"=>\&TranslateAsmDefault,
"UMULLLE"=>\&TranslateAsmDefault,
);
my @unknownOpcodes;
sub GetTranslator ($)
{
my $opcode = shift;
# see if opcode looks like a label
return $opcodeTranslatorMapping{"LABEL:"} if ($opcode =~ /\w+\:$/);
# just look it up
my $translator = $opcodeTranslatorMapping{$opcode};
return $translator if $translator;
# see if we know the 'root' of the opcode
return $opcodeTranslatorMapping{substr($opcode, 0, 3)};
}
my %seenIncFiles = ();
sub trackSourceLine($)
{
my ($line) = @_;
if ($line =~ /\#line (\d+)\s*(.*)$/ ) {
$lineno = $1-1;
$sourcefile = $2;
if ($sourcefile =~ /.*\.h/i) {
unless ($seenIncFiles{$sourcefile}) {
$seenIncFiles{$sourcefile} = 1;
my $incfile = "$sourcefile";
$incfile =~ s/\"//go;
$incfile =~ s/\\\\/${main::PATHSEP}/go;
$incfile = "$incroot"."$incfile" unless ($incfile =~ /^${main::PATHSEP}/);
push @IncFiles, $incfile;
}
}
}
}
sub AssertSourceFile()
{
printf "#line %d %s\n", $lineno, $sourcefile;
}
my @contents;
sub AddLabel($) {
my ($label) = @_;
if ($label =~ /\s*(\S+)\s*/ ) {
$label = $1;
}
push @knownLabels, $label;
}
sub MaybeEmitImport ($) {
my ($l) = @_;
print "\timport $l\[DYNAMIC\]\n" if NeedsImporting($l);
}
sub Pass1()
{
die "ERROR: Couldn't open $infile\n" unless open INP, "<$infile";
my $line;
MAINBLOCK: while ($line = <INP>) {
# strip off comment if present
my $statement;
my $comment = 0;
push @contents, $line;
if ($line =~ /^\s*$/) {
next MAINBLOCK;
}
if ($line =~ /(.*)\/\/(.+)/) {
$statement = $1;
$comment = $2;
} else {
$statement = $line;
}
if ($statement =~ /^((.*;\s*)|(\s*))asm\s*\(/) {
foreach $statement ( split /\;/, $statement ) {
TRANSLATE_ASM:
if ($statement =~ /^\s*asm\s*\(\s*\"(.*)\"\s*(:.*)*\)/) {
my $asm = $1;
my $constraints = $2;
$asm =~ s/\"\s*\"//g;
$asm =~ /\s*(\S+)/;
my $opcode = $1;
# if its a label record it
if ($opcode =~ /(\w+)\:$/) {
AddLabel($1);
}
}
}
}
}
close INP;
}
sub CanonicalizeAsm($) {
my ($s) = @_;
if ($s =~ /(asm\([^\)]+\))\s*\;(.*)/o) {
my $start = "$`";
my $subst = $1;
my $rem = $2;
$subst =~ s/\;/ \"\)\; asm\(\"/g;
return "$start"."$subst; ".CanonicalizeAsm($rem);
} else {
return $s;
}
}
sub Pass2()
{
$lineno = 0;
my $startingBody = 0;
my $line;
MAINBLOCK: foreach $line ( @contents ) {
# strip off comment if present
my $statement;
my $comment = 0;
warn "$lineno\n" if $plineno;
$lineno++;
if ($line =~ /^\s*$/) {
print "$line";
next MAINBLOCK;
}
if ($line =~ /(.*)\/\/(.+)/) {
$statement = $1;
$comment = $2;
} else {
$statement = $line;
}
if ($statement =~ /^((.*;\s*)|(\s*))asm\s*\(/) {
# unfortunately we get things like:
# asm("mcr"#cc" p15, 0, "#r", c7, c5, 0; sub"#cc" pc, pc, #4 ");
# we need to turn this into asm("mcr"#cc" p15, 0, "#r", c7, c5, 0"); asm("sub"#cc" pc, pc, #4 ");
$statement = CanonicalizeAsm($statement);
foreach $statement ( split /\;/, $statement ) {
TRANSLATE_ASM:
if ($statement =~ /^\s*asm\s*\(\s*\"(.*)\"\s*(:.*)*\)/) {
my $asm = $1;
my $constraints = $2;
$asm =~ s/\"\s*\"//g;
$asm =~ /\s*(\S+)/;
my $opcode = uc $1;
AssertSourceFile();
my $translator = GetTranslator($opcode);
if ($translator) {
$translator->($line, $asm, $constraints, $comment);
} else {
push @unknownOpcodes, $opcode ;
EmitUnimplementedOpcode($line, $asm, $constraints, $comment);
}
} elsif ($statement =~ /^\s*(__declspec.*\s* __asm .*\)\s*\{)(.*)$/) {
AssertSourceFile();
print "$1\n";
print "\tPRESERVE8\n\tCODE32\n";
$statement = $2;
goto TRANSLATE_ASM;
} elsif ($statement =~ /^\s*(__asm .*\)\s*\{)(.*)$/) {
AssertSourceFile();
print "$1\n";
print "\tPRESERVE8\n\tCODE32\n";
$statement = $2;
goto TRANSLATE_ASM;
} elsif (($statement =~ /^\s*.*\s+__asm [^\{]*$/) || ($statement =~ /^\s*__asm [^\{]*$/)) {
AssertSourceFile();
print "$statement";
$startingBody = 1;
} elsif ($startingBody && ($statement =~ /^\s*\{\s*$/) ) {
AssertSourceFile();
print "$statement";
print "\tPRESERVE8\n\tCODE32\n";
$startingBody = 0;
} elsif ($statement =~ /\s*(\S.*)$/) {
print "\t$1;\n";
}
}
} elsif (($statement =~ /^\s*.*\s+__asm [^\{]*$/) || ($statement =~ /^\s*__asm [^\{]*$/)) {
AssertSourceFile();
print "$statement";
$startingBody = 1;
} elsif ($startingBody && ($statement =~ /^\s*\{\s*$/) ) {
AssertSourceFile();
print "$statement";
print "\tPRESERVE8\n\tCODE32\n";
$startingBody = 0;
} else {
trackSourceLine($line);
print "$line";
}
}
}
sub Main () {
Pass1();
Pass2();
}
Main();
if ($outfile) {
select $savedOut;
close OUT;
}
if (@unknownOpcodes > 0){
printf STDERR "WARNING: The following opcodes were unrecognised:\n";
my $op;
foreach $op (sort @unknownOpcodes) { printf STDERR "\t$op\n";}
}
if ($recordUnmangledSymbols){
open US, ">>$symbolsfile";
foreach (@unmangledSymbols) { print US "$_ \n";}
close US;
}
sub Usage
{
print <<EOT;
tranasm
Translate GCC inline assembler into ARM embedded assembler
Usage:
tranasm [options] file
Where:
[file] The file to be translated.
Options:
--record-emitter each translation annotated with name of translation function
--suppress-check omit deliberate errors inserted to force human checking
--no-original do not emit the original gcc inline assembler as comment
--error-string the string to emit as the deliberate error
--output the name of the output file
--help this message
Options may also be specified as a short abbreviation, ie -h or -o=foo.tr.
The default deliberate error is indicated thus />>> CHECK THIS .*<<</.
EOT
exit 1;
}
__END__