(* 
 * Note, this file contains conditional pattern matching rules.
 * You'll have to run it thru the tool wheregen 
 * (source for this is in the directory Tools/WhereGen) 
 * to generate the output.
 * 
 * -- Allen
 *)

local
 
    (* ew! *)
   structure I =  
   struct
     include "amd64Instr.sml" (* import instruction definitions *)
   end

in

functor AMD64Peephole
   (structure Instr : AMD64INSTR
    structure Eval  : MLTREE_EVAL
      sharing Instr.T = Eval.T
   ) : PEEPHOLE =
struct
   structure I = Instr
   structure C = I.C
   structure CBase = CellsBasis

   (* IMPORTANT: instructions are given in forward order *)
   fun peephole instrs =
   let fun isStackPtr(I.Direct (_, r)) = CBase.sameColor(r, C.rsp)
         | isStackPtr _ = false

       fun isZeroLE le = (Eval.valueOf le = 0) handle _ => false

       fun isZero(I.Immed n) = n = 0
         | isZero(I.ImmedLabel le) = isZeroLE le
         | isZero _ = false

       fun isZeroOpt NONE = true
         | isZeroOpt (SOME opn) = isZero opn

       fun loop(code, instrs) = 
           (case code of
             [] => instrs

              (* x <- x +/- 0; 
               *)
           | I.INSTR(I.BINARY{binOp=(I.ADDL | I.SUBL | I.ADDQ | I.SUBQ),
                              src=I.ImmedLabel le, ...})::rest 
               where isZeroLE le => loop(rest, instrs)

              (* remove lea 0(r), r *)
           | I.INSTR(I.LEAL{r32, 
                        addr=I.Displace{base, disp=I.ImmedLabel le,...}})::rest
                where (isZeroLE le) andalso 
                      CBase.sameColor(r32,base) => loop(rest, instrs)

           | I.INSTR(I.LEAQ{r64, 
                        addr=I.Displace{base, disp=I.ImmedLabel le,...}})::rest
                where (isZeroLE le) andalso 
                      CBase.sameColor(r64,base) => loop(rest, instrs)

             (*   addq n, %rsp; subq m, %rsp
              * => addq (n-m), %rsp     ;; when m < n
              * => -                    ;; when m = n
              * => subq (m-n), %rsp     ;; when m > n
              *)
           | I.INSTR(I.BINARY{binOp=I.ADDQ, src=I.Immed n, dst=I.Direct (_, d_i)})::
             I.INSTR(I.BINARY{binOp=I.SUBQ, src=I.Immed m, dst=I.Direct (ty, d_j)})::
             rest
                where CBase.sameColor(d_i, C.rsp) andalso
                      CBase.sameColor(d_j, C.rsp) =>
                  if (m = n) then loop (rest, instrs)
                  else if (m < n) then 
                    loop(rest,
                         I.binary{binOp=I.ADDQ, src=I.Immed(n-m), 
                                  dst=I.Direct(ty, C.rsp)}::instrs)
                  else 
                    loop(rest,
                         I.binary{binOp=I.SUBQ, src=I.Immed(m-n), 
                                  dst=I.Direct(ty, C.rsp)}::instrs)

             (* push folding:
              *   subq 4, %rsp
              *   movq src, 0(%rsp)  (where src <> %rsp !!! )
              * => 
              *   pushq src
              *)
           | I.INSTR(I.BINARY{binOp=I.SUBQ,src=I.Immed 4,dst=I.Direct (_, dst_i)}):: 
             I.INSTR(I.MOVE{mvOp=I.MOVQ,src,
                            dst=I.Displace{base,disp=I.Immed 0,...}})
             ::rest 
               where CBase.sameColor(base, C.rsp) andalso
                     CBase.sameColor(dst_i, C.rsp) andalso
                     not(isStackPtr src) =>
               loop(rest, I.pushq src::instrs)
               
             (* pop folding: 
              *   movq 0(%rsp), dst   (where dst <> %rsp!!!!)
              *   addq 4, %rsp
              * => 
              *   popq dst
              *)
           | I.INSTR(I.MOVE{mvOp=I.MOVQ, 
                            src=I.Displace{base, disp=I.Immed 0, ...}, dst})::
             I.INSTR(I.BINARY{binOp=I.ADDQ, src=I.Immed 4, 
                              dst=I.Direct (_, dst_i)}):: 
             rest 
               where CBase.sameColor(base, C.rsp) andalso
                     CBase.sameColor(dst_i,C.rsp) andalso
                     not(isStackPtr dst) =>
               loop(rest, I.pop dst::instrs)

           | I.INSTR(I.MOVE{mvOp=(I.MOVQ|I.MOVL), src, dst as I.Direct _})::rest 
              where isZero src => 
                loop(rest, I.binary{binOp=I.XORL, src=dst, dst=dst}::instrs)

           | i::rest => loop(rest, i::instrs)
           )
   in  loop(instrs, [])
   end
end 

end
