;;;-*-Mode: LISP; Package: ccl -*-
;;;
;;;   Copyright (C) 2008-2009 Clozure Associates and contributors
;;;   This file is part of Clozure CL.  
;;;
;;;   Clozure CL is licensed under the terms of the Lisp Lesser GNU Public
;;;   License , known as the LLGPL and distributed with Clozure CL as the
;;;   file "LICENSE".  The LLGPL consists of a preamble and the LGPL,
;;;   which is distributed with Clozure CL as the file "LGPL".  Where these
;;;   conflict, the preamble takes precedence.  
;;;
;;;   Clozure CL is referenced in the preamble as the "LIBRARY."
;;;
;;;   The LLGPL is also available online at
;;;   http://opensource.franz.com/preamble.html

;;; Shared compiler backend utilities and infrastructure.

(in-package "CCL")


(defun nx2-bigger-cdr-than (x y)
  (declare (cons x y))
  (> (cdr x) (cdr y)))


;;; Return an unordered list of "varsets": each var in a varset can be
;;; assigned a register and all vars in a varset can be assigned the
;;; same register (e.g., no scope conflicts.)

(defun nx2-partition-vars (vars inherited-vars &optional (afunc-flags 0))
  (labels ((var-weight (var)
             (let* ((bits (nx-var-bits var)))
               (declare (fixnum bits))
               (if (eql 0 (logand bits (logior
                                        (ash 1 $vbitpuntable)
                                        (ash -1 $vbitspecial)
                                        (ash 1 $vbitnoreg))))
                 (if (or (var-nvr var)  ; already assiged a register via other means
                         (eql (logior (ash 1 $vbitclosed) (ash 1 $vbitsetq))
                              (logand bits (logior (ash 1 $vbitclosed) (ash 1 $vbitsetq)))))
                   0
                   (let* ((w (var-refs var)))
                     (if (logbitp $fbittailcallsself afunc-flags)
                       (ash w 2)
                       w)))
                 0)))
           (sum-weights (varlist) 
             (let ((sum 0))
               (dolist (v varlist sum) (incf sum (var-weight v)))))
           (vars-disjoint-p (v1 v2)
             (if (eq v1 v2)
               nil
               (if (memq v1 (var-binding-info v2))
                 nil
                 (if (memq v2 (var-binding-info v1))
                   nil
                   t)))))
    (dolist (iv inherited-vars)
      (dolist (v vars) (pushnew iv (var-binding-info v) :test #'eq))
      (push iv vars))
    (setq vars (%sort-list-no-key
                ;;(delete-if #'(lambda (v) (eql (var-weight v) 0)) vars) 
                (do* ((handle (cons nil vars))
                      (splice handle))
                     ((null (cdr splice)) (cdr handle))                  
                  (declare (dynamic-extent handle) (type cons handle splice))
                  (if (eql 0 (var-weight (%car (cdr splice))))
                    (rplacd splice (%cdr (cdr splice)))
                    (setq splice (cdr splice))))
                #'(lambda (v1 v2) (%i> (var-weight v1) (var-weight v2)))))
    ;; This isn't optimal.  It partitions all register-allocatable
    ;; variables into sets such that 1) no variable is a member of
    ;; more than one set and 2) all variables in a given set are
    ;; disjoint from each other A set might have exactly one member.
    ;; If a register is allocated for any member of a set, it's
    ;; allocated for all members of that set.
    (let* ((varsets nil))
      (do* ((all vars (cdr all)))
           ((null all))
        (let* ((var (car all)))
          (when (dolist (already varsets t)
                  (when (memq var (car already)) (return)))
            (let* ((varset (cons var nil)))
              (dolist (v (cdr all))
                (when (dolist (already varsets t)
                        (when (memq v (car already)) (return)))
                  (when (dolist (d varset t)
                          (unless (vars-disjoint-p v d) (return)))
                    (push v varset))))
              (let* ((weight (sum-weights varset)))
                (declare (fixnum weight))
                (if (>= weight 3)
                  (push (cons (nreverse varset) weight) varsets)))))))
      varsets)))

;;; Maybe globally allocate registers to symbols naming functions & variables,
;;; and to simple lexical variables.
(defun nx2-afunc-allocate-global-registers (afunc nvrs)
  (let* ((vcells (afunc-vcells afunc))
         (fcells (afunc-fcells afunc))
         (all-vars (afunc-all-vars afunc))
         (inherited-vars (afunc-inherited-vars afunc)))
    (if (null nvrs)
      (progn
        (dolist (c fcells) (%rplacd c nil))
        (dolist (c vcells) (%rplacd c nil))
        (values 0 nil))
      (let* ((maybe (nx2-partition-vars
                     all-vars
                     inherited-vars
                     (afunc-bits afunc))))
        (dolist (c fcells) 
          (if (>= (the fixnum (cdr c)) 3) (push c maybe)))
        (dolist (c vcells) 
          (if (>= (the fixnum (cdr c)) 3) (push c maybe)))
        (do* ((things (%sort-list-no-key maybe #'nx2-bigger-cdr-than) (cdr things))
              (n 0 (1+ n))
              (registers nvrs)
              (regno (pop registers) (pop registers))
              (constant-alist ()))
             ((or (null things) (null regno))
              (dolist (cell fcells) (%rplacd cell nil))
              (dolist (cell vcells) (%rplacd cell nil))
              (values n constant-alist))
          (declare (list things)
                   (fixnum n regno))
          (let* ((thing (car things)))
            (if (or (memq thing fcells)
                    (memq thing vcells))
              (push (cons thing regno) constant-alist)
              (dolist (var (car thing))
                (setf (var-nvr var) regno)))))))))

(defun nx2-assign-register-var (v)
  (var-nvr v))

(defun nx2-select-fpr-candidates (vars &optional restricted)
  (let* ((fvars ()))
    (dolist (v vars (%sort-list-no-key (nx2-partition-vars fvars nil)
                                       #'nx2-bigger-cdr-than))
      (unless (member v restricted :test #'eq)
        (let* ((bits (nx-var-bits v)))
          (declare (fixnum bits))
          (when (eql 0 (logand bits (logior 
                                     (ash 1 $vbitpuntable)
                                     (ash -1 $vbitspecial)
                                     (ash 1 $vbitnoreg)
                                     (ash 1 $vbitdynamicextent)
                                     (ash 1 $vbitclosed))))
            (if (logbitp $vbitsetq bits)
              (setf (var-refs v) (ash (var-refs v) 2))
              (unless (var-declared-unboxed-type v)
                (let* ((inittype (var-inittype v)))
                  (when inittype
                    (if (subtypep inittype 'double-float)
                      (setf (var-declared-unboxed-type v) 'double-float)
                      (if (subtypep inittype 'single-float)
                        (setf (var-declared-unboxed-type v) 'single-float)))))))
            (let* ((type (var-declared-unboxed-type v)))
              (when (and (or (eq type 'single-float)
                             (eq type 'double-float))
                         (logbitp $vbitsetq bits))
                (push v fvars)))))))))
          
                
              

(defun nx2-constant-form-p (form)
  (setq form (nx-untyped-form form))
  (if form
    (or (nx-null form)
        (nx-t form)
        (and (consp form)
             (or (eq (acode-operator form) (%nx1-operator immediate))
                 (eq (acode-operator form) (%nx1-operator fixnum))
                 (eq (acode-operator form) (%nx1-operator simple-function)))))))

(defun nx2-lexical-reference-p (form)
  (when (acode-p form)
    (let ((op (acode-operator (setq form (acode-unwrapped-form-value form)))))
      (when (or (eq op (%nx1-operator lexical-reference))
                (eq op (%nx1-operator inherited-arg)))
        (%cadr form)))))

(defun nx2-acode-call-p (form)
  (when (acode-p form)
    (let ((op (acode-operator (acode-unwrapped-form-value form))))
      (or (eq op (%nx1-operator multiple-value-call))
          (eq op (%nx1-operator call))
          (eq op (%nx1-operator lexical-function-call))
          (eq op (%nx1-operator self-call))
          (eq op (%nx1-operator builtin-call))))))
          
  

;;; Returns true iff lexical variable VAR isn't setq'ed in FORM.
;;; Punts a lot ...
(defun nx2-var-not-set-by-form-p (var form)
  (let* ((bits (nx-var-bits var)))
    (or (not (%ilogbitp $vbitsetq bits))
        (nx2-setqed-var-not-set-by-form-p var form (logbitp $vbitclosed bits)))))

(defun nx2-setqed-var-not-set-by-form-p (var form &optional closed)
  (setq form (acode-unwrapped-form form))
  (or (atom form)
      (nx2-constant-form-p form)
      (nx2-lexical-reference-p form)
      (let ((op (acode-operator form))
            (subforms nil))
        (if (eq op (%nx1-operator setq-lexical))
          (and (neq var (cadr form))
               (nx2-setqed-var-not-set-by-form-p var (caddr form)))
          (and (or (not closed)
                   (logbitp operator-side-effect-free-bit op))
               (flet ((not-set-in-formlist (formlist)
                        (dolist (subform formlist t)
                          (unless (nx2-setqed-var-not-set-by-form-p var subform closed) (return)))))
                 (if
                   (cond ((%ilogbitp operator-acode-subforms-bit op) (setq subforms (%cdr form)))
                         ((%ilogbitp operator-acode-list-bit op) (setq subforms (cadr form))))
                   (not-set-in-formlist subforms)
                   (and (or (eq op (%nx1-operator call))
                            (eq op (%nx1-operator lexical-function-call)))
                        (nx2-setqed-var-not-set-by-form-p var (cadr form))
                        (setq subforms (caddr form))
                        (not-set-in-formlist (car subforms))
                        (not-set-in-formlist (cadr subforms))))))))))

(defun nx2-var-not-reffed-by-form-p (var form &optional closed)
  (setq form (acode-unwrapped-form form))
  (unless (eq var (nx2-lexical-reference-p form))
    (or (atom form)
        (nx2-lexical-reference-p form)  ;not us
        (nx2-constant-form-p form)
        (let ((op (acode-operator form))
              (subforms nil))
          (if (eq op (%nx1-operator setq-lexical))
            (and (neq var (cadr form))
                 (nx2-var-not-reffed-by-form-p var (caddr form)))
            (and (or (not closed)
                     (logbitp operator-side-effect-free-bit op))
                 (flet ((not-reffed-in-formlist (formlist)
                          (dolist (subform formlist t)
                            (unless (nx2-var-not-reffed-by-form-p var subform closed) (return)))))
                   (if
                     (cond ((%ilogbitp operator-acode-subforms-bit op) (setq subforms (%cdr form)))
                           ((%ilogbitp operator-acode-list-bit op) (setq subforms (cadr form))))
                     (not-reffed-in-formlist subforms)
                     (and (or (eq op (%nx1-operator call))
                              (eq op (%nx1-operator lexical-function-call)))
                          (nx2-var-not-reffed-by-form-p var (cadr form))
                          (setq subforms (caddr form))
                          (not-reffed-in-formlist (car subforms))
                          (not-reffed-in-formlist (cadr subforms)))))))))))

(defun nx2-node-gpr-p (reg)
  (and reg
       (eql (hard-regspec-class reg) hard-reg-class-gpr)
       (eql (get-regspec-mode reg) hard-reg-class-gpr-mode-node)))

;;; ENTRIES is a list of recorded-symbol entries, built by pushing
;;; info for each variable referenced by the function AFUNC as it
;;; comes into scope.  (Inherited variables "come into scope" before
;;; anything else, then required arguments, etc.)  Supplied-p variables
;;; may come into scope before "real" arglist entries do, which confuses
;;; functions that try to construct a function's arglist from the symbol
;;; map.  I -think- that confusion only exists when supplied-p variables
;;; are involved, so this returns its first argument unless they are;
;;; otherwise, it ensures that all toplevel arglist symbols are followed
;;; only by any inherited variables, and that the arglist symbols are
;;; in the correct (reversed) order
(defun nx2-recorded-symbols-in-arglist-order (entries afunc)
  (let* ((alambda (afunc-acode afunc)))
    (when (and (acode-p alambda)
               (eq (acode-operator alambda) (%nx1-operator lambda-list)))
      (destructuring-bind (req opt rest keys &rest ignore) (cdr alambda)
        (declare (ignore ignore))
        (when (or (dolist (sp (caddr opt))
                    (when sp (return t)))
                  (dolist (sp (caddr keys))
                    (when sp (return t))))
          (let* ((new ()))
            (flet ((info-for-var (var)
                     (assoc var entries :test #'eq)))
              (flet ((add-new-info (var)
                       (let* ((info (info-for-var var)))
                         (when info
                           (push info new)))))
                (setq entries (nreverse entries))
                (dolist (var (afunc-inherited-vars afunc))
                  (add-new-info var))
                (dolist (r req)
                  (add-new-info r))
                (dolist (o (car opt))
                  (add-new-info o))
                (when (consp rest)
                  (setq rest (car rest)))
                (when rest
                  (add-new-info rest))
                (dolist (k (cadr keys))
                  (add-new-info k))
                (dolist (e entries)
                  (unless (member e new :test #'eq)
                    (push e new)))
                (setq entries new)))))))
    entries))

(defun nx2-replace-var-refs (var value)
  (when (acode-p value)
    (let* ((op (acode-operator value))
           (operands (acode-operands value)))
      (when (typep op 'fixnum)
        (dolist (ref (var-ref-forms var) (setf (var-ref-forms var) nil))
          (when (acode-p ref)
            (setf (acode-operator ref) op
                  (acode-operands ref) operands)))))))

(defun acode-immediate-operand (x)
  (let* ((x (acode-unwrapped-form x)))
    (if (eq (acode-operator x) (%nx1-operator immediate))
      (cadr x)
      (compiler-bug "not an immediate: ~s" x))))

(defun nx2-constant-index-ok-for-type-keyword (idx keyword)
  (when (>= idx 0)
    (let* ((arch (backend-target-arch *target-backend*))
           (limit
            (case keyword
              ((:bignum 
                :single-float 
                :double-float 
                :xcode-vector
                :signed-32-bit-vector 
                :unsigned-32-bit-vector 
                :single-float-vector 
                :simple-string)
               (arch::target-max-32-bit-constant-index arch))
              (:bit-vector (arch::target-max-1-bit-constant-index arch))
              ((:signed-8-bit-vector :unsigned-8-bit-vector)
               (arch::target-max-8-bit-constant-index arch))
              ((:signed-16-bit-vector :unsigned-16-bit-vector)
               (arch::target-max-16-bit-constant-index arch))
              ((:signed-64-bit-vector 
                :unsigned-64-bit-vector 
                :double-float-vector)
               (arch::target-max-64-bit-constant-index arch))
              (t
               ;; :fixnum or node
               (target-word-size-case
                (32 (arch::target-max-32-bit-constant-index arch))
                (64 (arch::target-max-64-bit-constant-index arch)))))))
      (and limit (< idx limit)))))

(defun backend-use-operator (op seg vreg xfer &rest forms)
  (declare (dynamic-extent forms))
  (apply (svref (backend-p2-dispatch *target-backend*)
                (%ilogand op operator-id-mask))
         seg vreg xfer forms))

(defun backend-apply-acode (acode seg vreg xfer)
  (apply (svref (backend-p2-dispatch *target-backend*)
                (%ilogand (acode-operator acode) operator-id-mask))
         seg vreg xfer (acode-operands acode)))


(defun acode-constant-p (form)
  ;; This returns (values constant-value constantp); some code
  ;; may need to check constantp if constant-value is nil.
  (let* ((form (acode-unwrapped-form-value form))
         (op (if (acode-p form) (acode-operator form))))
    (cond ((eql op (%nx1-operator nil))
           (values nil t))
          ((eql op (%nx1-operator t))
           (values t t))
          ((or (eql op (%nx1-operator fixnum))
               (eql op (%nx1-operator immediate)))
           (values (cadr form) t))
          (t (values nil nil)))))

(defun acode-constant-fold-binop (seg vreg xfer x y function)
  (multiple-value-bind (const-x x-p) (acode-constant-p x)
    (when x-p
      (multiple-value-bind (const-y y-p) (acode-constant-p y)
        (when y-p
          (let* ((result (ignore-errors (funcall function const-x const-y))))
            (when result
              (backend-use-operator (if (nx1-target-fixnump result)
                                      (%nx1-operator fixnum)
                                      (%nx1-operator immediate))
                                    seg
                                    vreg
                                    xfer
                                    result)
              t)))))))

;;; Return non-nil iff we can do something better than a subprim call
;;; to .SPbuiltin-ash.
(defun acode-optimize-ash (seg vreg xfer num amt trust-decls &optional (result-type 'integer))
  (let* ((unsigned-natural-type *nx-target-natural-type*)
         (target-fixnum-type *nx-target-fixnum-type*)
         (max (target-word-size-case (32 32) (64 64)))
         (maxbits (target-word-size-case
                   (32 29)
                   (64 60)))
         (const-num (acode-integer-form-p num))
         (const-amt (acode-integer-form-p amt))
         (shifted (and const-num const-amt (ash const-num const-amt))))
    (cond (shifted
           (if (nx1-target-fixnump shifted)
             (backend-use-operator (%nx1-operator fixnum) seg vreg xfer shifted)
             (backend-use-operator (%nx1-operator immediate) seg vreg xfer shifted))
           t)
          ((eql const-amt 0)
           (backend-use-operator (%nx1-operator require-integer) seg vreg xfer num)
           t)
          ((and (fixnump const-amt) (< const-amt 0))
           (if (acode-form-typep num target-fixnum-type trust-decls)
             (progn
               (backend-use-operator (%nx1-operator %iasr)
                                     seg
                                     vreg
                                     xfer
                                     (make-acode (%nx1-operator fixnum)
                                                 (- const-amt))
                                     num)
               t)
             (if (acode-form-typep num unsigned-natural-type trust-decls)
               (progn
                 (if (< (- const-amt) max)
                   (backend-use-operator (%nx1-operator natural-shift-right)
                                         seg
                                         vreg
                                         xfer
                                         num
                                         (make-acode (%nx1-operator fixnum)
                                                   (- const-amt)))
                   (progn
                     (backend-use-operator (target-word-size-case
                                            (32 (%nx1-operator require-u32))
                                            (64 (%nx1-operator require-u64)))
                                           seg
                                           nil
                                           nil
                                           num)
                     (backend-use-operator (%nx1-operator fixnum)
                                           seg
                                           vreg
                                           xfer
                                           0)))
                 t))))
          ((and (fixnump const-amt)
                (<= 0 const-amt maxbits)
                (or (acode-form-typep num `(signed-byte ,(- (1+ maxbits) const-amt)) trust-decls)
                      (and (acode-form-typep num 'fixnum trust-decls)
                           trust-decls
                           (subtypep result-type 'fixnum))))
           (progn
             (backend-use-operator (%nx1-operator %ilsl)
                                   seg
                                   vreg
                                   xfer
                                   (make-acode (%nx1-operator fixnum)
                                               const-amt)
                                   num)
             t))
          ((and (fixnump const-amt)
                (< 0 const-amt max)
                (acode-form-typep num unsigned-natural-type trust-decls)
                trust-decls
                (subtypep result-type unsigned-natural-type))
           (backend-use-operator (%nx1-operator natural-shift-left)
                                 seg
                                 vreg
                                 xfer
                                 num
                                 amt)
           t)
          ((typep const-num target-fixnum-type)
           (let* ((field-width (1+ (integer-length const-num)))
                    ;; num fits in a `(signed-byte ,field-width)
                    (max-shift (- (1+ maxbits) field-width)))
               (when (acode-form-typep amt `(mod ,(1+ max-shift)) trust-decls)
                 (backend-use-operator (%nx1-operator %ilsl)
                                       seg
                                       vreg
                                       xfer
                                       amt
                                       (make-acode (%nx1-operator fixnum)
                                                   const-num))
                 t)))
          (t
           (let* ((numtype (specifier-type (acode-form-type num trust-decls)))
                  (amttype (specifier-type (acode-form-type amt trust-decls)))
                  (fixtype (specifier-type target-fixnum-type)))
             (if (and (typep numtype 'numeric-ctype)
                      (csubtypep numtype fixtype)
                      (typep amttype 'numeric-ctype)
                      (csubtypep amttype fixtype))
               (let* ((highnum (numeric-ctype-high numtype))
                      (lownum (numeric-ctype-low numtype))
                      (widenum (if (> (integer-length highnum)
                                      (integer-length lownum))
                                 highnum
                                 lownum))
                      (maxleft (numeric-ctype-high amttype)))
                 (when (and (>= (numeric-ctype-low amttype)
                                (target-word-size-case
                                 (32 -31)
                                 (64 -63)))
                            (< maxleft
                               (arch::target-nbits-in-word (backend-target-arch *target-backend*)))
                            (typep (ignore-errors (ash widenum maxleft))
                                   target-fixnum-type))
                   (backend-use-operator (%nx1-operator fixnum-ash)
                                         seg
                                         vreg
                                         xfer
                                         num
                                         amt)
                   t))))))))




(defun acode-optimize-logand2 (seg vreg xfer num1 num2 trust-decls &optional (result-type 'integer))
  (declare (ignore result-type))        ;see below
  (or (acode-constant-fold-binop seg vreg xfer num1 num2 'logand)
      (let* ((unsigned-natural-type *nx-target-natural-type*)
             (target-fixnum-type *nx-target-fixnum-type*))
        (cond ((eql (acode-fixnum-form-p num1) -1)
               (backend-use-operator (%nx1-operator require-integer)
                                     seg
                                     vreg
                                     xfer
                                     num2)
               t)
              ((eql (acode-fixnum-form-p num2) -1)
               (backend-use-operator (%nx1-operator require-integer)
                                     seg
                                     vreg
                                     xfer
                                     num1)
               t)
              ((and (acode-form-typep num1 target-fixnum-type trust-decls)
                    (acode-form-typep num2 target-fixnum-type trust-decls))
               (backend-use-operator (%nx1-operator %ilogand2)
                                     seg
                                     vreg
                                     xfer
                                     num1
                                     num2)
               t)
              ((and (acode-form-typep num1 unsigned-natural-type trust-decls)
                    (acode-form-typep num2 unsigned-natural-type trust-decls))
               (backend-use-operator (%nx1-operator %natural-logand)
                                     seg
                                     vreg
                                     xfer
                                     num1
                                     num2)
               t)
              ;; LOGAND of a natural integer N and a signed integer
              ;; is a natural integer <= N, and there may be cases
              ;; where we want to truncate a larger result to the
              ;; machine word size based on the result type.  Later.
              (t nil)))))

(defun acode-optimize-logior2 (seg vreg xfer num1 num2 trust-decls &optional (result-type 'integer))
  (declare (ignorable result-type))
  (or (acode-constant-fold-binop seg vreg xfer num1 num2 'logior)
      (let* ((unsigned-natural-type *nx-target-natural-type*)
             (target-fixnum-type *nx-target-fixnum-type*))
        (cond ((eql (acode-fixnum-form-p num1) 0)
               (backend-use-operator (%nx1-operator require-integer)
                                     seg
                                     vreg
                                     xfer
                                     num2)
               t)
              ((eql (acode-fixnum-form-p num2) 0)
               (backend-use-operator (%nx1-operator require-integer)
                                     seg
                                     vreg
                                     xfer
                                     num1)
               t)
              ((and (acode-form-typep num1 target-fixnum-type trust-decls)
                    (acode-form-typep num2 target-fixnum-type trust-decls))
               (backend-use-operator (%nx1-operator %ilogior2)
                                     seg
                                     vreg
                                     xfer
                                     num1
                                     num2)
               t)
              ((and (acode-form-typep num1 unsigned-natural-type trust-decls)
                    (acode-form-typep num2 unsigned-natural-type trust-decls))
               (backend-use-operator (%nx1-operator %natural-logior)
                                     seg
                                     vreg
                                     xfer
                                     num1
                                     num2)
               t)
              (t nil)))))

(defun acode-optimize-logxor2 (seg vreg xfer num1 num2 trust-decls &optional (result-type 'integer))
  (declare (ignorable result-type))
  (or (acode-constant-fold-binop seg vreg xfer num1 num2 'logxor)
      (let* ((unsigned-natural-type *nx-target-natural-type*)
             (target-fixnum-type *nx-target-fixnum-type*))
        (cond ((eql (acode-fixnum-form-p num1) 0)
               (backend-use-operator (%nx1-operator require-integer)
                                     seg
                                     vreg
                                     xfer
                                     num2)
               t)
              ((eql (acode-fixnum-form-p num2) 0)
               (backend-use-operator (%nx1-operator require-integer)
                                     seg
                                     vreg
                                     xfer
                                     num1)
               t)
              ((and (acode-form-typep num1 target-fixnum-type trust-decls)
                    (acode-form-typep num2 target-fixnum-type trust-decls))
               (backend-use-operator (%nx1-operator %ilogxor2)
                                     seg
                                     vreg
                                     xfer
                                     num1
                                     num2)
               t)
              ((and (acode-form-typep num1 unsigned-natural-type trust-decls)
                    (acode-form-typep num2 unsigned-natural-type trust-decls))
               (backend-use-operator (%nx1-operator %natural-logxor)
                                     seg
                                     vreg
                                     xfer
                                     num1
                                     num2)
               t)
              (t nil)))))



(defun acode-optimize-add2 (seg vreg xfer num1 num2 trust-decls &optional (result-type 'number))
  (declare (ignorable result-type))
  (or (acode-constant-fold-binop seg vreg xfer num1 num2 '+)
      (multiple-value-bind (num1 num2)
          (nx-binop-numeric-contagion num1 num2 trust-decls)
        (if (and (acode-form-typep num1 'double-float trust-decls)
                 (acode-form-typep num2 'double-float trust-decls))
          (progn
            (backend-use-operator (%nx1-operator %double-float+-2)
                                  seg
                                  vreg
                                  xfer
                                  num1
                                  num2)
            t)
          (if (and (acode-form-typep num1 'single-float trust-decls)
                   (acode-form-typep num2 'single-float trust-decls))
            (progn
              (backend-use-operator (%nx1-operator %short-float+-2)
                                    seg
                                    vreg
                                    xfer
                                    num1
                                    num2)
              t)
            (if (and (acode-form-typep num1 *nx-target-fixnum-type* trust-decls)
                     (acode-form-typep num2 *nx-target-fixnum-type* trust-decls))
              (progn
                (backend-use-operator (%nx1-operator %i+)
                                      seg
                                      vreg
                                      xfer
                                      num1
                                      num2
                                      t)
                t)))))))

(defun acode-optimize-sub2 (seg vreg xfer num1 num2 trust-decls &optional (result-type 'number))
  (declare (ignorable result-type))
  (or (acode-constant-fold-binop seg vreg xfer num1 num2 '-)
      (multiple-value-bind (num1 num2)
          (nx-binop-numeric-contagion num1 num2 trust-decls)
        (if (and (acode-form-typep num1 'double-float trust-decls)
                 (acode-form-typep num2 'double-float trust-decls))
          (progn
            (backend-use-operator (%nx1-operator %double-float--2)
                                  seg
                                  vreg
                                  xfer
                                  num1
                                  num2)
            t)
          (if (and (acode-form-typep num1 'single-float trust-decls)
                   (acode-form-typep num2 'single-float trust-decls))
            (progn
              (backend-use-operator (%nx1-operator %short-float--2)
                                    seg
                                    vreg
                                    xfer
                                    num1
                                    num2)
              t)
            (if (and (acode-form-typep num1 *nx-target-fixnum-type* trust-decls)
                     (acode-form-typep num2 *nx-target-fixnum-type* trust-decls))
              (progn
                (if (eql (acode-constant-p num1) 0)
                  (backend-use-operator (%nx1-operator %ineg) seg vreg xfer num2)
                  (backend-use-operator (%nx1-operator %i-)
                                        seg
                                        vreg
                                        xfer
                                        num1
                                        num2
                                        t))
                t)))))))
        

        
(defun acode-optimize-mul2 (seg vreg xfer num1 num2 trust-decls &optional (result-type 'number))
  (declare (ignorable result-type))
  (or (acode-constant-fold-binop seg vreg xfer num1 num2 '*)
      (let* ((f1 (acode-fixnum-form-p num1)))
        (when f1
          (cond ((and (eql f1 1)
                      (acode-form-typep num2 'number trust-decls))
                 (backend-apply-acode num2 seg vreg xfer)
                 t)
                ((and (eql (logcount f1) 1)
                      (> f1 0)
                      (acode-form-typep num2 *nx-target-fixnum-type* trust-decls))
                 (backend-use-operator (%nx1-operator ash)
                                       seg
                                       vreg
                                       xfer
                                       num2
                                       (make-acode (%nx1-operator fixnum)
                                                   (1- (integer-length f1))))
                 t))))
      (let* ((f2 (acode-fixnum-form-p num2)))
        (when f2
          (cond ((and (eql f2 1)
                      (acode-form-typep num1 'number trust-decls))
                 (backend-apply-acode num1 seg vreg xfer)
                 t)
                ((and (eql (logcount f2) 1)
                      (> f2 0)
                      (acode-form-typep num1 *nx-target-fixnum-type* trust-decls))
                 (backend-use-operator (%nx1-operator ash)
                                       seg
                                       vreg
                                       xfer
                                       num1
                                       (make-acode (%nx1-operator fixnum)
                                                   (1- (integer-length f2))))
                 t))))
      (multiple-value-bind (form1 form2)
          (nx-binop-numeric-contagion num1 num2 trust-decls)
        (if (and (acode-form-typep form1 'double-float trust-decls)
                 (acode-form-typep form2 'double-float trust-decls))
          (progn
            (backend-use-operator (%nx1-operator %double-float*-2)
                                  seg
                                  vreg
                                  xfer
                                  form1
                                  form2)
            t)
          (if (and (acode-form-typep form1 'single-float trust-decls)
                   (acode-form-typep form2 'single-float trust-decls))
            (progn
              (backend-use-operator (%nx1-operator %short-float*-2)
                                    seg
                                    vreg
                                    xfer
                                    form1
                                    form2)
              t))))))

(defun acode-optimize-div2 (seg vreg xfer num1 num2 trust-decls &optional (result-type 'number))
  (declare (ignorable result-type))
  (or (acode-constant-fold-binop seg vreg xfer num1 num2 '/)
      (multiple-value-bind (num1 num2)
          (nx-binop-numeric-contagion num1 num2 trust-decls)
        (if (and (acode-form-typep num1 'double-float trust-decls)
                 (acode-form-typep num2 'double-float trust-decls))
          (progn
            (backend-use-operator (%nx1-operator %double-float/-2)
                                  seg
                                  vreg
                                  xfer
                                  num1
                                  num2)
            t)
          (if (and (acode-form-typep num1 'single-float trust-decls)
                   (acode-form-typep num2 'single-float trust-decls))
            (progn
              (backend-use-operator (%nx1-operator %short-float/-2)
                                    seg
                                    vreg
                                    xfer
                                    num1
                                    num2)
              t)
            (let* ((f2 (acode-fixnum-form-p num2))
                   (unwrapped (acode-unwrapped-form num1))
                   (f1 nil)
                   (f1/f2 nil))
              (if (and f2
                       (not (zerop f2))
                       (acode-p unwrapped)
                       (or (eq (acode-operator unwrapped) (%nx1-operator mul2))
                           (eq (acode-operator unwrapped) (%nx1-operator %i*)))
                       (setq f1 (acode-fixnum-form-p (cadr unwrapped)))
                       (typep (setq f1/f2 (/ f1 f2)) 'fixnum))
                (progn
                  (backend-use-operator (%nx1-operator mul2)
                                        seg
                                        vreg
                                        xfer
                                        (make-acode (%nx1-operator fixnum) f1/f2)
                                        (caddr unwrapped))
                  t))))))))

(defun acode-optimize-numcmp (seg vreg xfer cc num1 num2 trust-decls &optional (result-type 'boolean))
  (declare (ignorable result-type))
  (let* ((c1 (acode-real-constant-p num1))
         (c2 (acode-real-constant-p num2)))

    (if (and c1 c2 (svref (backend-p2-dispatch *target-backend*) (logand (%nx1-operator nil) operator-id-mask)))
      (let* ((name (ecase (cadr cc)
                     (:eq '=-2)
                     (:ne '/=-2)
                     (:lt '<-2)
                     (:le '<=-2)
                     (:gt '>-2)
                     (:ge '>=-2))))
        (backend-use-operator (if (funcall name c1 c2) (%nx1-operator t) (%nx1-operator nil)) seg vreg xfer)
        t)
      (cond ((and (acode-form-typep num1 *nx-target-fixnum-type* trust-decls)
                  (acode-form-typep num2 *nx-target-fixnum-type* trust-decls))
             (backend-use-operator (%nx1-operator %i<>) seg vreg xfer cc num1 num2)
             t)
            ((and (acode-form-typep num1 *nx-target-natural-type* trust-decls)
                  (acode-form-typep num2 *nx-target-natural-type* trust-decls))
             (backend-use-operator (%nx1-operator %natural<>) seg vreg xfer cc num1 num2)
             t)
            ((and (acode-form-typep num1 'double-float trust-decls)
                  (acode-form-typep num2 'double-float trust-decls))
             (backend-use-operator (%nx1-operator double-float-compare) seg vreg xfer cc num1 num2)
             t)
            ((and (acode-form-typep num1 'single-float trust-decls)
                  (acode-form-typep num2 'single-float trust-decls))
             (backend-use-operator (%nx1-operator short-float-compare) seg vreg xfer cc num1 num2)
             t)))))

(defun acode-optimize-minus1 (seg vreg xfer form trust-decls &optional (result-type 'number))
  (declare (ignorable result-type))
  (multiple-value-bind (val constp) (acode-constant-p form)
    (cond ((and (and constp (ignore-errors (setq val (- val)))))
           (backend-use-operator (if (typep val *nx-target-fixnum-type*)
                                   (%nx1-operator fixnum)
                                   (%nx1-operator immediate))
                                 seg vreg xfer val)
           t)
          ((acode-form-typep form 'double-float trust-decls)
           (backend-use-operator (%nx1-operator %double-float-negate) seg vreg xfer form)
           t)
          ((acode-form-typep form 'single-float trust-decls)
           (backend-use-operator (%nx1-operator %single-float-negate) seg vreg xfer form)
           t)
          ((acode-form-typep form *nx-target-fixnum-type* trust-decls)
           (backend-use-operator (%nx1-operator %ineg) seg vreg xfer form)
           t))))

(defun nx2-is-comparison-of-var-to-fixnums (form)
  ;; Catches some cases.  May miss some.
  (flet ((is-simple-comparison-of-var-to-fixnum (form)
           (let* ((var nil)
                  (fixval nil))
             (setq form (acode-unwrapped-form form))
             (when (acode-p form)
               (let* ((op (acode-operator form)))
                 (cond ((eql op (%nx1-operator eq))
                        (destructuring-bind (cc x y) (cdr form)
                          (when (eq :eq (acode-immediate-operand cc))
                            (if (setq var (nx2-lexical-reference-p x))
                              (setq fixval (acode-fixnum-form-p y))
                              (if (setq var (nx2-lexical-reference-p y))
                                (setq fixval (acode-fixnum-form-p x)))))))
                       ((eql op (%nx1-operator %izerop))
                        (destructuring-bind (cc val) (cdr form)
                          (when (eq :eq (acode-immediate-operand cc))
                            (setq var (nx2-lexical-reference-p val)
                                  fixval 0)))))))
             (if (and var fixval)
               (values var fixval)
               (values nil nil)))))
    (setq form (acode-unwrapped-form form))
    (multiple-value-bind (var val) (is-simple-comparison-of-var-to-fixnum form)
      (if var
        (values var (list val))
        (if (and (acode-p form) (eql (acode-operator form) (%nx1-operator or)))
          (collect ((vals))
            (let* ((clauselist (cadr form)))
              (if (multiple-value-setq (var val) (is-simple-comparison-of-var-to-fixnum (car clauselist)))
                (progn
                  (vals val)
                  (dolist (clause (cdr clauselist) (values var (vals)))
                    (multiple-value-bind (var1 val1)
                        (is-simple-comparison-of-var-to-fixnum clause)
                      (unless (eq var var1)
                        (return (values nil nil)))
                      (vals val1))))
                (values nil nil)))))))))
           


                    
               
        
                
;;; If an IF form (in acode) appears to be the expansion of a
;;; CASE/ECASE/CCASE where all values are fixnums, try to recover
;;; that information and let the backend decide what to do with it.
;;; (A backend might plausibly replace a sequence of comparisons with
;;; a jumptable.)
;;; Returns 4 values: a list of lists of fixnums, the corresponding true
;;; forms for each sublist, the variable being tested, and the "otherwise"
;;; or default form.
;;; Something like (IF (EQL X 1) (FOO) (BAR)) will return non-nil values.
;;; The backend -could- generate a jump table in that case, but probably
;;; wouldn't want to.
(defun nx2-reconstruct-case (test true false)
  (multiple-value-bind (var vals) (nx2-is-comparison-of-var-to-fixnums test)
    (if (not var)
      (values nil nil nil nil)
      (collect ((ranges)
                (trueforms))
        (let* ((otherwise nil))
          (ranges vals)
          (trueforms true)
          (labels ((descend (original)
                     (let* ((form (acode-unwrapped-form original)))
                       (if (or (not (acode-p form))
                               (not (eql (acode-operator form)
                                         (%nx1-operator if))))
                         (setq otherwise original)
                         (destructuring-bind (test true false) (cdr form)
                           (multiple-value-bind (v vals)
                               (nx2-is-comparison-of-var-to-fixnums test)
                             (cond ((eq v var)
                                    (ranges vals)
                                    (trueforms true)
                                    (descend false))
                                   (t (setq otherwise original)))))))))
            (descend false))
          (values (ranges) (trueforms) var otherwise))))))
