# Optimizer for 1forth
# Note that this can rearrange comments in a confusing way
#
# Note that some optimizations are done in the compiler itself
# for example:
# Literal macros
# Single word macros
# tail end call to jump conversion
# "fall through" at tail end jump
#
# This optimizer does:
# replace PUSH x/POP y with MOV x,y
# replace 

# Limitations
# If a label appears between push/pop the optimize is aborted even
# though if it is only "fall through" (and the only one) you could optimize it out

# Also, we don't "unwind" that is:
#  push 10
#  push 20
#  pop x
#  pop y
# could be done as move 20->x and 10->y but we only "catch" the
# innermost optimization

BEGIN {
      opt_rcall1=1
      opt_rcall2=1   # this is the one that is buggered but I can't tell why
      opt_jmp=1
      opt_pushpop=1
      }


# strip labels and put on a line by themselves
/^[ \t]*[^: \t]+:/ { 
    label=$1; 
    $1=""; 
    if (save!="") {
      print "\t" save
      save="";
      }
      if (RCALLPEND!="") {
        print "\t" RCALLPEND
	RCALLPEND=""
    }
    if (PENDJUMP!="") {
      print "\t" PENDJUMP
      PENDJUMP=""
      }

    if (PENDCALL!="") {
       print "\t" PENDCALL
       PENDCALL=""
       }
    print label; 
    }

  { sub(/^[ \t]+/,""); }  # trim excess leading space



# Check for old RCALLs followed by return
RCALLPEND!="" {
  if ($1=="RETURN" && label=="") {
     sub(/FPC_CALL/,"FPC",RCALLPEND)
     print "\t" RCALLPEND " ; hidden return (opt)"
     RCALLPEND=""
     next;
     }
# twas nothing
     if (save!="") {
     	print "\t" save;
	save="";
       }	
  print "\t" RCALLPEND
  RCALLPEND=""
}

# check for jumps followed by return
PENDJUMP!="" {

          if (save!="") {
	      	print "\t" save;
		save="";
		 }	
	     print "\t" PENDJUMP
	     PENDJUMP=""
	     if ($1=="RETURN" && label=="") { 
	     	print "; return optimized out" 
		next; 
		}
}

# check for CALLs followed by return
PENDCALL!="" {
   if ($1=="RETURN" && label=="") {
     sub(/CALL/,"JMP",PENDCALL)
     if (save!="") {
        print "\t" save;
	save="";
 	}
     print "\t" PENDCALL " ; hidden return (opt)"
     PENDCALL="";
     next;
     }
  else 
    {
     if (save!="") {
       print "\t" save;
       save="";
       }
     print "\t" PENDCALL
    PENDCALL=""
    }

}


# ignore strings
/^[ \t]*STRING[ \t]/ { print $0; next; }


/^[ \t]*;/ { print; next; }  # comment

  { label=""; }


# Fix up old style CALLR
/MOV[ \t]+R\([^,]+,FPC_CALL/ {
  if (opt_rcall1) {
    RCALLPEND=$0
    next;
    }
}

$1=="JMP" || $1=="JMPQ" || $1=="JMPR" {
	  if (opt_jmp) {
	  	  PENDJUMP=$0
		   next;
		   }
}


$1=="CALL" || $1=="CALLQ" || $1=="CALLR" {
	   if (opt_rcall2) {
	     PENDCALL=$0
	     next;
	     }
}



# if its a push, save until we see if the next is a pop
/MOV[ \t].*,[ \t]*FDSTK_PUSH/     {
   if (opt_pushpop) {
      if (save!="") print "\t" save
         save=$0
	 savel=label;
	 next;   
      }
   }

/MOV .*FDSTK_POP[ \t]*,/  {
     if (save!="") {
       if (label=="") {
              # parse the push pop and generate a single MOV
       	      split(save,src,/[ \t,]+/);
	      split($0,dst,/[ \t,]+/);
	      print "\tMOV " src[2] "," dst[3] " ; (optimized push/pop) "
	      } else {
	      print "\t" save;
	      print "\t" $0
	      }
       save="";
       next;
       }
}


     {
     if (save!="") {
     	print "\t" save;
	save="";
       }	
     print "\t" $0;
}
