;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;
;;;;;                        Peephole Optimizer
;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;
;;;; Based loosely on the peephole optimizer in Peter Norvig's book.
;;;;
;;;; The optimizer receives a list of code and literals. Code is a list of
;;;; instructions. Each instruction is a symbol, representing a label, or
;;;; a list of a symbol, representing an opcode, followed by numbers or
;;;; symbols.

;**** fun info; funs argument??

(in-package "XLSCMP")

(defun peephole-optimize (cl funs)
  (loop (if (not (peephole-optimize-one cl funs)) (return cl))))

(defun peephole-optimize-one (cl funs)
  (do* ((all-code (first cl))
	(code all-code (rest code))
	(instr (first code) (first code))
	(changed nil))
       ((or changed (null code)) changed)
       (setf changed
	     (cond
	      ((consp instr)
	       (simplify-instruction instr code all-code))
	      ((not (member instr funs))
	       (drop-label-if-not-used instr code all-code))))))


;;;;
;;;; Support Functions
;;;;

(defun find-target (label code)
  (dolist (c (rest (member label code)) (error "no code after ~s" label))
    (if (consp c) (return c))))

(defun drop-label-if-not-used (label code all-code)
  (when (not (find label all-code
		   :test #'(lambda (x y) (if (consp y) (member x y)))))
	(setf (first code) (second code) (rest code) (rest (rest code)))
	t))

(defun tension-test-jump (instr code all-code)
  (let ((ct (find-target (third instr) all-code))
	(at (find-target (fourth instr) all-code))
	(changed nil))
    (when (eq '%goto (first ct))
	  (setf (third instr) (second ct))
	  (setf changed t))
    (when (eq '%goto (first at))
	  (setf (fourth instr) (second at))
	  (setf changed t))
    (when (drop-dead-code instr code all-code)
	  (setf changed t))
    changed))

;;**** use loop here; is this ever called??
(defun drop-dead-code (instr code all-code)
  (when (and (consp (rest code)) (consp (second code)))
	(setf (rest code) (rest (rest code)))))

(defun short-operand-p (x) (<= 0 x 127))


;;;;
;;;; Data-Driven Instruction-Specific Optimizations
;;;;

(let ((table (make-hash-table :test 'eq)))
  (defun add-peephole-simplifier (sym fun) (push fun (gethash sym table)))
  (defun get-peephole-simplifiers (sym) (gethash sym table)))

(defun simplify-instruction (instr code all-code)
  (let ((funs (get-peephole-simplifiers (first instr))))
    (dolist (f funs)
      (when (funcall f instr code all-code)
	    (return t)))))

(defmacro define-peephole-simplifier (sym args &body body)
  `(add-peephole-simplifier ',sym #'(lambda ,args ,@body)))


;;;;
;;;; Test Jump and Goto Tensioning
;;;;

(dolist (s '(%test-1 %test-2 %test-arith-2))
  (add-peephole-simplifier s #'tension-test-jump))

(define-peephole-simplifier %goto (instr code all-code)
  (if (eq (second instr) (second code))
      (setf (first code) (second code) (rest code) (rest (rest code)))
      (let ((gt (find-target (second instr) all-code))
	    (changed nil))
	(when (and (eq '%goto (first gt)) (not (eq instr gt)))
	      (setf (second instr) (second gt))
	      (setf changed t))
	(when (drop-dead-code instr code all-code)
	      (setf changed t))
	changed)))


;;;;
;;;; Simplifiers for Other Opcodes
;;;;

;; (%initialize 0 ...) => (%initialize-0 ...)
(define-peephole-simplifier %initialize (instr code all-code)
  (when (eql 0 (second instr))
	(setf (first code) `(%initialize-0 ,@(rest (rest instr))))
	t))

;; (%set-one-value x) => (%set-one-value-return c x)
;; (%return c)
(define-peephole-simplifier %set-one-value (instr code all-code)
  (let ((next-instr (first (rest code))))
    (when (and (consp next-instr) (eq (first next-instr) '%return))
	  (setf (first code)
		`(%set-one-value-return ,(second next-instr) ,(second instr)))
	  (setf (rest code) (rest (rest code)))
	  t)))

;; (%set-values ...) => (%set-values-return c ...)
;; (%return c)
(define-peephole-simplifier %set-values (instr code all-code)
  (let ((next-instr (first (rest code))))
    (when (and (consp next-instr) (eq (first next-instr) '%return))
	  (setf (first code)
		`(%set-values-return ,(second next-instr) ,@(rest instr)))
	  (setf (rest code) (rest (rest code)))
	  t)))

;; (%set-values-list x y) => (%set-values-list-return c x y)
;; (%return c)
(define-peephole-simplifier %set-values-list (instr code all-code)
  (let ((next-instr (first (rest code))))
    (when (and (consp next-instr) (eq (first next-instr) '%return))
	  (setf (first code)
		`(%set-values-list-return ,(second next-instr)
					  ,(second instr)))
	  (setf (rest code) (rest (rest code)))
	  t)))

;; drop (%copy x x)
(define-peephole-simplifier %copy (instr code all-code)
  (when (= (second instr) (third instr))
	(setf (first code) (second code) (rest code) (rest (rest code)))
	t))

;; (%copy x z) => (%copy y z)
;; (%copy y z)
(define-peephole-simplifier %copy (instr code all-code)
  (let ((next-instr (first (rest code))))
    (when (and (consp next-instr)
	       (eq (first next-instr) '%copy)
	       (/= (third instr) (second next-instr))
	       (= (third instr) (third next-instr)))
	  (setf (first code) (second code))
	  (setf (rest code) (rest (rest code)))
	  t)))


syntax highlighted by Code2HTML, v. 0.9.1