#!/bin/perl
#
# Usage: $0 <cpu?.s
#
# Small perl script to optimise cpu?.s
# Note: some optimisations done here are not safe! It is a chance
# if everything works fine...
#
# 05/04/97: Modified to make it work with perl5.
# 25/10/97: Removed some optimisations suspected to introduce bugs.
#
# (c) By Samuel Devulder, 04/97.
#

# uncomment the next 2 lines if you experience problems with the
# optimized code:
#while(<>) { print; }
#exit 0;

###############################################################################

if($#ARGV >= $[) {
    &usage;
} 

###############################################################################

while(<>) {
    print;
    last if /^_op_[0-9a-f]+:$/;
}

###############################################################################

chop;
print STDERR "$_\r";
$num_opt = 0;
while($last_func = &analyse_function) {
    while(&optimize_function) {}
    &dump_function;
    print $last_func;$_ = $last_func;chop;
    print STDERR "$_\r";
}
while(&optimize_function) {}
&dump_function;

print STDERR "$num_opt optimisations\n";

exit 0;

###############################################################################

sub delete_line {
    local($i) = @_;
    for(; $i < $lineno; ++$i) {
        $line[$i] = $line[1+$i];
    }
    $line[--$lineno] = "";
}
    
###############################################################################

sub optimize_function {
    local($i, $j, $ext, $adr, $reg, $opt, $tmp);
    $opt = $num_opt;
    for($i = 0; $i < $lineno; ++$i) {
        study($line[$i]);
        #################################################################
        #   INST.x  X,Dn            =>  INST.x  X,Dn
        #   TST.x   Dn                  <deleted>
        #################################################################
        if(($line[$i] =~/(mov[e]?|or|and|add|sub|tst)([bwl])\s+.+,(d[0-7])$/)
        && ($line[$i+1]=~ /tst$2\s+$3/)) {
            &delete_line($i+1);
            ++$num_opt;
        } 
        #################################################################
        #   MOVE.x  Dn,X            =>  MOVE.x  Dn,X
        #   TST.x   Dn                  <deleted>
        #################################################################
        if(($line[$i]  =~ /mov[e]?([bwl])\s+(d[0-7]),(.+)$/)
        && ($line[$i+1]=~ /tst$1\s+$2/)) {
            &delete_line($i+1);
            ++$num_opt;
        }
        #################################################################
        #   MOVE.B  1(An),Dr        =>  MOVE.W  (An)+,Dr
        #   ADDQ.x  #2,AN               <deleted>
        # This is allowed since An always points on (B1,B2) With B1==0
        #################################################################
        if($line[$i]   =~ /moveb\s+(a[0-7])@\(1\),(d[0-7])$/) {
            $adr=$1; $reg=$2;
            if($line[$i+1] =~ /addq.\s+#2,$adr/) {
                $line[$i] = "\tmovew $adr@+,$reg\n";
                &delete_line($i+1);
                ++$num_opt;
        #################################################################
        #   CLR.W   Dn              =>  <deleted>
        #   MOVE.W  X,Dn                MOVE.W  X,Dn
        #################################################################
                if(($i>1) && ($line[$i-1] =~ /clrw\s+$reg/)) {
                &delete_line($i-1);
                ++$num_opt;
            }
        }       
        }       
        #################################################################
        #   TST.x   Dn              =>  MOVE.x  Dn,X
        #   ..(Dn not used)..           ..(Dn not used)..
        #   MOVE.x  Dn,X                <deleted>
        #################################################################
#        if(($line[$i] =~ /tst([bwl])\s+(d[0-7])$/)) {
#            $ext = $1; $reg = $2;
#            $line[$lineno] = "rts"; # sentinel
#            $tmp = "(^.+:)|rts|\sj.+\s|$reg";
#            for($j = $i+1; $line[$j] !~ /$tmp/; ++$j) {}
#            if($line[$j] =~ /mov[e]?$ext\s+$reg,(.+)$/) {
#                $adr = $1;
#                $tmp = "";
#                if($adr =~ /(a[0-7])/) {if($tmp eq "") {$tmp = "$1";} else {$tmp = "$tmp|$1"}}
#                if($adr =~ /(d[0-7])/) {if($tmp eq "") {$tmp = "$1";} else {$tmp = "$tmp|$1"}}
#                if(!($tmp eq "")) {
#                    for($k = $i+1; $k<$j && ($line[$k]!~/$tmp/); ++$k) {}
#                } else {$k = $j;}
#                if($k == $j) {
#                    $line[$i] = $line[$j];
#                    &delete_line($j);
#                    ++$num_opt;
#                }
#            }
#        }
        if($line[$i] =~ /_regs/) { # some speedup
        #################################################################
        #   INST   .._regs+N1..     =>  lea     _regs+N2,An
        #   ..(no refs to _regs)..      INST    ..N1-N2(An)..
        #   lea     _regs+N2,An         ..(no refs to _regs)..
        #################################################################
        if($line[$i] =~ /_regs(\+\d+)?/) {
            $N1 = $1;
            $line[$lineno] = "rts"; # sentinel
            $tmp = "_regs|rts|\s+j.+\s|(^.+:)";
            for($j = $i+1; $line[$j] !~ /$tmp/; ++$j) {}
            if($line[$j] =~ /lea\s+_regs(\+\d+)?,(a[0-7])$/) {
                $N2 = $1; $An = $2;
                for($k = $i; $line[$k] !~ /$An/; $k++) {}
                if($k == $j) {
                    $N1 =~ s/^\+//; $N2 =~ y/+/-/;
                    $line[$i] =~ s/_regs(\+?)$N1/$An@\($N1$N2\)/g;
                    $ext = $line[$j];
                    for($k=$j; $k>$i; --$k) {$line[$k] = $line[$k-1];}
                    $line[$i] = $ext;
                    ++$num_opt;
                }
            }
        }
        #################################################################
        #   LEA     _regs+N1,An     =>  LEA     _regs+N1,An
        #   ..An not modified..         ...
        #   INST    .._regs+N2..        INST    ..(N2-N1)An..
        #################################################################
        if($line[$i] =~ /lea\s+_regs(\+\d+)?,(a[0-7])/) {
            $N1 = $1;$An = $2;
            $line[$lineno] = "rts"; # sentinel
            $tmp = "_regs|$An@[-+]|,$An|\sj.+\s|rts|(^.+:)";
            for($j = $i+1; $line[$j] !~ /$tmp/; ++$j) {}
            if($line[$j] =~ /_regs(\+\d+)?/) {
                $N2 = $1; $N1 =~ y/+/-/;
                $tmp = $N2; $tmp =~ s/[+]/\\+/;
                $line[$j] =~ s/_regs$tmp/$An@\($N2$N1\)/g;
                $line[$j] =~ s/\(\)//g;
                ++$num_opt;
            }
        }
        }
        #################################################################
        #   MOVE.x  X,Dp            =>  MOVE.x  X,Dp
        #   ..Dp not used..             MOVE.W  CCR,Y
        #   TST.x   Dp                  ..Dp not used..
        #   MOVE.W  ccr,Y
        #################################################################
#        if($line[$i] =~ /mov[e]?([bwl])\s+.+,(d[0-7])$/) {
#            $ext = $1;$reg = $2;
#            $line[$lineno] = "rts"; # sentinel
#            $tmp = "$reg|rts|\sj.+\s|(^.+:)";
#            for($j = $i+1; $line[$j] !~ /$tmp/; $j++) {}
#            if(($line[$j] =~ /tst$ext\s+$reg$/)
#            && ($line[$j+1] =~ /mov[e]?w\s+ccr,/)) {
#                &delete_line($j);
#                $ext = $line[$j];
#                for($k=$j;$k>$i+1;--$k) {$line[$k] = $line[$k-1];}
#                $line[$i+1] = $ext;
#                ++$num_opt;
#            }
#        }
        #################################################################
        # General instruction scheduling optimisation:
        #   INST1   <memory op>     =>  INST1   <memory op>
        #   INST2   <memory op>         INST3   [R1,]R2
        #   INST3   [R1,]R2             INST2   <memory op>
        #################################################################
        if(($i>=2)
        && ($line[$i] =~ /\s+(([ad][0-7]),)?([ad][0-7])$/)
        && ($line[$i] !~ /^.+:/)
        && ($line[$i+1] !~ /ccr|jb[^s]/)) {
            $adr = $2?$2:"a8"; # unused register
            $reg = $3;
            study($line[$i-1]);
            study($line[$i-2]);
            if(($line[$i-1] =~ /@|_regflags/)
            && ($line[$i-2] =~ /@|_regflags/)
            && ($line[$i-1] !~ /(^[^\s]+:)|$adr|$reg|jb|ccr|lea|pea/)
            && ($line[$i-2] !~ /$adr|$reg|lea|pea/)) {
                $tmp        = $line[$i-1];
                $line[$i-1] = $line[$i];
                $line[$i]   = $tmp;
                ++$num_opt;
            }
        }
    }
    return $num_opt-$opt;
}

###############################################################################

sub not_used {
    local($i, $reg) = @_;

    while($i < $lineno) {
        $line[$i]=~/$reg/ && return 0;
        ++$i;
    }
    return 1;
}

###############################################################################

sub not_used_before {
    local($i, $reg) = @_;

    while($i--) {
        $line[$i]=~/$reg/ && return 0;
    }
    return 1;
}

###############################################################################

sub analyse_function {
    $lineno = 0;
    while(<>) {
        next if /^#(NO_)?APP/;
        last if /^_op_[0-9a-f]+:$/;
        $line[$lineno++] = $_;
    }
    return $_;
}

###############################################################################

sub dump_function {
    local($i);
    for($i=0;$i < $lineno; ++$i) {
        print $line[$i];
    }
}

###############################################################################

sub usage {
    open(FILE,__FILE__);
    open(OUT,"|less");
    <FILE>;
    while(<FILE>) {
        last if !/^#/;
        s/^#[ ]?//;
        s/\$0/$0/;
        print OUT;
    }
    close OUT;
    exit;
}
    
###############################################################################
    
