;;; x86_64.ss
;;; Copyright 1984-2016 Cisco Systems, Inc.
;;; 
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;; 
;;; http://www.apache.org/licenses/LICENSE-2.0
;;; 
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.

;;; SECTION 1: registers
(if-feature windows
  (define-registers
    (reserved
      [%tc  %r14           #t 14]
      [%sfp %r13           #t 13]
      [%ap  %rdi           #t  7]
      #;[%esp]
      #;[%eap]
      #;[%trap])
    (allocable
      [%ac0 %rbp           #t  5]
      [%xp  %r12           #t 12]
      [%ts  %rax %Cretval  #f  0]
      [%td  %rbx           #t  3]
      [%ac1 %r10           #f 10]
      [%yp  %r11           #f 11]
      [%cp  %r15           #t 15]
      [#;%ret %rsi           #t  6]
      [     %rcx %Carg1    #f  1]
      [     %rdx %Carg2    #f  2]
      [     %r8  %Carg3    #f  8]
      [     %r9  %Carg4    #f  9])
    (machine-dependent
      [%Cfparg1 %Cfpretval #f  0]
      [%Cfparg2            #f  1]
      [%Cfparg3            #f  2]
      [%Cfparg4            #f  3]
      [%flreg1             #f  4]  ; xmm 0-5 are caller-save
      [%flreg2             #f  5]  ; xmm 6-15 are callee-save
      [%sp                 #t  4]))
  (define-registers
    (reserved
      [%tc  %r14           #t 14]
      [%sfp %r13           #t 13]
      [%ap  %r9  %Carg6    #f  9]
      #;[%esp]
      #;[%eap]
      #;[%trap])
    (allocable
      [%ac0 %rbp           #t  5]
      [%xp  %r12           #t 12]
      [%ts  %rax %Cretval  #f  0]
      [%td  %rbx           #t  3]
      [%ac1 %r10           #f 10]
      [%yp  %r11           #f 11]
      [%cp  %r15           #t 15]
      [#;%ret %r8  %Carg5    #f  8]
      [     %rdi %Carg1    #f  7]
      [     %rsi %Carg2    #f  6]
      [     %rdx %Carg3    #f  2]
      [     %rcx %Carg4    #f  1])
    (machine-dependent
      [%Cfparg1 %Cfpretval #f  0]
      [%Cfparg2            #f  1]
      [%Cfparg3            #f  2]
      [%Cfparg4            #f  3]
      [%Cfparg5            #f  4]
      [%Cfparg6            #f  5]
      [%Cfparg7            #f  6]
      [%Cfparg8            #f  7]
      [%flreg1             #f  8]
      [%flreg2             #f  9]
      [%sp                 #t  4])))

;;; SECTION 2: instructions
(module (md-handle-jump) ; also sets primitive handlers
  (import asm-module)

  (define-syntax seq
    (lambda (x)
      (syntax-case x ()
        [(_ e ... ex)
         (with-syntax ([(t ...) (generate-temporaries #'(e ...))])
           #'(let ([t e] ...)
               (with-values ex
                 (case-lambda
                   [(x*) (cons* t ... x*)]
                   [(x* p) (values (cons* t ... x*) p)]))))])))

  ; don't bother with literal@? check since lvalues can't be literals
  (define lmem? mref?)

  (define mem?
    (lambda (x)
      (or (lmem? x) (literal@? x))))

  (define real-imm32?
    (lambda (x)
      (nanopass-case (L15c Triv) x
        [(immediate ,imm)
         (constant-case ptr-bits
           [(32) #t]                   ; allows 2^31...2^32-1 per immediate?
           [(64) (signed-32? imm)])]   ; 2^31...2^32-1 aren't 32-bit values on 64-bit machines
        [else #f])))

  (define negatable-real-imm32?
    (lambda (x)
      (nanopass-case (L15c Triv) x
        [(immediate ,imm) (<= #x-7FFFFFFF imm #x7FFFFFFF)]
        [else #f])))

  (define lvalue->ur
    (lambda (x k)
      (if (mref? x)
          (let ([u (make-tmp 'u)])
            (seq
              (set-ur=mref u x)
              (k u)))
          (k x))))

  (define mref->mref
    (lambda (a k)
      (define return
        (lambda (x0 x1 imm)
          (k (with-output-language (L15d Triv) `(mref ,x0 ,x1 ,imm)))))
      (nanopass-case (L15c Triv) a
        [(mref ,lvalue0 ,lvalue1 ,imm)
         (lvalue->ur lvalue0
           (lambda (x0)
             (lvalue->ur lvalue1
               (lambda (x1)
                 (if (signed-32? imm)
                     (return x0 x1 imm)
                     (let ([u (make-tmp 'u)])
                       (seq
                         (build-set! ,u (immediate ,imm))
                         (if (eq? x1 %zero)
                             (return x0 u 0)
                             (seq
                               (build-set! ,u (asm ,null-info ,asm-add ,u ,x1))
                               (return x0 u 0))))))))))])))

  (define mem->mem
    (lambda (a k)
      (cond
        [(literal@? a)
         (let ([u (make-tmp 'u)])
           (seq
             (build-set! ,u ,(literal@->literal a))
             (k (with-output-language (L15d Lvalue) `(mref ,u ,%zero 0)))))]
        [else (mref->mref a k)])))

  (define-syntax coercible?
    (syntax-rules ()
      [(_ ?a ?aty*)
       (let ([a ?a] [aty* ?aty*])
         (or (memq 'ur aty*)
             (or (and (memq 'imm32 aty*) (imm32? a))
                 (and (memq 'imm aty*) (imm? a))
                 (and (memq 'zero aty*) (imm0? a))
                 (and (memq 'real-imm32 aty*) (real-imm32? a))
                 (and (memq 'negatable-real-imm32 aty*) (negatable-real-imm32? a))
                 (and (memq 'mem aty*) (mem? a)))))]))

  (define-syntax coerce-opnd ; passes k something compatible with aty*
    (syntax-rules ()
      [(_ ?a ?aty* ?k)
       (let ([a ?a] [aty* ?aty*] [k ?k])
         (cond
           [(and (memq 'mem aty*) (mem? a)) (mem->mem a k)]
           [(and (memq 'imm32 aty*) (imm32? a)) (k (imm->imm a))]
           [(and (memq 'imm aty*) (imm? a)) (k (imm->imm a))]
           [(and (memq 'zero aty*) (imm0? a)) (k (imm->imm a))]
           [(and (memq 'real-imm32 aty*) (real-imm32? a)) (k (imm->imm a))]
           [(and (memq 'negatable-real-imm32 aty*) (negatable-real-imm32? a)) (k (imm->imm a))]
           [(memq 'ur aty*)
            (cond
              [(ur? a) (k a)]
              [(imm? a)
               (let ([u (make-tmp 'u)])
                 (seq
                   (build-set! ,u ,(imm->imm a))
                   (k u)))]
              [(mem? a)
               (mem->mem a
                 (lambda (a)
                   (let ([u (make-tmp 'u)])
                     (seq
                       (build-set! ,u ,a)
                       (k u)))))]
              [else (sorry! 'coerce-opnd "unexpected operand ~s" a)])]
           [else (sorry! 'coerce-opnd "cannot coerce ~s to ~s" a aty*)]))]))

  (define set-ur=mref
    (lambda (ur mref)
      (mref->mref mref
        (lambda (mref)
          (build-set! ,ur ,mref)))))

  (define-who extract-imm
    (lambda (e)
      (nanopass-case (L15d Triv) e
        [(immediate ,imm) imm]
        [else (sorry! who "~s is not an immediate" e)])))

  (define md-handle-jump
    (lambda (t)
      (with-output-language (L15d Tail)
        (nanopass-case (L15c Triv) t
          [,lvalue
           (if (mem? lvalue)
               (mem->mem lvalue
                 (lambda (mref)
                   (values '() `(jump ,mref))))
               (values '() `(jump ,lvalue)))]
          [(literal ,info)
           (guard (and (not (info-literal-indirect? info))
                       (memq (info-literal-type info) '(entry library-code))))
           (values '() `(jump (literal ,info)))]
          [(label-ref ,l ,offset)
           (values '() `(jump (label-ref ,l ,offset)))]
          [else
           (let ([tmp (make-tmp 'utmp)])
             (values
               (with-output-language (L15d Effect) `(set! ,(make-live-info) ,tmp ,t))
               `(jump ,tmp)))]))))

  (define-syntax define-instruction
    (lambda (x)
      (define acsame-mem
        (lambda (c a b bty* k)
          #`(lambda (c a b)
              (if (and (lmem? c) (same? a c) (coercible? b '#,bty*))
                  (coerce-opnd b '#,bty*
                    (lambda (b)
                      (mem->mem c
                        (lambda (c)
                          (#,k c b)))))
                  (next c a b)))))

      (define-who acsame-ur
        (lambda (c a b bty* k)
          #`(lambda (c a b)
              (if (and (same? a c) (coercible? b '#,bty*))
                  (coerce-opnd b '#,bty*
                    (lambda (b)
                      (cond
                        [(ur? c) (#,k c b)]
                        [(mref? c)
                         (nanopass-case (L15c Triv) c
                           [(mref ,lvalue0 ,lvalue1 ,imm)
                           ; TODO: does this use too many registers? (no longer special casing fv x0, x1 case)
                            (lvalue->ur lvalue0
                              (lambda (x0)
                                (lvalue->ur lvalue1
                                  (lambda (x1)
                                    (let ([u1 (make-tmp 'u)])
                                      (if (signed-32? imm)
                                          (seq
                                            (build-set! ,u1 (mref ,x0 ,x1 ,imm))
                                            (#,k u1 b)
                                            (build-set! (mref ,x0 ,x1 ,imm) ,u1))
                                          (let ([u2 (make-tmp 'u)])
                                            (seq
                                              (build-set! ,u2 ,imm)
                                              (build-set! ,x1 (asm ,null-info ,asm-add ,x1 ,u2))
                                              (build-set! ,u1 (mref ,x0 ,x1 0))
                                              (#,k u1 b)
                                              (build-set! (mref ,x0 ,x1 0) ,u1)))))))))])]
                        ; can't be literal@ since literals can't be lvalues
                        [else (sorry! '#,(datum->syntax #'* who) "unexpected operand ~s" c)])))
                  (next c a b)))))

      (define make-value-clause
        (lambda (fmt)
          (syntax-case fmt (mem ur xp)
            [(op (c mem) (a ?c) (b bty* ...))
             (bound-identifier=? #'?c #'c)
             (acsame-mem #'c #'a #'b #'(bty* ...) #'(lambda (c b) (rhs c c b)))]
            [(op (c ur) (a ?c) (b bty* ...))
             (bound-identifier=? #'?c #'c)
             (acsame-ur #'c #'a #'b #'(bty* ...) #'(lambda (c b) (rhs c c b)))]
            [(op (c mem) (a aty* ...) (b ?c))
             (bound-identifier=? #'?c #'c)
             (acsame-mem #'c #'b #'a #'(aty* ...) #'(lambda (c a) (rhs c a c)))]
            [(op (c ur) (a aty* ...) (b ?c))
             (bound-identifier=? #'?c #'c)
             (acsame-ur #'c #'b #'a #'(aty* ...) #'(lambda (c a) (rhs c a c)))]
            [(op (c mem) (a aty ...) (b bty ...))
             #`(lambda (c a b)
                 (if (and (lmem? c) (coercible? a '(aty ...)) (coercible? b '(bty ...)))
                     (coerce-opnd b '(bty ...)
                       (lambda (b)
                         (coerce-opnd a '(aty ...)
                           (lambda (a)
                             (mref->mref c (lambda (c) (rhs c a b)))))))
                     (next c a b)))]
            [(op (c ur) (a aty ...) (b bty ...))
             #`(lambda (c a b)
                 (if (and (coercible? a '(aty ...)) (coercible? b '(bty ...)))
                     (coerce-opnd b '(bty ...)
                       (lambda (b)
                         (coerce-opnd a '(aty ...)
                           (lambda (a)
                             (if (ur? c)
                                 (rhs c a b)
                                 (let ([u (make-tmp 'u)])
                                   (seq
                                     (rhs u a b)
                                     (mref->mref c
                                       (lambda (c)
                                         (build-set! ,c ,u))))))))))
                     (next c a b)))]
            ; four-operand case below can require four unspillables
            [(op (c ur) (a ur) (b ur) (d dty ...))
             (not (memq 'mem (datum (dty ...))))
             #`(lambda (c a b d)
                 (if (coercible? d '(dty ...))
                     (coerce-opnd d '(dty ...)
                       (lambda (d)
                         (coerce-opnd a '(ur)
                           (lambda (a)
                             (coerce-opnd b '(ur)
                               (lambda (b)
                                 (if (ur? c)
                                     (rhs c a b d)
                                     (let ([u (make-tmp 'u)])
                                       (seq
                                         (rhs u a b d)
                                         (mref->mref c
                                           (lambda (c)
                                             (build-set! ,c ,u))))))))))))
                     (next c a b d)))]
            [(op (c mem) (a ?c))
             (bound-identifier=? #'?c #'c)
             #`(lambda (c a)
                 (if (and (lmem? c) (same? c a))
                     (mem->mem c
                       (lambda (c)
                         (rhs c c)))
                     (next c a)))]
            [(op (c ur) (a ?c))
             (bound-identifier=? #'?c #'c)
             #`(lambda (c a)
                 (if (same? a c)
                     (if (ur? c)
                         (rhs c c)
                         (mem->mem c
                           (lambda (c)
                             (let ([u (make-tmp 'u)])
                               (seq
                                 (build-set! ,u ,c)
                                 (rhs u u)
                                 (build-set! ,c ,u))))))
                     (next c a)))]
            [(op (c mem) (a aty ...))
             #`(lambda (c a)
                 (if (and (lmem? c) (coercible? a '(aty ...)))
                     (coerce-opnd a '(aty ...)
                       (lambda (a)
                         (mem->mem c
                           (lambda (c)
                             (rhs c a)))))
                     (next c a)))]
            [(op (c ur) (a aty ...))
             #`(lambda (c a)
                 (if (coercible? a '(aty ...))
                     (coerce-opnd a '(aty ...)
                       (lambda (a)
                         (if (ur? c)
                             (rhs c a)
                             (mem->mem c
                               (lambda (c)
                                 (let ([u (make-tmp 'u)])
                                   (seq
                                     (rhs u a)
                                     (build-set! ,c ,u))))))))
                     (next c a)))]
            [(op (c ur))
             #`(lambda (c)
                 (if (ur? c)
                     (rhs c)
                     (mem->mem c
                       (lambda (c)
                         (let ([u (make-tmp 'u)])
                           (seq
                             (rhs u)
                             (build-set! ,c ,u)))))))]
            [(op (c mem))
             #`(lambda (c)
                 (if (lmem? c)
                     (mem->mem c
                       (lambda (c)
                         (rhs c)))
                     (next c)))])))

      (define-who make-pred-clause
        (lambda (fmt)
          (syntax-case fmt ()
            [(op (a aty ...) ...)
             #`(lambda (a ...)
                 (if (and (coercible? a '(aty ...)) ...)
                     #,(let f ([a* #'(a ...)] [aty** #'((aty ...) ...)])
                         (if (null? a*)
                             #'(rhs a ...)
                             #`(coerce-opnd #,(car a*) '#,(car aty**)
                                 (lambda (#,(car a*)) #,(f (cdr a*) (cdr aty**))))))
                     (next a ...)))])))

      (define-who make-effect-clause
        (lambda (fmt)
          (syntax-case fmt ()
            [(op (a aty ...) ...)
             #`(lambda (a ...)
                 (if (and (coercible? a '(aty ...)) ...)
                     #,(let f ([a* #'(a ...)] [aty** #'((aty ...) ...)])
                         (if (null? a*)
                             #'(rhs a ...)
                             #`(coerce-opnd #,(car a*) '#,(car aty**)
                                 (lambda (#,(car a*)) #,(f (cdr a*) (cdr aty**))))))
                     (next a ...)))])))

      (syntax-case x (definitions)
        [(k context (sym ...) (definitions defn ...) [(op (a aty ...) ...) ?rhs0 ?rhs1 ...] ...)
         ; potentially unnecessary level of checking, but the big thing is to make sure
         ; the number of operands expected is the same on every clause of define-intruction
         (and (not (null? #'(op ...)))
              (andmap identifier? #'(sym ...))
              (andmap identifier? #'(op ...))
              (andmap identifier? #'(a ... ...))
              (andmap identifier? #'(aty ... ... ...)))
         (with-implicit (k info return with-output-language)
           (with-syntax ([((opnd* ...) . ignore) #'((a ...) ...)])
             (define make-proc
               (lambda (make-clause)
                 (let f ([op* #'(op ...)]
                         [fmt* #'((op (a aty ...) ...) ...)]
                         [arg* #'((a ...) ...)]
                         [rhs* #'((?rhs0 ?rhs1 ...) ...)])
                   (if (null? op*)
                       #'(lambda (opnd* ...)
                           (sorry! name "no match found for ~s" (list opnd* ...)))
                       #`(let ([next #,(f (cdr op*) (cdr fmt*) (cdr arg*) (cdr rhs*))]
                               [rhs (lambda #,(car arg*)
                                      (let ([#,(car op*) name])
                                        #,@(car rhs*)))])
                           #,(make-clause (car fmt*)))))))
             (unless (let ([a** #'((a ...) ...)])
                       (let* ([a* (car a**)] [len (length a*)])
                         (andmap (lambda (a*) (fx= (length a*) len)) (cdr a**))))
               (syntax-error x "mismatched instruction arities"))
             (cond
               [(free-identifier=? #'context #'value)
                #`(let ([fvalue (lambda (name)
                                  (lambda (info opnd* ...)
                                    defn ...
                                    (with-output-language (L15d Effect)
                                      (#,(make-proc make-value-clause) opnd* ...))))])
                    (begin
                      (safe-assert (eq? (primitive-type (%primitive sym)) 'value))
                      (primitive-handler-set! (%primitive sym) (fvalue 'sym)))
                    ...)]
               [(free-identifier=? #'context #'pred)
                #`(let ([fpred (lambda (name)
                                 (lambda (info opnd* ...)
                                   defn ...
                                   (with-output-language (L15d Pred)
                                     (#,(make-proc make-pred-clause) opnd* ...))))])
                    (begin
                      (safe-assert (eq? (primitive-type (%primitive sym)) 'pred))
                      (primitive-handler-set! (%primitive sym) (fpred 'sym)))
                    ...)]
               [(free-identifier=? #'context #'effect)
                #`(let ([feffect (lambda (name)
                                   (lambda (info opnd* ...)
                                     defn ...
                                     (with-output-language (L15d Effect)
                                       (#,(make-proc make-effect-clause) opnd* ...))))])
                    (begin
                      (safe-assert (eq? (primitive-type (%primitive sym)) 'effect))
                      (primitive-handler-set! (%primitive sym) (feffect 'sym)))
                    ...)]
               [else (syntax-error #'context "unrecognized context")])))]
        [(k context (sym ...) cl ...) #'(k context (sym ...) (definitions) cl ...)]
        [(k context sym cl ...) (identifier? #'sym) #'(k context (sym) (definitions) cl ...)])))

  ; x is not the same as z in any clause that follows a clause where (x z)
  ; and y is coercible to one of its types, however:
  ; WARNING: do not assume that if x isn't the same as z then x is independent
  ; of z, since x might be an mref with z as it's base or index

  (define-instruction value (-)
    [(op (z mem) (x z) (y ur imm32))
     `(set! ,(make-live-info) ,z (asm ,info ,asm-sub ,x ,y))]
    [(op (z mem) (x zero) (y z))
     `(set! ,(make-live-info) ,z (asm ,info ,asm-negate ,y))]
    [(op (z ur) (x z) (y ur mem imm32))
     `(set! ,(make-live-info) ,z (asm ,info ,asm-sub ,x ,y))]
    [(op (z ur) (x zero) (y ur))
     (seq
       `(set! ,(make-live-info) ,z ,y)
       `(set! ,(make-live-info) ,z (asm ,info ,asm-negate ,z)))]
    [(op (z ur) (x ur mem imm32) (y z))
     `(set! ,(make-live-info) ,z (asm ,info ,asm-sub-negate ,y ,x))]
    [(op (z ur) (x ur) (y negatable-real-imm32))
     (seq
       `(move-related ,z ,x)
       `(set! ,(make-live-info) ,z (asm ,info ,(asm-lea1 (- (extract-imm y))) ,x)))]
    [(op (z ur) (x mem imm32) (y ur))
     (let ([t (make-tmp 'u)])
       (seq
         `(set! ,(make-live-info) ,t ,y)
         `(set! ,(make-live-info) ,t (asm ,info ,asm-sub-negate ,t ,x))
         `(set! ,(make-live-info) ,z ,t)))]
    [(op (z ur) (x ur) (y ur mem imm32))
     (let ([t (make-tmp 'u)])
       (seq
         `(set! ,(make-live-info) ,t ,x)
         `(set! ,(make-live-info) ,t (asm ,info ,asm-sub ,t ,y))
         `(set! ,(make-live-info) ,z ,t)))])

  (define-instruction value (-/ovfl -/eq) ; must set condition codes, so can't use lea or sub-negate
    [(op (z mem) (x z) (y ur imm32))
     `(set! ,(make-live-info) ,z (asm ,info ,asm-sub ,x ,y))]
    [(op (z mem) (x zero) (y z))
     `(set! ,(make-live-info) ,z (asm ,info ,asm-negate ,y))]
    [(op (z ur) (x z) (y ur mem imm32))
     `(set! ,(make-live-info) ,z (asm ,info ,asm-sub ,x ,y))]
    [(op (z ur) (x zero) (y ur))
     (seq
       `(set! ,(make-live-info) ,z ,y)
       `(set! ,(make-live-info) ,z (asm ,info ,asm-negate ,z)))]
    [(op (z ur) (x ur) (y ur mem imm32))
     (let ([t (make-tmp 'u)])
       (seq
         `(set! ,(make-live-info) ,t ,x)
         `(set! ,(make-live-info) ,t (asm ,info ,asm-sub ,t ,y))
         `(set! ,(make-live-info) ,z ,t)))])

  (define-instruction value (+)
    [(op (z mem) (x z) (y ur imm32))
     `(set! ,(make-live-info) ,z (asm ,info ,asm-add ,z ,y))]
    [(op (z mem) (x ur imm32) (y z))
     `(set! ,(make-live-info) ,z (asm ,info ,asm-add ,z ,x))]
    [(op (z ur) (x z) (y ur mem imm32))
     `(set! ,(make-live-info) ,z (asm ,info ,asm-add ,z ,y))]
    [(op (z ur) (x ur mem imm32) (y z))
     `(set! ,(make-live-info) ,z (asm ,info ,asm-add ,z ,x))]
    [(op (z ur) (x ur) (y real-imm32))
     (seq
       `(move-related ,z ,x)
       `(set! ,(make-live-info) ,z (asm ,info ,(asm-lea1 (extract-imm y)) ,x)))]
    [(op (z ur) (x real-imm32) (y ur))
     (seq
       `(move-related ,z ,y)
       `(set! ,(make-live-info) ,z (asm ,info ,(asm-lea1 (extract-imm x)) ,y)))]
    [(op (z ur) (x ur) (y mem imm32))
     (let ([t (make-tmp 'u)])
       (seq
         `(set! ,(make-live-info) ,t ,x)
         `(set! ,(make-live-info) ,t (asm ,info ,asm-add ,t ,y))
         `(set! ,(make-live-info) ,z ,t)))]
    [(op (z ur) (x mem imm32) (y ur))
     (let ([t (make-tmp 'u)])
       (seq
         `(set! ,(make-live-info) ,t ,y)
         `(set! ,(make-live-info) ,t (asm ,info ,asm-add ,t ,x))
         `(set! ,(make-live-info) ,z ,t)))]
    [(op (z ur) (x ur) (y ur))
     (seq
       `(move-related ,z ,y)
       `(move-related ,z ,x)
       `(set! ,(make-live-info) ,z (asm ,info ,(asm-lea2 0) ,x ,y)))])

  (define-instruction value (+/ovfl +/carry) ; must set condition codes, so can't use lea
    [(op (z mem) (x z) (y ur imm32))
     `(set! ,(make-live-info) ,z (asm ,info ,asm-add ,z ,y))]
    [(op (z mem) (x ur imm32) (y z))
     `(set! ,(make-live-info) ,z (asm ,info ,asm-add ,z ,x))]
    [(op (z ur) (x z) (y ur mem imm32))
     `(set! ,(make-live-info) ,z (asm ,info ,asm-add ,z ,y))]
    [(op (z ur) (x ur mem imm32) (y z))
     `(set! ,(make-live-info) ,z (asm ,info ,asm-add ,z ,x))]
    [(op (z ur) (x ur) (y mem imm32))
     (let ([t (make-tmp 'u)])
       (seq
         `(set! ,(make-live-info) ,t ,x)
         `(set! ,(make-live-info) ,t (asm ,info ,asm-add ,t ,y))
         `(set! ,(make-live-info) ,z ,t)))]
    [(op (z ur) (x mem imm32) (y ur))
     (let ([t (make-tmp 'u)])
       (seq
         `(set! ,(make-live-info) ,t ,y)
         `(set! ,(make-live-info) ,t (asm ,info ,asm-add ,t ,x))
         `(set! ,(make-live-info) ,z ,t)))]
    [(op (z ur) (x ur) (y ur))
     (let ([t (make-tmp 'u)])
       (seq
         `(set! ,(make-live-info) ,t ,x)
         `(set! ,(make-live-info) ,t (asm ,info ,asm-add ,t ,y))
         `(set! ,(make-live-info) ,z ,t)))])

  (define-instruction value (* */ovfl) ; */ovfl must set multiply-overflow flag on overflow
    [(op (z ur) (x z) (y ur mem))
     `(set! ,(make-live-info) ,z (asm ,info ,asm-mul ,z ,y))]
    [(op (z ur) (x ur mem) (y z))
     `(set! ,(make-live-info) ,z (asm ,info ,asm-mul ,z ,x))]
    [(op (z ur) (x ur mem) (y imm32))
     `(set! ,(make-live-info) ,z (asm ,info ,asm-muli ,x ,y))]
    [(op (z ur) (x imm32) (y ur mem))
     `(set! ,(make-live-info) ,z (asm ,info ,asm-muli ,y ,x))]
    [(op (z ur) (x ur) (y ur))
     (let ([t (make-tmp 'u)])
       (seq
         `(set! ,(make-live-info) ,t ,x)
         `(set! ,(make-live-info) ,t (asm ,info ,asm-mul ,t ,y))
         `(set! ,(make-live-info) ,z ,t)))])

  (define-instruction value (/)
    (definitions
      (define go
        (lambda (z x y)
          (let ([urax (make-precolored-unspillable 'urax %rax)]
                [urdx (make-precolored-unspillable 'urdx %rdx)])
            (with-output-language (L15d Effect)
              (seq
                `(set! ,(make-live-info) ,urax ,x)
                `(set! ,(make-live-info) ,urdx (asm ,null-info ,asm-sext-rax->rdx ,urax))
                `(set! ,(make-live-info) ,urax (asm ,null-info ,asm-div ,urax ,urdx ,y))
                `(set! ,(make-live-info) ,z ,urax)))))))
    [(op (z mem) (x ur mem imm) (y ur mem)) (go z x y)]
    [(op (z ur) (x ur mem imm) (y ur mem)) (go z x y)])

  (define-instruction value (logand logor logxor)
    [(op (z mem) (x z) (y ur imm32))
     `(set! ,(make-live-info) ,z (asm ,info ,(asm-addop op) ,z ,y))]
    [(op (z mem) (x ur imm32) (y z))
     `(set! ,(make-live-info) ,z (asm ,info ,(asm-addop op) ,z ,x))]
    [(op (z ur) (x z) (y ur mem imm32))
     `(set! ,(make-live-info) ,z (asm ,info ,(asm-addop op) ,z ,y))]
    [(op (z ur) (x ur mem imm32) (y z))
     `(set! ,(make-live-info) ,z (asm ,info ,(asm-addop op) ,z ,x))]
    [(op (z ur) (x ur) (y mem imm32))
     (let ([t (make-tmp 'u)])
       (seq
         `(set! ,(make-live-info) ,t ,x)
         `(set! ,(make-live-info) ,t (asm ,info ,(asm-addop op) ,t ,y))
         `(set! ,(make-live-info) ,z ,t)))]
    [(op (z ur) (x ur mem imm32) (y ur))
     (let ([t (make-tmp 'u)])
       (seq
         `(set! ,(make-live-info) ,t ,y)
         `(set! ,(make-live-info) ,t (asm ,info ,(asm-addop op) ,t ,x))
         `(set! ,(make-live-info) ,z ,t)))])

  (define-instruction value (lognot)
    [(op (z mem) (x z))
     `(set! ,(make-live-info) ,z (asm ,info ,asm-lognot ,x))]
    [(op (z ur) (x z))
     `(set! ,(make-live-info) ,z (asm ,info ,asm-lognot ,x))]
    [(op (z ur) (x ur mem imm32))
     (seq
        `(set! ,(make-live-info) ,z ,x)
        `(set! ,(make-live-info) ,z (asm ,info ,asm-lognot ,z)))])

  ; TODO: use lea for certain constant shifts when x != z
  (define-instruction value (sll srl sra)
    (definitions
      (define go
        (lambda (info op z x y)
          (let ([urcx (make-precolored-unspillable 'urcx %rcx)])
            (with-output-language (L15d Effect)
              (seq
                `(set! ,(make-live-info) ,urcx ,y)
                `(set! ,(make-live-info) ,z (asm ,info ,(asm-shiftop op) ,x ,urcx))))))))
    [(op (z mem) (x z) (y imm32))
     `(set! ,(make-live-info) ,z (asm ,info ,(asm-shiftop op) ,x ,y))]
    ;; NB: need to return in these cases?
    [(op (z mem) (x z) (y ur mem imm)) (go info op z x y)]
    [(op (z ur) (x z) (y imm32))
     `(set! ,(make-live-info) ,z (asm ,info ,(asm-shiftop op) ,x ,y))]
    [(op (z ur) (x z) (y ur mem imm)) (go info op z x y)]
    [(op (z ur) (x ur mem imm32) (y imm32))
     (let ([t (make-tmp 'u)])
       (seq
         `(set! ,(make-live-info) ,t ,x)
         `(set! ,(make-live-info) ,t (asm ,info ,(asm-shiftop op) ,t ,y))
         `(set! ,(make-live-info) ,z ,t)))]
    [(op (z ur) (x ur mem imm32) (y ur mem imm))
     (let ([t (make-tmp 'u)])
       (seq
         `(set! ,(make-live-info) ,t ,x)
         (go info op t t y)
         `(set! ,(make-live-info) ,z ,t)))])

  (define-instruction value move
    [(op (z mem) (x ur imm32))
     `(set! ,(make-live-info) ,z ,x)]
    [(op (z ur) (x ur mem imm))
     ; NOTE: risc arch's will need to deal with limitations on imm
     `(set! ,(make-live-info) ,z ,x)])

  (define-instruction value lea1
    [(op (z ur) (x ur))
     (let ([offset (info-lea-offset info)])
       (if (signed-32? offset)
           `(set! ,(make-live-info) ,z (asm ,info ,(asm-lea1 (info-lea-offset info)) ,x))
           (let ([u (make-tmp 'u)])
             (seq
               `(set! ,(make-live-info) ,u (immediate ,offset))
               `(set! ,(make-live-info) ,z (asm ,info ,(asm-lea2 0) ,x ,u))))))])

  (define-instruction value lea2
    [(op (z ur) (x ur) (y ur))
     (let ([offset (info-lea-offset info)])
       (if (signed-32? offset)
           `(set! ,(make-live-info) ,z (asm ,info ,(asm-lea2 (info-lea-offset info)) ,x ,y))
           (let ([u (make-tmp 'u)])
             (seq
               `(set! ,(make-live-info) ,u (immediate ,offset))
               `(set! ,(make-live-info) ,u (asm ,null-info ,asm-add ,u ,y))
               `(set! ,(make-live-info) ,z (asm ,info ,(asm-lea2 0) ,x ,u))))))])

  (define-instruction value (sext8 sext16 sext32 zext8 zext16 zext32)
    [(op (z ur) (x ur mem)) `(set! ,(make-live-info) ,z (asm ,info ,(asm-move/extend op) ,x))])

  (define-instruction value (load)
    (definitions
      (define maybe-swap
        (lambda (info z expr)
          (with-output-language (L15d Effect)
            (if (info-load-swapped? info)
                (seq
                  expr
                  `(set! ,(make-live-info) ,z (asm ,info ,(asm-swap (info-load-type info)) ,z)))
                expr)))))
    [(op (z ur) (x ur) (y ur) (w imm32))
     (maybe-swap info z
       `(set! ,(make-live-info) ,z (asm ,info ,(asm-load (info-load-type info)) ,x ,y ,w)))]
    [(op (z ur) (x ur) (y ur) (w ur))
     (maybe-swap info z
       (if (eq? y %zero)
           `(set! ,(make-live-info) ,z (asm ,info ,(asm-load (info-load-type info)) ,x ,w (immediate 0)))
           (let ([u (make-tmp 'u)])
             (seq
               `(set! ,(make-live-info) ,u (asm ,null-info ,(asm-lea2 0) ,y ,w))
               `(set! ,(make-live-info) ,z (asm ,info ,(asm-load (info-load-type info)) ,x ,u (immediate 0)))))))])

  (define-instruction effect (store)
    (definitions
      (define maybe-swap
        (lambda (info w k)
          (with-output-language (L15d Effect)
            (if (info-load-swapped? info)
                (let ([u (make-tmp 'u)])
                  (seq
                    `(set! ,(make-live-info) ,u ,w)
                    `(set! ,(make-live-info) ,u (asm ,info ,(asm-swap (info-load-type info)) ,u))
                    (k u)))
                (k w))))))
    [(op (x ur) (y ur) (z imm32) (w ur real-imm32))
     (maybe-swap info w
       (lambda (w)
         `(asm ,info ,(asm-store (info-load-type info)) ,x ,y ,z ,w)))]
    [(op (x ur) (y ur) (z ur) (w ur real-imm32))
     (maybe-swap info w
       (lambda (w)
         (if (eq? y %zero)
             `(asm ,info ,(asm-store (info-load-type info)) ,x ,z (immediate 0) ,w)
             (let ([u (make-tmp 'u)])
               (seq
                 `(set! ,(make-live-info) ,u (asm ,null-info ,(asm-lea2 0) ,y ,z))
                 `(asm ,info ,(asm-store (info-load-type info)) ,x ,u (immediate 0) ,w))))))])

  (define-instruction effect (load-single->double load-double->single)
    [(op (x ur) (y ur) (z imm32))
     `(asm ,info ,(asm-fl-cvt op (info-loadfl-flreg info)) ,x ,y ,z)])

  (define-instruction effect (store-single->double)
    [(op (x ur) (y ur) (z imm32))
     `(asm ,info ,(asm-store-single->double (info-loadfl-flreg info)) ,x ,y ,z)])

  (define-instruction effect (store-single store-double)
    [(op (x ur) (y ur) (z imm32))
     `(asm ,info ,(asm-fl-store op (info-loadfl-flreg info)) ,x ,y ,z)])

  (define-instruction effect (load-double load-single)
    [(op (x ur) (y ur) (z imm32))
     `(asm ,info ,(asm-fl-load op (info-loadfl-flreg info)) ,x ,y ,z)])

  (define-instruction effect (flt)
    [(op (x mem ur) (y ur)) `(asm ,info ,asm-flt ,x ,y)])

  (define-instruction effect (fl+ fl- fl/ fl*)
    [(op (x ur) (y ur) (z ur)) `(asm ,info ,(asm-flop-2 op) ,x ,y ,z)])

  (define-instruction effect (flsqrt)
    [(op (x ur) (y ur)) `(asm ,info ,asm-flsqrt ,x ,y)])

  (define-instruction effect inc-cc-counter
    [(op (x ur) (y imm32 ur) (z imm32 ur)) `(asm ,info ,asm-inc-cc-counter ,x ,y ,z)])

  (define-instruction effect inc-profile-counter 
    [(op (x ur mem) (y imm32 ur)) `(asm ,info ,asm-inc-profile-counter ,x ,y)])

  (define-instruction value (trunc)
    [(op (z ur) (x ur)) `(set! ,(make-live-info) ,z (asm ,info ,asm-trunc ,x))])

  (define-instruction value get-tc
    [(op (z ur))
     ; always used with %rax as the lhs.  we take advantage of this to use z also
     ; as the jump temp (rax is assumed by linker's x86_64_set_jump)
     (safe-assert (eq? z %rax))
     `(set! ,(make-live-info) ,z (asm ,info ,asm-get-tc))])

  ; TODO: risc architectures will have to take info-asmlib-save-ra? into account
  (define-instruction value asmlibcall
    [(op (z ur))
     (let ([urax (make-precolored-unspillable 'urax %rax)]) ; rax is assumed by linker's x86_64_set_jump
       (seq
         `(set! ,(make-live-info) ,urax (asm ,null-info ,asm-kill))
         `(set! ,(make-live-info) ,z (asm ,info ,(asm-library-call (info-asmlib-libspec info)) ,urax ,(info-kill*-live*-live* info) ...))))])

  (define-instruction effect asmlibcall!
    [(op)
     (let ([urax (make-precolored-unspillable 'urax %rax)]) ; rax is assumed by linker's x86_64_set_jump
       (seq
         `(set! ,(make-live-info) ,urax (asm ,null-info ,asm-kill))
         `(asm ,info ,(asm-library-call (info-asmlib-libspec info)) ,urax ,(info-kill*-live*-live* info) ...)))])

  (safe-assert (reg-callee-save? %tc)) ; no need to save-restore
  (define-instruction effect (c-simple-call)
    [(op)
     (let ([urax (make-precolored-unspillable 'urax %rax)]) ; rax is assumed by linker's x86_64_set_jump
       (seq
         `(set! ,(make-live-info) ,urax (asm ,null-info ,asm-kill))
         `(asm ,info ,(asm-c-simple-call (info-c-simple-call-entry info)) ,urax)))])

  (define-instruction value pop
    [(op (z ur)) `(set! ,(make-live-info) ,z (asm ,info ,asm-pop))])

  (define-instruction pred (fl= fl< fl<=)
    [(op (x ur) (y ur))
     (let ([info (make-info-condition-code op #t #f)]) ; NB: reversed? flag is assumed to be #t
       (values '() `(asm ,info ,(asm-fl-relop info) ,x ,y)))])

  (define-instruction pred (eq? u< < > <= >=)
    ; the idea (following from the intel x86/x86_64 documentation)
    ; is that we want to squeeze this into a CMP that allows one of
    ; the following formats:
    ; CMP r/m, imm
    ; CMP r/m, r
    ; CMP r, r/m
    ; the last format we may want to drop, since it uses a different
    ; format from the one above it, but is interchangable with it,
    ; if we reverse the operands.
    [(op (x mem) (y ur imm32))
     (let ([info (make-info-condition-code op #f #t)])
       (values '() `(asm ,info ,(asm-relop info) ,x ,y)))]
    [(op (x ur) (y mem))
     (let ([info (make-info-condition-code op #t #t)])
       (values '() `(asm ,info ,(asm-relop info) ,y ,x)))]
    [(op (x imm32) (y ur mem))
     (let ([info (make-info-condition-code op #t #t)])
       (values '() `(asm ,info ,(asm-relop info) ,y ,x)))]
    [(op (x ur) (y ur imm32))
     (let ([info (make-info-condition-code op #f #t)])
       (values '() `(asm ,info ,(asm-relop info) ,x ,y)))])

  (define-instruction pred (condition-code)
    [(op) (values '() `(asm ,info ,(asm-condition-code info)))])

  (let* ([info-cc-eq (make-info-condition-code 'eq? #f #t)]
         [asm-eq (asm-relop info-cc-eq)])
    (define-instruction pred (type-check?)
      [(op (x ur mem) (mask imm32 ur) (type imm32 ur))
       (let ([tmp (make-tmp 'u)])
         (values
           (with-output-language (L15d Effect)
             (seq
               `(set! ,(make-live-info) ,tmp ,x)
               `(set! ,(make-live-info) ,tmp (asm ,null-info ,asm-logand ,tmp ,mask))))
           `(asm ,info-cc-eq ,asm-eq ,tmp ,type)))])

    (define-instruction pred (logtest log!test)
      [(op (x mem) (y ur imm32))
       (values '() `(asm ,info-cc-eq ,(asm-logtest (eq? op 'log!test) info-cc-eq) ,x ,y))]
      [(op (x ur imm32) (y mem))
       (values '() `(asm ,info-cc-eq ,(asm-logtest (eq? op 'log!test) info-cc-eq) ,y ,x))]
      [(op (x imm32) (y ur))
       (values '() `(asm ,info-cc-eq ,(asm-logtest (eq? op 'log!test) info-cc-eq) ,y ,x))]
      [(op (x ur) (y ur imm32))
       (values '() `(asm ,info-cc-eq ,(asm-logtest (eq? op 'log!test) info-cc-eq) ,x ,y))])

    (define-instruction pred (lock!)
      [(op (x ur) (y ur) (w imm32))
       (let ([uts (make-precolored-unspillable 'uts %ts)])
         (values
           (nanopass-case (L15d Triv) w
             [(immediate ,imm)
              (with-output-language (L15d Effect)
                (seq
                  `(set! ,(make-live-info) ,uts (immediate 1))
                  `(set! ,(make-live-info) ,uts
                     (asm ,info ,asm-exchange ,uts
                       (mref ,x ,y ,imm)))))])
           `(asm ,info-cc-eq ,asm-eq ,uts (immediate 0))))]))

  (define-instruction effect (locked-incr!)
    [(op (x ur) (y ur) (w imm32))
     `(asm ,info ,asm-locked-incr ,x ,y ,w)])

  (define-instruction effect (locked-decr!)
    [(op (x ur) (y ur) (w imm32))
     `(asm ,info ,asm-locked-decr ,x ,y ,w)])

  (define-instruction effect (pause)
    [(op) `(asm ,info ,asm-pause)])

  (define-instruction value read-performance-monitoring-counter
    [(op (z ur) (x ur mem imm))
     (safe-assert (eq? z %rax))
     (safe-assert (and (info-kill*? info) (memq %rdx (info-kill*-kill* info))))
     (let ([urcx (make-precolored-unspillable 'urcx %rcx)])
       (seq
         `(set! ,(make-live-info) ,urcx ,x)
         `(set! ,(make-live-info) ,z (asm ,info ,asm-read-performance-monitoring-counter ,urcx))))])

  (define-instruction value read-time-stamp-counter
    [(op (z ur))
     (safe-assert (eq? z %rax))
     (safe-assert (and (info-kill*? info) (memq %rdx (info-kill*-kill* info))))
     `(set! ,(make-live-info) ,z (asm ,info ,asm-read-time-stamp-counter))])

  ; NB: shouldn't need to list (info-kill*-live*-live* info) ... here, since we've already
  ; NB: computed spillable/register live sets
  (define-instruction effect (c-call)
    [(op (x ur mem)) `(asm ,info ,asm-indirect-call ,x ,(info-kill*-live*-live* info) ...)])

  (define-instruction effect (push)
    [(op (x ur)) `(asm ,info ,asm-push ,x)])

  (define-instruction effect save-flrv
    [(op) `(asm ,info ,asm-save-flrv)])

  (define-instruction effect restore-flrv
    [(op) `(asm ,info ,asm-restore-flrv)])

  (define-instruction effect invoke-prelude
   ; align sp on 16-byte boundary, taking into account 8-byte
   ; return address already pushed by caller
    [(op)
     (seq
       `(set! ,(make-live-info) ,%sp (asm ,info ,asm-sub ,%sp (immediate 8)))
       `(set! ,(make-live-info) ,%tc ,%Carg1))])
  )

;;; SECTION 3: assembler
(module asm-module (; required exports
                     asm-move asm-move/extend asm-load asm-store asm-swap asm-library-call asm-library-jump
                     asm-mul asm-muli asm-addop asm-add asm-sub asm-negate asm-sub-negate
                     asm-pop asm-shiftop asm-sll asm-logand asm-lognot
                     asm-logtest asm-fl-relop asm-relop asm-push asm-indirect-jump asm-literal-jump
                     asm-direct-jump asm-return-address asm-jump asm-conditional-jump asm-data-label asm-rp-header
                     asm-lea1 asm-lea2 asm-indirect-call asm-condition-code
                     asm-fl-cvt asm-fl-store asm-fl-load asm-flt asm-trunc asm-div
                     asm-exchange asm-pause asm-locked-incr asm-locked-decr
                     asm-flop-2 asm-flsqrt asm-c-simple-call
                     asm-save-flrv asm-restore-flrv asm-return asm-size
                     asm-enter asm-foreign-call asm-foreign-callable
                     asm-inc-profile-counter
                     asm-inc-cc-counter asm-read-time-stamp-counter asm-read-performance-monitoring-counter
                     ; threaded version specific
                     asm-get-tc
                     ; machine dependent exports
                     asm-sext-rax->rdx asm-store-single->double asm-kill)

  (define ax-register?
    (case-lambda
      [(x) (record-case x [(reg) r #t] [else #f])]
      [(x reg) (record-case x [(reg) r (eq? r reg)] [else #f])]))

  (define ax-ea-reg-code
    (lambda (ea)
      (record-case ea
        [(reg) r (reg-mdinfo r)]
        [else (sorry! 'ax-ea-reg-code "ea=~s" ea)])))

  (define ax-imm-data
    (lambda (ea)
      (record-case ea
        [(imm) (n) n]
        [else ($oops 'assembler-internal "ax-imm-data ea=~s" ea)])))

  ; define-op sets up assembly op macros--
  ; suffixes are a sub-list of (b w l 1)--
  ; the opcode, the size (byte word, long, quad), and all other expressions
  ; are passed to the specified handler--
  ; for prefix 'p' and each suffix 's' a macro of the form 'ps' is set up--
  ; if no suffix is specified the prefix is defined as a macro
  (define-syntax define-op
    (lambda (x)
      (syntax-case x ()
        [(k prefix (suffix ...) handler e ...)
         (let ([suffix* (datum (suffix ...))])
           (unless (andmap (lambda (x) (memq x '(b w l *))) suffix*)
             (syntax-error x (format "invalid suffix list ~s" suffix*)))
           (with-syntax ([(op ...) (map (lambda (x)
                                          (if (eq? x '*)
                                              (construct-name #'k "asmop-" #'prefix)
                                              (construct-name #'k "asmop-" #'prefix x)))
                                     suffix*)]
                         [(size ...) (map (lambda (x)
                                            (case x [(b) #'byte] [(w) #'word] [(l) #'long] [(*) #'quad]))
                                       suffix*)])
             #'(begin
                 (define-syntax op
                   (syntax-rules ()
                     [(_ mneu arg (... ...))
                      (handler 'mneu 'size e ... arg (... ...))]))
                 ...)))]
        [(k op handler e ...)
         (with-syntax ([op (construct-name #'k "asmop-" #'op)])
           #'(define-syntax op
               (syntax-rules ()
                 [(_ mneu arg (... ...))
                  (handler 'mneu e ... arg (... ...))])))])))

  (define-syntax emit
    (lambda (x)
      (syntax-case x ()
        [(k op x ...)
         (with-syntax ([emit-op (construct-name #'k "asmop-" #'op)])
           #'(emit-op op x ...))])))

  ;;; note that the assembler isn't clever--you must be very explicit about
  ;;; which flavor you want, and there are a few new varieties introduced
  ;;; (commented-out opcodes are not currently used by the assembler--
  ;;; spaces are left to indicate possible size extensions)

  (define-op asl  (*) unary-op  #b1101001 #b100) ; shifts by CL
  (define-op lsr  (*) unary-op  #b1101001 #b101) ; shifts by CL
  (define-op asr  (*) unary-op  #b1101001 #b111) ; shifts by CL
  (define-op asli (*) shifti-op #b1100000 #b100)
  (define-op lsri (*) shifti-op #b1100000 #b101)
  (define-op asri (*) shifti-op #b1100000 #b111)

  (define-op addi (#;b *) addi-op   #b100000 #b000)
  (define-op subi (#;b *) addi-op   #b100000 #b101)
  (define-op cmpi (b *) addi-op   #b100000 #b111)

  (define-op ori  (#;b *) logi-op #b001)
  (define-op andi (b *) logi-op #b100)
  (define-op xori (#;b *) logi-op #b110)
  (define-op testi (b *) testi-op #b1111011 #b000)

  (define-op movi (b w l *) movi-op #b000)

  (define-op mov    (b w l *) binary-op #b100010)
  (define-op movsb            mulq-op #b10111110)
  (define-op movsw            mulq-op #b10111111)
  (define-op movsl            quad-op #b01100011)
  (define-op movzb            mulq-op #b10110110)
  (define-op movzw            mulq-op #b10110111)

  (define-op add  (#;b *) binary-op #b000000)
  (define-op or   (#;b *) binary-op #b000010)
  (define-op and  (#;b *) binary-op #b001000)
  (define-op sub  (#;b *) binary-op #b001010)
  (define-op xor  (#;b *) binary-op #b001100)
  (define-op test (#;b *) test-op   #b1000010)
  (define-op cmp  (#;b *) binary-op #b001110)
  (define-op xchg (#;b *) xchg-op   #b1000011)
  (define-op bswap (#;l *) bswap-op)
 
  (define-op divsax (*) unary-op   #b1111011  #b111)
  #;(define-op mulsax (*) unary-op   #b1111011  #b100)
  (define-op muls mulq-op #b10101111)
  (define-op mulsi muliq-op    #b01101001)

  (define-op lea       quad-op     #b10001101)

  (define-op pop      push-op #b01011)
  (define-op push     push-op #b01010)
  (define-op pushi    pushi-op)
  (define-op pushf     byte-op     #b10011100)
  (define-op popf      byte-op     #b10011101)

  #;(define-op nop       byte-op    #b10010000)
  (define-op ret       byte-op    #b11000011)
  (define-op extad quad-byte-op   #b10011001) ; extend rax to rdx

  (define-op rdtsc     two-byte-op     #b1111 #b00110001) ; read time-stamp counter
  (define-op rdpmc     two-byte-op     #b1111 #b00110011) ; read performance monitoring counter
  (define-op pause     two-byte-op #b11110011 #b10010000) ; equivalent to rep nop

  (define-op dec (#;b *) unary-op  #b1111111 #b001)
  (define-op inc (#;b *) unary-op  #b1111111 #b000)
  (define-op neg (b *) unary-op  #b1111011 #b011) ; was commented out in x86_64macros
  (define-op not (#;b *) unary-op  #b1111011 #b010)

  (define-op locked-dec (#;b *) locked-unary-op #b1111111 #b001)
  (define-op locked-inc (#;b *) locked-unary-op #b1111111 #b000)

  ; also do inc-reg dec-reg

  ; the following are forms of the call instruction and push the return address
  (define-op call      jump-op #b010) ; reg/mem indirect
  #;(define-op bsrl      branch-always-op long #b11101000) ; pc-relative
  (define-op bsr       bsr-op)

  ; the following are forms of the jmp instruction
  (define-op jmp       jump-op #b100) ; reg/mem indirect
  (define-op bra       bra-op)

  (define-op bvs branch-op #b0000) ; jump on overflow
  (define-op bvc branch-op #b0001) ; jump on not overflow
  (define-op bcs branch-op #b0010) ; jump on below (carry set)
  (define-op bcc branch-op #b0011) ; jump on not below (carry clear)
  (define-op beq branch-op #b0100) ; jump on equal
  (define-op bne branch-op #b0101) ; jump on not equal
  (define-op bls branch-op #b0110) ; jump on less or same (below or equal)
  (define-op bhi branch-op #b0111) ; jump on higher (above)
  (define-op blt branch-op #b1100) ; jump on less than
  (define-op bge branch-op #b1101) ; jump on greater than or equal
  (define-op ble branch-op #b1110) ; jump on less than or equal
  (define-op bgt branch-op #b1111) ; jump on greater than

  ; SSE2 instructions
  (define-op sse.addsd     sse-op1 #xF2 #x58 0)
  (define-op sse.andpd     sse-op1 #x66 #x54 0)
  (define-op sse.cvtss2sd  sse-op1 #xF3 #x5A 0)
  (define-op sse.cvtsd2ss  sse-op1 #xF2 #x5A 0)
  (define-op sse.cvttsd2si sse-op1 #xF2 #x2C 1)
  (define-op sse.cvtsi2sd  sse-op1 #xF2 #x2A 1)
  (define-op sse.divsd     sse-op1 #xF2 #x5E 0)
  (define-op sse.movd      sse-op2 #x66 #x6E #x7E 1)
  (define-op sse.movsd     sse-op2 #xF2 #x10 #x11 0)
  (define-op sse.movss     sse-op2 #xF3 #x10 #x11 0)
  (define-op sse.mulsd     sse-op1 #xF2 #x59 0)
  (define-op sse.sqrtsd    sse-op1 #xF2 #x51 0)
  (define-op sse.subsd     sse-op1 #xF2 #x5C 0)
  (define-op sse.ucomisd   sse-op1 #x66 #x2E 0)
  (define-op sse.xorpd     sse-op1 #x66 #x57 0)

  (define sse-op1
    (lambda (op prefix-code op-code w source dest-reg code*)
      (emit-code (op source dest-reg code*)
        (build byte prefix-code)
        (ax-ea-rex w source dest-reg #f)
        (build byte #x0F)
        (build byte op-code)
        (ax-ea-modrm-reg source dest-reg)
        (ax-ea-sib source)
        (ax-ea-addr-disp source))))

  (define sse-op2
    (lambda (op prefix-code dstreg-op-code srcreg-op-code w source dest code*)
      (cond
        [(ax-register? source)
         (emit-code (op source dest code*)
           (build byte prefix-code)
           (ax-ea-rex w dest source #f)
           (build byte #x0F)
           (build byte srcreg-op-code)
           (ax-ea-modrm-reg dest source)
           (ax-ea-sib dest)
           (ax-ea-addr-disp dest))]
        [(ax-register? dest)
         (emit-code (op source dest code*)
           (build byte prefix-code)
           (ax-ea-rex w source dest #f)
           (build byte #x0F)
           (build byte dstreg-op-code)
           (ax-ea-modrm-reg source dest)
           (ax-ea-sib source)
           (ax-ea-addr-disp source))]
        [else
          ($oops 'assembler-internal "sse-op2 source=~s dest=~s" source dest)])))

  (define mulq-op
    ; used for movs/movz as well as mulsq
    (lambda (op op-code source-ea dest-reg code*)
      (emit-code (op source-ea dest-reg code*)
        (ax-ea-rex 1 source-ea dest-reg 'quad)
        (build byte #b00001111)
        (build byte op-code)
        (ax-ea-modrm-reg source-ea dest-reg)
        (ax-ea-sib source-ea)
        (ax-ea-addr-disp source-ea))))

  (define muliq-op
    (lambda (op op-code imm-data source-ea dest-reg code*)
      (emit-code (op imm-data source-ea dest-reg code*)
        (ax-ea-rex 1 source-ea dest-reg 'quad)
        (build byte op-code)
        (ax-ea-modrm-reg source-ea dest-reg)
        (ax-ea-sib source-ea)
        (ax-ea-addr-disp source-ea)
        (build long (ax-imm-data imm-data)))))

  (define quad-op
    (lambda (op op-code source-ea dest-reg code*)
      (emit-code (op source-ea dest-reg code*)
        (ax-ea-rex 1 source-ea dest-reg 'quad)
        (build byte op-code)
        (ax-ea-modrm-reg source-ea dest-reg)
        (ax-ea-sib source-ea)
        (ax-ea-addr-disp source-ea))))

  (define test-op
    (lambda (op size op-code source-ea reg code*)
      (emit-code (op source-ea reg code*)
        (ax-ea-rex (if (eq? size 'quad) 1 0) source-ea reg size)
        (build byte
          (byte-fields
            [1 op-code]
            [0 (ax-size-code size)]))
        (ax-ea-modrm-reg source-ea reg)
        (ax-ea-sib source-ea)
        (ax-ea-addr-disp source-ea))))

  (define unary-op
    (lambda (op size op-code ttt-code dest-ea code*)
      (emit-code (op dest-ea code*)
        (ax-ea-rex (if (eq? size 'quad) 1 0) dest-ea #f size)
        (build byte
          (byte-fields
            [1 op-code]
            [0 (ax-size-code size)]))
        (ax-ea-modrm-ttt dest-ea ttt-code)
        (ax-ea-sib dest-ea)
        (ax-ea-addr-disp dest-ea))))

  (define locked-unary-op
    (lambda (op size op-code ttt-code dest-ea code*)
      (emit-code (op dest-ea code*)
        (build byte #xf0) ; lock prefix
        (ax-ea-rex (if (eq? size 'quad) 1 0) dest-ea #f size)
        (build byte
          (byte-fields
            [1 op-code]
            [0 (ax-size-code size)]))
        (ax-ea-modrm-ttt dest-ea ttt-code)
        (ax-ea-sib dest-ea)
        (ax-ea-addr-disp dest-ea))))

  (define pushi-op
    (lambda (op imm-ea code*)
      (if (ax-range? -128 imm-ea 127)
          (emit-code (op imm-ea code*)
            (build byte 106)
            (ax-ea-imm-data 'byte imm-ea))
          (emit-code (op imm-ea code*)
            (build byte 104)
            (ax-ea-imm-data 'long imm-ea)))))

  ; imm-data can be either an (imm n) or else a (literal size addr) record.
  ;
  (define testi-op
    (lambda (op size op-code ttt-code imm-ea dest-ea code*)
      (emit-code (op imm-ea dest-ea code*)
        (ax-ea-rex (if (eq? size 'quad) 1 0) dest-ea #f size)
        (build byte
          (byte-fields
            [1 op-code]
            [0 (ax-size-code size)]))
        (ax-ea-modrm-ttt dest-ea ttt-code)
        (ax-ea-sib dest-ea)
        (ax-ea-addr-disp dest-ea)
        (ax-ea-imm-data size imm-ea))))

  (define logi-op
    (lambda (op size ttt-code imm-ea dest-ea code*)
      (if (and (eq? size 'quad)
               (record-case imm-ea
                 [(imm) (n) (<= -128 n 127)]
                 [else #f]))
          (emit-code (op imm-ea dest-ea code*)
            (ax-ea-rex 1 dest-ea #f size)
            (build byte
              (byte-fields
                [1 #b1000001]
                [0 (ax-size-code size)]))
            (ax-ea-modrm-ttt dest-ea ttt-code)
            (ax-ea-sib dest-ea)
            (ax-ea-addr-disp dest-ea)
            (ax-ea-imm-data 'byte imm-ea))
          (emit-code (op imm-ea dest-ea code*)
            (ax-ea-rex 1 dest-ea #f size)
            (build byte
              (byte-fields
                [1 #b1000000]
                [0 (ax-size-code size)]))
            (ax-ea-modrm-ttt dest-ea ttt-code)
            (ax-ea-sib dest-ea)
            (ax-ea-addr-disp dest-ea)
            (ax-ea-imm-data size imm-ea)))))

  (define addi-op
    (lambda (op size op-code ttt-code imm-ea dest-ea code*)
      (if (and (eq? size 'quad)
               (record-case imm-ea
                 [(imm) (n) (<= -128 n 127)]
                 [else #f]))
          (emit-code (op imm-ea dest-ea code*)
            (ax-ea-rex 1 dest-ea #f size)
            (build byte
              (byte-fields
                [2 op-code]
                [1 1]
                [0 (ax-size-code size)]))
            (ax-ea-modrm-ttt dest-ea ttt-code)
            (ax-ea-sib dest-ea)
            (ax-ea-addr-disp dest-ea)
            (ax-ea-imm-data 'byte imm-ea))
          (emit-code (op imm-ea dest-ea code*)
            (ax-ea-rex (if (eq? size 'quad) 1 0) dest-ea #f size)
            (build byte
              (byte-fields
                [2 op-code]
                [1 0]
                [0 (ax-size-code size)]))
            (ax-ea-modrm-ttt dest-ea ttt-code)
            (ax-ea-sib dest-ea)
            (ax-ea-addr-disp dest-ea)
            (ax-ea-imm-data size imm-ea)))))

  (define movi-op
    (lambda (op size ttt-code imm-ea dest-ea code*)
      (cond
        [(and (eq? size 'quad)
              (record-case dest-ea [(reg) stuff #t] [else #f])
              (not (ax-range? #x-80000000 imm-ea #x7fffffff)))
         (emit-code (op imm-ea dest-ea code*)
           (ax-ea-rex 1 dest-ea #f 'quad)
           (build byte
             (byte-fields
               [4 #b1011]
               [3 (ax-size-code size)]
               [0 (fxlogand (ax-ea-reg-code dest-ea) 7)]))
           (ax-ea-imm-data 'full-quad imm-ea))]
        [else
          (emit-code (op imm-ea dest-ea code*)
            (and (eq? size 'word) (build byte 102))
            (ax-ea-rex (if (eq? size 'quad) 1 0) dest-ea #f size)
            (build byte
              (byte-fields
                [1 #b1100011]
                [0 (ax-size-code size)]))
            (ax-ea-modrm-ttt dest-ea ttt-code)
            (ax-ea-sib dest-ea)
            (ax-ea-addr-disp dest-ea)
            (ax-ea-imm-data size imm-ea))])))

  ;;; always need byte immediate data for shift ops
  (define shifti-op
    (lambda (op size op-code ttt-code imm-ea dest-ea code*)
      (emit-code (op imm-ea dest-ea code*)
        (ax-ea-rex (if (eq? size 'quad) 1 0) dest-ea #f size)
        (build byte
          (byte-fields
            [1 op-code]
            [0 (ax-size-code size)]))
        (ax-ea-modrm-ttt dest-ea ttt-code)
        (ax-ea-sib dest-ea)
        (ax-ea-addr-disp dest-ea)
        (ax-ea-imm-data 'byte imm-ea))))

  (define binary-op
    (lambda (op size op-code source dest code*)
      (cond
        [(ax-register? source)
         (emit-code (op source dest code*)
           (and (eq? size 'word) (build byte 102))
           (ax-ea-rex (if (eq? size 'quad) 1 0) dest source size)
           (build byte
             (byte-fields
               [2 op-code]
               [1 0]
               [0 (ax-size-code size)]))
           (ax-ea-modrm-reg dest source)
           (ax-ea-sib dest)
           (ax-ea-addr-disp dest))]
        [(ax-register? dest)
         (emit-code (op source dest code*)
           (and (eq? size 'word) (build byte 102))
           (ax-ea-rex (if (eq? size 'quad) 1 0) source dest size)
           (build byte
             (byte-fields
               [2 op-code]
               [1 1]
               [0 (ax-size-code size)]))
           (ax-ea-modrm-reg source dest)
           (ax-ea-sib source)
           (ax-ea-addr-disp source))]
        [else
          ($oops 'assembler-internal "binary-op source=~s dest=~s" source dest)])))

  (define xchg-op
    (lambda (op size op-code source dest code*)
      (cond
        [(ax-register? source)
         (emit-code (op source dest code*)
           (ax-ea-rex (if (eq? size 'quad) 1 0) dest source size)
           (build byte
             (byte-fields
               [1 op-code]
               [0 (ax-size-code size)]))
           (ax-ea-modrm-reg dest source)
           (ax-ea-sib dest)
           (ax-ea-addr-disp dest))]
        [(ax-register? dest)
         (emit-code (op source dest code*)
           (ax-ea-rex (if (eq? size 'quad) 1 0) source dest size)
           (build byte
             (byte-fields
               [1 op-code]
               [0 (ax-size-code size)]))
           (ax-ea-modrm-reg source dest)
           (ax-ea-sib source)
           (ax-ea-addr-disp source))]
        [else
          ($oops 'assembler-internal "xchg-op source=~s dest=~s" source dest)])))

  (define branch-op
    (lambda (op condition-code disp code*)
      (record-case disp
        [(label) (offset l)
                 (if (and (fixnum? offset) (fx<= -128 offset 127))
                     (emit-code (op disp code*)
                       (build byte
                         (byte-fields
                           [4 7]
                           [0 condition-code]))
                       (build byte offset))
                     (emit-code (op disp code*)
                       (build byte 15)
                       (build byte
                         (byte-fields
                           [4 8]
                           [0 condition-code]))
                       (build long offset)))]
        [else
          (emit-code (op disp code*)
            (build byte 15)
            (build byte
              (byte-fields
                [4 8]
                [0 condition-code]))
            (ax-ea-branch-disp disp))])))

  (define jump-op
    (lambda (op ttt-code dest-ea code*)
      (emit-code (op dest-ea code*)
        (ax-ea-rex 0 dest-ea #f 'quad)
        (build byte 255)
        (ax-ea-modrm-ttt dest-ea ttt-code)
        (ax-ea-sib dest-ea)
        (ax-ea-addr-disp dest-ea))))

  (define bra-op
    (lambda (op disp code*)
      (record-case disp
        [(label) (offset l)
                 (if (and (fixnum? offset) (fx<= -128 offset 127))
                     (emit-code (op disp code*)
                       (build byte #b11101011)
                       (build byte offset))
                     (emit-code (op disp code*)
                       (build byte #b11101001)
                       (build long offset)))]
        [else 
          (emit-code (op disp code*)
            (build byte #b11101001)
            (ax-ea-branch-disp disp))])))

  ;; TODO: not useful on x86_64?
  (define bsr-op
    (lambda (op disp code*)
      (emit-code (op disp code*)
        (build byte #b11101000)
        (if (pair? disp)
            (ax-ea-branch-disp disp)
            (build long disp)))))

  (define byte-op
    (lambda (op op-code code*)
      (emit-code (op code*)
        (build byte op-code))))

  (define two-byte-op
    (lambda (op op-code1 op-code2 code*)
      (emit-code (op code*)
        (build byte op-code1)
        (build byte op-code2))))

  (define bswap-op
    (lambda (op size reg code*)
      (begin
        (unless (ax-register? reg)
          ($oops 'assembler-internal "(bswap-op) ~s is not a real register" reg))
        (emit-code (op reg code*)
          (ax-ea-rex (if (eq? size 'quad) 1 0) reg #f size)
          (build byte #b00001111)
          (build byte
            (byte-fields
              [3 #b11001]
              [0 (fxlogand (ax-ea-reg-code reg) 7)]))))))

  (define quad-byte-op
    (lambda (op op-code code*)
      (emit-code (op code*)
        (build byte #x48) ; rex prefix w/rex.w bit set
        (build byte op-code))))

  (define push-op
    (lambda (op op-code reg code*)
      (begin
        (unless (ax-register? reg)
          ($oops 'assembler-internal "(push-op) ~s is not a real register" reg))
        (emit-code (op reg code*)
          (ax-ea-rex 0 reg #f 'quad)
          (build byte
            (byte-fields
              [3 op-code]
              [0 (fxlogand (ax-ea-reg-code reg) 7)]))))))

  (define-syntax emit-code
    (lambda (x)
      (define build-maybe-cons*
        (lambda (e* e-ls)
          (if (null? e*)
              e-ls
              #`(let ([t #,(car e*)] [ls #,(build-maybe-cons* (cdr e*) e-ls)])
                  (if t (cons t ls) ls)))))
      (syntax-case x ()
        [(_ (op opnd ... ?code*) chunk ...)
         (build-maybe-cons* #'(chunk ...)
           #'(aop-cons* `(asm ,op ,opnd ...) ?code*))])))

  (define-who ax-size-code
    (lambda (x)
      (case x
        [(byte) 0]
        [(word) 1]
        [(long) 1]
        [(quad) 1]
        [else (sorry! who "invalid size ~s" x)])))

  (define-syntax build
    (syntax-rules ()
      [(_ x e)
       (and (memq (datum x) '(byte word long quad)) (integer? (datum e)))
       (quote (x . e))]
      [(_ x e)
       (memq (datum x) '(byte word long quad))
       (cons 'x e)]))

  (define-syntax byte-fields
    (syntax-rules ()
      [(byte-fields (n e) ...)
       (andmap fixnum? (datum (n ...)))
       (fx+ (fxsll e n) ...)]))

  (define ax-ea-rex
    (lambda (w ea maybe-reg size)
      (define (rex-required? x)
        ; rex prefix is required to access lsb of RSP, RBP, RSI, RDI
        ; (and R8-R15, but they require rex prefix anyway)
        (and (eq? size 'byte)
             (record-case x
               [(reg) r (fx<= 4 (reg-mdinfo r) 7)]
               [else #f])))
      (define (rex.r)
        (if maybe-reg
            (record-case maybe-reg
              [(reg) r (fxsrl (reg-mdinfo r) 3)]
              [else ($oops 'assembler-internal "maybe-reg=~s" maybe-reg)])
            0))
      (define (build-rex x b)
        (let ([b (build byte
                   (byte-fields
                     [4 #b0100]
                     [3 w]
                     [2 (rex.r)]
                     [1 x]
                     [0 b]))])
          (and (or (not (fx= (cdr b) #x40))
                   (rex-required? ea)
                   (and maybe-reg (rex-required? maybe-reg)))
               b)))
      (record-case ea
        [(index) (size index-reg base-reg) 
         (build-rex
           (fxsrl (reg-mdinfo index-reg) 3)
           (fxsrl (reg-mdinfo base-reg) 3))]
        [(riprel) stuff (build-rex 0 0)]
        [(disp) (size reg) (build-rex 0 (fxsrl (reg-mdinfo reg) 3))]
        [(reg) r (build-rex 0 (fxsrl (reg-mdinfo r) 3))]
        [else ($oops 'assembler-internal "ax-ea-rex ea ~s" ea)])))

  (define ax-ea-addr-disp
    (lambda (dest-ea)
      (record-case dest-ea
        [(index) (size index-reg base-reg)
         (cond
           [(and (fxzero? size) (not (eq? base-reg %rbp)) (not (eq? base-reg %r13))) #f]
           [(ax-byte-size? size) (build byte size)]
           [else (build long size)])]
        [(riprel) (disp) (build long disp)]
        [(disp) (size reg)
         (cond
           [(and (fxzero? size) (not (eq? reg %rbp)) (not (eq? reg %r13))) #f] ; indirect
           [(ax-byte-size? size) (build byte size)]
           [else (build long size)])]
        [(reg) r #f]
        [else ($oops 'assembler-internal "ax-ea-addr-disp dest-ea=~s" dest-ea)])))

  (define ax-ea-sib
    (let ([ax-ss-index-base
           (lambda (index-reg base-reg)
             (build byte
               (byte-fields
                 [6 #b00]          ; 2 bits, scaled by bytes.
                 [3 index-reg]     ; 3 bits, index register.
                 [0 base-reg])))]) ; 3 bits, base register.
      (lambda (dest-ea)
        (record-case dest-ea
          [(index) (size index-reg base-reg)
           (ax-ss-index-base (fxlogand (reg-mdinfo index-reg) 7)
             (fxlogand (reg-mdinfo base-reg) 7))]
          [(riprel) stuff #f]
          [(disp) (size reg)
           (and (or (eq? reg %sp) (eq? reg %r12))
                (ax-ss-index-base #b100 #b100))]
          [(reg) r #f]
          [else ($oops 'assembler-internal "ax-ea-sib dest-ea=~s" dest-ea)]))))

  (define ax-ea-modrm-reg
    (lambda (dest-ea reg)
      (ax-ea-modrm-ttt dest-ea (fxlogand (ax-ea-reg-code reg) 7))))

  (define ax-ea-modrm-ttt
    (letrec
      ([ax-mod-ttt-r/m
        (lambda (mod ttt r/m)
          (build byte
            (byte-fields
              [6 mod]     ; 2 bits
              [3 ttt]     ; 3 bits
              [0 r/m])))] ; 3 bits
       [ax-r/m ; 3 bits
        (lambda (dest-ea)
          (record-case dest-ea
            [(index) (size index-reg base-reg) #b100]
            [(riprel) stuff #b101]
            [(disp) (size reg) (fxlogand (reg-mdinfo reg) 7)]
            [(reg) r (fxlogand (reg-mdinfo r) 7)]
            [else ($oops 'assembler-internal
                    "ax-r/m dest-ea=~s" dest-ea)]))]
       [ax-mod ; 2 bits
        (lambda (dest-ea)
          (record-case dest-ea
            [(index) (size index-reg base-reg)
             (cond
               [(and (fxzero? size) (not (eq? base-reg %rbp)) (not (eq? base-reg %r13))) #b00]
               [(ax-byte-size? size) #b01]
               [else #b10])]
            [(riprel) stuff #b00]
            [(disp) (size reg)
             (cond
               [(and (fxzero? size) (not (eq? reg %rbp)) (not (eq? reg %r13))) #b00] ; indirect
               [(ax-byte-size? size) #b01]
               [else #b10])]
            [(reg) r #b11]
            [else ($oops 'assembler-internal "ax-mod dest-ea ~s" dest-ea)]))])
      (lambda (dest-ea ttt)
        (ax-mod-ttt-r/m (ax-mod dest-ea) ttt (ax-r/m dest-ea)))))

  (define ax-ea-imm-data
    (lambda (size imm-data)
      (case size
        [(full-quad)
         (record-case imm-data
           [(literal) stuff (cons 'abs stuff)]
           [(funcrel) stuff (cons 'funcrel (ax-ea-imm-data 'quad stuff))] ; added, not sure if this works for x86_64
           [(imm) (n) (cons 'quad n)]
           [else ($oops 'assembler-internal
                   "ax-ea-imm-data size=~s imm-data=~s" size imm-data)])]
        [else
         (record-case imm-data
           [(imm) (n) (cons (if (eq? size 'quad) 'long size) n)]
           [else ($oops 'assembler-internal
                   "ax-ea-imm-data size=~s imm-data=~s" size imm-data)])])))

  (define ax-byte-size?
    (lambda (n)
      (<= -128 n 127)))

  (define ax-range?
    (lambda (low x high)
      (record-case x
        [(imm) (n) (<= low n high)]
        [else #f])))

  (define ax-ea-branch-disp
    (lambda (dest-ea)
      (record-case dest-ea
        [(literal) stuff (cons 'rel stuff)]
        [else ($oops 'assembler-internal
                "ax-ea-branch-disp dest-ea=~s" dest-ea)])))

  (define asm-size
    (lambda (x)
      (case (car x)
        [(asm x86_64-jump x86_64-call) 0]
        [(byte) 1]
        [(word) 2]
        [(long) 4]
        [else 8])))

  (define-who asm-move
    (lambda (code* dest src)
      (Trivit (dest src)
        (record-case src
          [(imm) (n)
                 (if (and (eqv? n 0) (record-case dest [(reg) r #t] [else #f]))
                     (emit xor dest dest code*)
                     (emit movi src dest code*))]
          [(literal) stuff (emit movi src dest code*)]
          [else (emit mov src dest code*)]))))

  (define-who asm-move/extend
    (lambda (op)
      (lambda (code* dest src)
        (Trivit (dest src)
          (case op
            [(sext8) (emit movsb src dest code*)]
            [(sext16) (emit movsw src dest code*)]
            [(sext32) (emit movsl src dest code*)]
            [(zext8) (emit movzb src dest code*)]
            [(zext16) (emit movzw src dest code*)]
            [(zext32) (emit movl src dest code*)]
            [else (sorry! who "unexpected op ~s" op)])))))

  (define asm-fl-cvt
    (lambda (op flreg)
      (lambda (code* base index offset)
        (let ([src (build-mem-opnd base index offset)])
          (case op
            [(load-single->double) (emit sse.cvtss2sd src (cons 'reg flreg) code*)]
            [(load-double->single) (emit sse.cvtsd2ss src (cons 'reg flreg) code*)])))))

  (define asm-store-single->double
    (lambda (flreg)
      (lambda (code* base index offset)
        (let ([dest (build-mem-opnd base index offset)] [flreg (cons 'reg flreg)])
          (emit sse.cvtss2sd flreg flreg
            (emit sse.movsd flreg dest code*))))))

  (define asm-fl-store
    (lambda (op flreg)
      (lambda (code* base index offset)
        (let ([dest (build-mem-opnd base index offset)])
          (case op
            [(store-single) (emit sse.movss (cons 'reg flreg) dest code*)]
            [(store-double) (emit sse.movsd (cons 'reg flreg) dest code*)])))))

  (define asm-fl-load
    (lambda (op flreg)
      (lambda (code* base index offset)
        (let ([src (build-mem-opnd base index offset)])
          (case op
            [(load-single) (emit sse.movss src (cons 'reg flreg) code*)]
            [(load-double) (emit sse.movsd src (cons 'reg flreg) code*)])))))

  (define asm-flt
    (lambda (code* src flonumreg)
      (Trivit (src)
        (let ([dest `(disp ,(constant flonum-data-disp) ,flonumreg)]
              [flreg (cons 'reg %flreg1)])
          (emit sse.cvtsi2sd src flreg
            (emit sse.movsd flreg dest code*))))))

  (define asm-flop-2
    (lambda (op)
      (lambda (code* src1 src2 dest)
        (let ([src1 `(disp ,(constant flonum-data-disp) ,src1)]
              [src2 `(disp ,(constant flonum-data-disp) ,src2)]
              [dest `(disp ,(constant flonum-data-disp) ,dest)])
          (let ([code* (emit sse.movsd (cons 'reg %flreg1) dest code*)])
            (let ([code* (case op
                           [(fl+) (emit sse.addsd src2 (cons 'reg %flreg1) code*)]
                           [(fl-) (emit sse.subsd src2 (cons 'reg %flreg1) code*)]
                           [(fl*) (emit sse.mulsd src2 (cons 'reg %flreg1) code*)]
                           [(fl/) (emit sse.divsd src2 (cons 'reg %flreg1) code*)])])
              (emit sse.movsd src1 (cons 'reg %flreg1) code*)))))))

  (define asm-flsqrt
    (lambda (code* src dest)
      (let ([src `(disp ,(constant flonum-data-disp) ,src)]
            [dest `(disp ,(constant flonum-data-disp) ,dest)])
        (emit sse.sqrtsd src (cons 'reg %flreg1)
          (emit sse.movsd (cons 'reg %flreg1) dest code*)))))

  (define asm-trunc
    (lambda (code* dest flonumreg)
      (Trivit (dest)
        (let ([src `(disp ,(constant flonum-data-disp) ,flonumreg)])
          (emit sse.cvttsd2si src dest code*)))))

  (define asm-load
    (lambda (type)
      (lambda (code* dest base index offset)
        (Trivit (dest)
          (let ([src (build-mem-opnd base index offset)])
            (case type
              [(integer-64 unsigned-64) (emit mov src dest code*)]
              [(integer-32) (emit movsl src dest code*)]
              [(unsigned-32) (emit movl src dest code*)] ; clears upper 32 bits of destination register
              [(integer-16) (emit movsw src dest code*)]
              [(unsigned-16) (emit movzw src dest code*)]
              [(integer-8) (emit movsb src dest code*)]
              [(unsigned-8) (emit movzb src dest code*)]
              [else (sorry! 'asm-load "unexpected mref type ~s" type)]))))))

  (define asm-store
    (lambda (type)
      (lambda (code* base index offset src)
        (define imm8 (lambda (n) `(imm ,(modulo n #x100))))
        (define imm16 (lambda (n) `(imm ,(modulo n #x10000))))
        (Trivit (src)
          (let ([dest (build-mem-opnd base index offset)])
            (record-case src
              [(imm) (n)
               (case type
                 [(integer-64 unsigned-64) (emit movi src dest code*)]
                 [(integer-32 unsigned-32) (emit movil src dest code*)]
                 [(integer-16 unsigned-16) (emit moviw (imm16 n) dest code*)]
                 [(integer-8 unsigned-8) (emit movib (imm8 n) dest code*)]
                 [else (sorry! 'asm-store "unexpected mset! type ~s" type)])]
              [(literal) stuff
               (case type
                 [(integer-64 unsigned-64) (emit movi src dest code*)]
                 [(integer-32 unsigned-32) (emit movil src dest code*)]
                 [(integer-16 unsigned-16) (emit moviw src dest code*)]
                 [(integer-8 unsigned-8) (emit movib src dest code*)]
                 [else (sorry! 'asm-store "unexpected mset! type ~s" type)])]
              [else
                (case type
                  [(integer-64 unsigned-64) (emit mov src dest code*)]
                  [(integer-32 unsigned-32) (emit movl src dest code*)]
                  [(integer-16 unsigned-16) (emit movw src dest code*)]
                  [(integer-8 unsigned-8) (emit movb src dest code*)]
                  [else (sorry! 'asm-store "unexpected mset! type ~s" type)])]))))))

  (define asm-swap
    (lambda (type)
      (lambda (code* dest src)
        (Trivit (dest)
          (safe-assert (equal? (Triv->rand src) dest))
          (emit bswap dest
            (case type
              [(integer-16) (emit asri '(imm 48) dest code*)]
              [(unsigned-16) (emit lsri '(imm 48) dest code*)]
              [(integer-32) (emit asri '(imm 32) dest code*)]
              [(unsigned-32) (emit lsri '(imm 32) dest code*)]
              [(integer-64 unsigned-64) code*]
              [else ($oops 'assembler-internal "unexpected asm-swap type argument ~s" type)]))))))

  (define asm-mul
    (lambda (code* dest src0 src1)
      (Trivit (dest src1)
        (safe-assert (equal? (Triv->rand src0) dest))
        (emit muls src1 dest code*))))

  (define asm-div
    (lambda (code* dest-rax src-rax src-rdx src2)
      (Trivit (src2)
        (safe-assert (and (eq? dest-rax %rax) (eq? src-rax %rax) (eq? src-rdx %rdx)))
        (emit divsax src2 code*))))

  (define asm-sext-rax->rdx
    (lambda (code* dest-rdx src-rax)
      (safe-assert (and (eq? dest-rdx %rdx) (eq? src-rax %rax)))
      (emit extad code*)))

  (define asm-muli
    (lambda (code* dest src0 src1)
      (Trivit (dest src0 src1)
        (emit mulsi src1 src0 dest code*))))

  (define-who asm-addop
    (lambda (op)
      (case op
        [(+) asm-add]
        [(logand) asm-logand]
        [(logor) asm-logor]
        [(logxor) asm-logxor]
        [else ($oops who "unsupported op ~s" op)])))

  (define asm-add
    (lambda (code* dest src0 src1)
      (Trivit (dest src1)
        (safe-assert (equal? (Triv->rand src0) dest))
        (record-case src1
          [(imm literal) stuff (emit addi src1 dest code*)]
          [else (emit add src1 dest code*)]))))

  (define asm-read-performance-monitoring-counter
    (lambda (code* dest src)
      ; rdx is an implied dest and included in info's kill list
      (safe-assert (eq? dest %rax))
      (safe-assert (eq? src %rcx))
      (emit rdpmc code*)))

  (define asm-read-time-stamp-counter
    (lambda (code* dest)
      ; rdx is an implied dest and included in info's kill list
      (safe-assert (eq? dest %rax))
      (emit rdtsc code*)))

  (define asm-inc-profile-counter
    (lambda (code* dest src)
      (Trivit (dest src)
        (record-case src
          [(imm) (n) (if (eqv? n 1) (emit inc dest code*) (emit addi src dest code*))]
          [else (emit add src dest code*)]))))

  (define-who asm-inc-cc-counter
    (lambda (code* base offset val)
      (let ([dest (nanopass-case (L16 Triv) offset
                    [(immediate ,imm) `(disp ,imm ,base)]
                    [,x `(index 0 ,x ,base)]
                    [else ($oops who "unexpected increment offset ~s" offset)])])
        (nanopass-case (L16 Triv) val
          [(immediate ,imm)
           (if (fx= imm 1)
               (emit inc dest code*)
               (emit addi `(imm ,imm) dest code*))]
          [,x (emit add (cons 'reg x) dest code*)]
          [else ($oops who "unsupported increment ~s" val)]))))

  (define asm-sub
    (lambda (code* dest src0 src1)
      (Trivit (dest src1)
        (safe-assert (equal? (Triv->rand src0) dest))
        (record-case src1
          [(imm literal) stuff (emit subi src1 dest code*)]
          [else (emit sub src1 dest code*)]))))

  (define asm-negate
    (lambda (code* dest src)
      (Trivit (dest)
        (safe-assert (equal? (Triv->rand src) dest))
        (emit neg dest code*))))

  (define asm-sub-negate
    (lambda (code* dest src0 src1)
      (Trivit (dest src1)
        (safe-assert (equal? (Triv->rand src0) dest))
        (let ([code* (emit neg dest code*)])
          (record-case src1
            [(imm literal) stuff (emit subi src1 dest code*)]
            [else (emit sub src1 dest code*)])))))

  (define asm-pop
    (lambda (code* dest)
      (Trivit (dest)
        (emit pop dest code*))))

  (define asm-return
    (lambda ()
      (emit addi '(imm 8) (cons 'reg %sp)
        (emit ret '()))))

  (define asm-locked-incr
    (lambda (code* base index offset)
      (let ([dest (build-mem-opnd base index offset)])
        (emit locked-inc dest code*))))

  (define asm-locked-decr
    (lambda (code* base index offset)
      (let ([dest (build-mem-opnd base index offset)])
        (emit locked-dec dest code*))))

  (define asm-pause
    (lambda (code*)
      (emit pause code*)))

  (define asm-exchange
    (lambda (code* dest src0 src1)
      (Trivit (dest src1)
        (safe-assert (equal? (Triv->rand src0) dest))
        (emit xchg src1 dest code*))))

  (define-who asm-shiftop
    (lambda (op)
      (case op
        [(sll) asm-sll]
        [(srl) asm-srl]
        [(sra) asm-sra]
        [else ($oops who "unsupported op ~s" op)])))

  (define asm-sll
    (lambda (code* dest src0 src1)
      (Trivit (dest src1)
        (safe-assert (equal? (Triv->rand src0) dest))
        (record-case src1
          [(imm literal) stuff (emit asli src1 dest code*)]
          [else
            (safe-assert (ax-register? src1 %rcx))
            (emit asl dest code*)]))))

  (define asm-srl
    (lambda (code* dest src0 src1)
      (Trivit (dest src1)
        (safe-assert (equal? (Triv->rand src0) dest))
        (record-case src1
          [(imm literal) stuff (emit lsri src1 dest code*)]
          [else
            (safe-assert (ax-register? src1 %rcx))
            (emit lsr dest code*)]))))

  (define asm-sra
    (lambda (code* dest src0 src1)
      (Trivit (dest src1)
        (safe-assert (equal? (Triv->rand src0) dest))
        (record-case src1
          [(imm literal) stuff (emit asri src1 dest code*)]
          [else
            (safe-assert (ax-register? src1 %rcx))
            (emit asr dest code*)]))))

  (define asm-logand
    (lambda (code* dest src0 src1)
      (Trivit (dest src1)
        (safe-assert (equal? (Triv->rand src0) dest))
        (record-case src1
          [(imm literal) stuff (emit andi src1 dest code*)]
          [else (emit and src1 dest code*)]))))

  (define asm-logor
    (lambda (code* dest src0 src1)
      (Trivit (dest src1)
        (safe-assert (equal? (Triv->rand src0) dest))
        (record-case src1
          [(imm literal) stuff (emit ori src1 dest code*)]
          [else (emit or src1 dest code*)]))))

  (define asm-logxor
    (lambda (code* dest src0 src1)
      (Trivit (dest src1)
        (safe-assert (equal? (Triv->rand src0) dest))
        (record-case src1
          [(imm literal) stuff (emit xori src1 dest code*)]
          [else (emit xor src1 dest code*)]))))

  (define asm-lognot
    (lambda (code* dest src)
      (Trivit (dest)
        (safe-assert (equal? (Triv->rand src) dest))
        (emit not dest code*))))

  (define asm-lea1
    (lambda (offset)
      (rec asm-lea1-internal
        (lambda (code* dest src)
          (if (eq? src dest)
              (Trivit (dest)
                (emit addi `(imm ,offset) dest code*))
              (Trivit (dest)
                (emit lea `(disp ,offset ,src) dest code*)))))))

  (define asm-lea2
    (lambda (offset)
      (rec asm-lea2-internal
        (lambda (code* dest src1 src2)
          (cond
            [(and (eq? src1 dest) (fx= offset 0))
             (Trivit (dest src2)
               (emit add src2 dest code*))]
            [(and (eq? src2 dest) (fx= offset 0))
             (Trivit (dest src1)
               (emit add src1 dest code*))]
            [else
              (Trivit (dest)
                (emit lea `(index ,offset ,src1 ,src2)
                  dest code*))])))))

  (define asm-logtest
    (lambda (i? info)
      (lambda (l1 l2 offset x y)
        (Trivit (x y)
          (safe-assert
            (record-case x
              [(disp reg index literal@) stuff #t]
              [else #f]))
          (values
            (record-case y
              [(imm) (n)
               (if (and (fixnum? n)
                        (fx= (fxlogand n #xff) n)
                        (record-case x
                          [(reg) r #t]
                          ; counting on little-endian byte order
                          [(disp index) stuff #t]))
                   (emit testib y x '())
                   (emit testi y x '()))]
              [(literal) stuff (emit testi y x '())]
              [else (emit test x y '())])
            (let-values ([(l1 l2) (if i? (values l2 l1) (values l1 l2))])
              (asm-conditional-jump info l2 l1 offset)))))))

  (define asm-fl-relop
    (lambda (info)
      (lambda (l1 l2 offset x y)
        (values
          (let ([x `(disp ,(constant flonum-data-disp) ,x)]
                [y `(disp ,(constant flonum-data-disp) ,y)])
            (emit sse.movsd y (cons 'reg %flreg1)
              (emit sse.ucomisd x (cons 'reg %flreg1) '())))
          (asm-conditional-jump info l1 l2 offset)))))

  (define asm-relop
    (lambda (info)
      (rec asm-relop-internal
        (lambda (l1 l2 offset x y)
          (Trivit (x y)
            (safe-assert
              (record-case x
                [(reg disp index) ignore #t]
                [else #f]))
            (values
              (record-case y
                [(imm literal) stuff (emit cmpi y x '())]
                [else (emit cmp y x '())])
              (asm-conditional-jump info l1 l2 offset)))))))

  (define asm-condition-code
    (lambda (info)
      (rec asm-check-flag-internal
        (lambda (l1 l2 offset)
          (values '() (asm-conditional-jump info l1 l2 offset))))))

  ; TODO: should this also handle pushil?
  (define asm-push
    (lambda (code* x)
      (Trivit (x)
        (emit push x code*))))

  (define asm-save-flrv
    (lambda (code*)
      ; save float return value in case we're calling into C
      ; from the foreign-procedure return path to allocate a flonum
      ; return value.  need 16 bytes rather than 8 to maintain
      ; 16-byte stack alignment
      (emit subi '(imm 16) (cons 'reg %sp)
        (emit sse.movsd (cons 'reg %Cfpretval) `(disp 0 ,%sp) code*))))

  (define asm-restore-flrv
    (lambda (code*)
      (emit sse.movsd `(disp 0 ,%sp) (cons 'reg %Cfpretval)
        (emit addi '(imm 16) (cons 'reg %sp) code*))))

  (define asm-library-jump
    (lambda (l)
      (asm-helper-jump '()
        `(x86_64-jump ,(constant code-data-disp) (library-code ,(libspec-label-libspec l))))))

  (define asm-library-call
    (lambda (libspec)
      (let ([target `(x86_64-call ,(constant code-data-disp) (library-code ,libspec))])
        (rec asm-asm-call-internal
          (lambda (code* jmp-reg . ignore)
            (asm-helper-call code* target jmp-reg))))))

  (define asm-c-simple-call
    (lambda (entry)
      (rec asm-c-simple-call-internal
        (lambda (code* jmp-reg)
          (let ([sp-opnd (cons 'reg %sp)])
            (if (fx= entry (lookup-c-entry Sreturn))
                ; pretend S_generic_invoke called Sreturn directly by wiping out
                ; pad added by invoke-prelude and jumping rather than calling
                (emit addi '(imm 8) sp-opnd
                  (asm-helper-jump code* `(x86_64-jump 0 (entry ,entry))))
                (let ([target `(x86_64-call 0 (entry ,entry))])
                  (if-feature windows
                    ; must leave room for callee to store argument registers,
                    ; even if there are no arguments
                    (emit subi '(imm 32) sp-opnd
                      (asm-helper-call (emit addi '(imm 32) sp-opnd code*) target jmp-reg))
                    (asm-helper-call code* target jmp-reg)))))))))

  (define asm-get-tc
    (let ([target `(x86_64-call 0 (entry ,(lookup-c-entry get-thread-context)))])
      (lambda (code* jmp-reg) ; dest is ignored, since it is always the first C argument (rax in this case)
        (asm-helper-call code* target jmp-reg))))

  (define asm-indirect-call
    (lambda (code* t . ignore)
      ; NB: c-call is already required to be a register or memory operand, so
      ; no need to build a relocation entry.
      (Trivit (t)
        (emit call t code*))))

  (define asm-direct-jump
    (lambda (l offset)
      (asm-helper-jump '() (make-funcrel 'x86_64-jump l offset))))

  (define asm-literal-jump
    (lambda (info)
      (asm-helper-jump '()
        `(x86_64-jump ,(info-literal-offset info)
           (,(info-literal-type info) ,(info-literal-addr info))))))

  (define asm-indirect-jump
    (lambda (t)
      (Trivit (t)
        (safe-assert (record-case t [(imm) (n) (signed-32? n)] [else #t]))
        (emit jmp t '()))))

  (define-who asm-return-address
    (lambda (dest l incr-offset next-addr)
      (make-rachunk dest l incr-offset next-addr
        (or (cond
              [(local-label-offset l) =>
               (lambda (offset)
                 (let ([disp (fx- next-addr (fx- offset incr-offset))])
                   (and (signed-32? disp)
                        (Trivit (dest)
                          (emit lea `(riprel ,disp) dest '())))))]
               [else #f])
            (asm-move '() dest (with-output-language (L16 Triv) `(label-ref ,l ,incr-offset)))))))

  (define asm-jump
    (lambda (l next-addr)
      (make-gchunk l next-addr
        (cond
          [(local-label-offset l) =>
           (lambda (offset)
             (let ([disp (fx- next-addr offset)])
               (safe-assert (signed-32? disp))
               (if (fx= disp 0)
                   '()
                   (emit bra `(label ,disp ,l) '()))))]
          [else
           ; label must be somewhere above.  generate something so that a hard loop
           ; doesn't get dropped.  this also has some chance of being the right size
           ; for the final branch instruction.
           (emit bra `(label 0 ,l) '())]))))

  (define-who asm-conditional-jump
    (lambda (info l1 l2 next-addr)
      (define get-disp-opnd
        (lambda (next-addr l)
          (cond
            [(and (local-label? l) (local-label-offset l)) =>
             (lambda (offset)
               (let ([disp (fx- next-addr offset)])
                 (safe-assert (signed-32? disp))
                 (values disp `(label ,disp ,l))))]
            [else (values 0 `(label 0 ,l))])))
      (safe-assert (and (local-label? l1) (local-label? l2)))
      (let ([type (info-condition-code-type info)]
            [reversed? (info-condition-code-reversed? info)])
        (make-cgchunk info l1 l2 next-addr
          (let ()
            (define-syntax pred-case
              (lambda (x)
                (define build-bop-seq
                  (lambda (bop opnd1 opnd2 l2 body)
                    #`(let ([code* (emit #,bop #,opnd1 code*)])
                        (let-values ([(disp #,opnd2) (get-disp-opnd (fx+ next-addr (asm-size* code*)) #,l2)])
                          #,body))))
                (define handle-or
                  (lambda (e opnd l)
                    (syntax-case e (or)
                      [(or bop1 bop2)
                       (build-bop-seq #'bop2 opnd opnd l
                         #`(emit bop1 #,opnd code*))]
                      [bop #`(emit bop #,opnd code*)])))
                (define handle-reverse
                  (lambda (e opnd l)
                    (syntax-case e (r?)
                      [(r? c1 c2) #`(if reversed? #,(handle-or #'c1 opnd l) #,(handle-or #'c2 opnd l))]
                      [_ (handle-or e opnd l)])))
                (define handle-inverse
                  (lambda (e)
                    (syntax-case e (i?)
                      [(i? c1 c2)
                       #`(cond
                           [(fx= disp1 0) #,(handle-reverse #'c1 #'opnd2 #'l2)]
                           [(fx= disp2 0) #,(handle-reverse #'c2 #'opnd1 #'l1)]
                           [else #,(build-bop-seq #'bra #'opnd2 #'opnd1 #'l1
                                     (handle-reverse #'c2 #'opnd1 #'l1))])]
                      [_ #`(cond ; treating e as c1: inverted condition, branching to false label
                             [(fx= disp1 0) #,(handle-reverse e #'opnd2 #'l2)]
                             [else #,(build-bop-seq #'bra #'opnd1 #'opnd2 #'l2
                                       (handle-reverse e #'opnd2 #'l2))])])))
                (syntax-case x ()
                  [(_ [(pred ...) cl-body] ...)
                   (with-syntax ([(cl-body ...) (map handle-inverse #'(cl-body ...))])
                     #'(let ([code* '()])
                         (let-values ([(disp1 opnd1) (get-disp-opnd next-addr l1)]
                                      [(disp2 opnd2) (get-disp-opnd next-addr l2)])
                           (case type
                             [(pred ...) cl-body] ...
                             [else ($oops who "~s branch type is currently unsupported" type)]))))])))
            (pred-case
              [(eq?) (i? bne beq)]
              [(u<) (i? (r? bls bcc) (r? bhi bcs))]
              [(<) (i? (r? ble bge) (r? bgt blt))]
              [(<=) (i? (r? blt bgt) (r? bge ble))]
              [(>) (i? (r? bge ble) (r? blt bgt))]
              [(>=) (i? (r? bgt blt) (r? ble bge))]
              [(overflow multiply-overflow) (i? bvc bvs)]
              [(carry) (i? bcc bcs)]
              ; unordered: zf,pf,cf <- 111; gt: 000; lt: 001; eq: 100
              ; reversed & inverted: !(fl< y x) = !(fl> x y) iff zf = 1 & cf = 1
              [(fl<) bls]
              ; reversed & inverted: !(fl<= y x) = !(fl>= x y) iff cf = 1
              [(fl<=) bcs]
              ; inverted: !(fl= x y) iff zf = 0 or cf (or pf) = 1
              [(fl=) (or bne bcs)]))))))

  (define asm-data-label
    (lambda (code* l offset func code-size)
      (let ([rel (make-funcrel 'abs l offset)])
        (cons* rel (aop-cons* `(asm "mrv point:" ,rel) code*)))))

  (define asm-helper-jump
    (lambda (code* reloc)
      (let ([jmp-reg (cons 'reg %ts)])
        (emit movi '(imm #xffffffff) jmp-reg
          (emit jmp jmp-reg
            (asm-helper-relocation code* reloc))))))

  (define asm-kill
    (lambda (code* dest)
      code*))

  (define asm-helper-call
    (lambda (code* reloc jmp-reg)
      (emit movi '(imm #xffffffff) (cons 'reg jmp-reg)
        (emit call (cons 'reg jmp-reg)
          (asm-helper-relocation code* reloc)))))

  (define asm-helper-relocation
    (lambda (code* reloc)
      (cons* reloc (aop-cons* `(asm "relocation:" ,reloc) code*))))

  (define asm-rp-header
    (let ([mrv-error `(abs ,(constant code-data-disp)
                        (library-code ,(lookup-libspec values-error)))])
      (lambda (code* mrvl fs lpm func code-size)
        (let ([size (constant-case ptr-bits [(32) 'long] [(64) 'quad])])
          (cons*
            (if (target-fixnum? lpm)
                `(,size . ,(fix lpm))
                `(abs 0 (object ,lpm)))
            (aop-cons* `(asm livemask: ,(format "~b" lpm))
              '(code-top-link)
              (aop-cons* `(asm code-top-link)
                `(,size . ,fs)
                (aop-cons* `(asm "frame size:" ,fs)
                  (if mrvl
                      (asm-data-label code* mrvl 0 func code-size)
                      (cons*
                        mrv-error
                        (aop-cons* `(asm "mrv point:" ,mrv-error)
                          code*)))))))))))

  (define-syntax asm-enter
    (lambda (x)
      (syntax-case x ()
        [(k e)
         (with-implicit (k %seq %inline)
           #'(%seq
               ; adjust to 16-byte boundary, accounting for 8-byte return address pushed by call
               (set! ,%sp ,(%inline - ,%sp (immediate 8)))
               ,e))])))

  (module (asm-foreign-call asm-foreign-callable)
    (if-feature windows
      (begin
        (define make-vint (lambda () (vector %Carg1 %Carg2 %Carg3 %Carg4)))
        (define make-vfp (lambda () (vector %Cfparg1 %Cfparg2 %Cfparg3 %Cfparg4))))
      (begin
        (define make-vint (lambda () (vector %Carg1 %Carg2 %Carg3 %Carg4 %Carg5 %Carg6)))
        (define make-vfp (lambda () (vector %Cfparg1 %Cfparg2 %Cfparg3 %Cfparg4 %Cfparg5 %Cfparg6 %Cfparg7 %Cfparg8)))))

    (define asm-foreign-call
      (with-output-language (L13 Effect)
        (letrec ([load-double-stack
                  (lambda (offset)
                    (lambda (x) ; requires var
                      (%seq
                        (inline ,(make-info-loadfl %flreg1) ,%load-double ,x ,%zero ,(%constant flonum-data-disp))
                        (inline ,(make-info-loadfl %flreg1) ,%store-double ,%sp ,%zero (immediate ,offset)))))]
                 [load-single-stack
                  (lambda (offset)
                    (lambda (x) ; requires var
                      (%seq
                        (inline ,(make-info-loadfl %flreg1) ,%load-double->single ,x ,%zero ,(%constant flonum-data-disp))
                        (inline ,(make-info-loadfl %flreg1) ,%store-single ,%sp ,%zero (immediate ,offset)))))]
                 [load-int-stack
                  (lambda (offset)
                    (lambda (rhs) ; requires rhs
                      `(set! ,(%mref ,%sp ,offset) ,rhs)))]
                 [load-double-reg
                  (lambda (fpreg)
                    (lambda (x) ; requires var
                      `(inline ,(make-info-loadfl fpreg) ,%load-double ,x ,%zero ,(%constant flonum-data-disp))))]
                 [load-single-reg
                  (lambda (fpreg)
                    (lambda (x) ; requires var
                      `(inline ,(make-info-loadfl fpreg) ,%load-double->single ,x ,%zero ,(%constant flonum-data-disp))))]
                 [load-int-reg
                  (lambda (type ireg)
                    (lambda (x)
                      (cond
                        [(nanopass-case (Ltype Type) type [(fp-integer ,bits) (fx= bits 32)] [else #f])
                         ; original code generated movil or movl
                         (nanopass-case (L13 Rhs) x
                           [,t `(set! ,ireg ,(%inline zext32 ,t))]
                           ; here we use ireg twice to get around needing a temporary when
                           ; x is a non-triv right-hand-side
                           [else (%seq (set! ,ireg ,x) (set! ,ireg ,(%inline zext32 ,ireg)))])]
                        [else `(set! ,ireg ,x)])))]
                 [do-args
                  (lambda (types vint vfp)
                    (if-feature windows
                      (let loop ([types types] [locs '()] [regs '()] [i 0] [isp 0])
                        (if (null? types)
                            (values isp locs regs)
                            (nanopass-case (Ltype Type) (car types)
                              [(fp-double-float)
                               (if (< i 4)
                                   (loop (cdr types)
                                     (cons (load-double-reg (vector-ref vfp i)) locs)
                                     regs (fx+ i 1) isp)
                                   (loop (cdr types)
                                     (cons (load-double-stack isp) locs)
                                     regs i (fx+ isp 8)))]
                              [(fp-single-float)
                               (if (< i 4)
                                   (loop (cdr types)
                                     (cons (load-single-reg (vector-ref vfp i)) locs)
                                     regs (fx+ i 1) isp)
                                   (loop (cdr types)
                                     (cons (load-single-stack isp) locs)
                                     regs i (fx+ isp 8)))]
                              [else
                               (if (< i 4)
                                   (let ([reg (vector-ref vint i)])
                                     (loop (cdr types)
                                       (cons (load-int-reg (car types) reg) locs)
                                       (cons reg regs)
                                       (fx+ i 1) isp))
                                   (loop (cdr types)
                                     (cons (load-int-stack isp) locs)
                                     regs i (fx+ isp 8)))])))
                      (let loop ([types types] [locs '()] [regs '()] [iint 0] [ifp 0] [isp 0])
                        (if (null? types)
                            (values isp locs regs)
                            (nanopass-case (Ltype Type) (car types)
                              [(fp-double-float)
                               (if (< ifp 8)
                                   (loop (cdr types)
                                     (cons (load-double-reg (vector-ref vfp ifp)) locs)
                                     regs iint (fx+ ifp 1) isp)
                                   (loop (cdr types)
                                     (cons (load-double-stack isp) locs)
                                     regs iint ifp (fx+ isp 8)))]
                              [(fp-single-float)
                               (if (< ifp 8)
                                   (loop (cdr types)
                                     (cons (load-single-reg (vector-ref vfp ifp)) locs)
                                     regs iint (fx+ ifp 1) isp)
                                   (loop (cdr types)
                                     (cons (load-single-stack isp) locs)
                                     regs iint ifp (fx+ isp 8)))]
                              [else
                               (if (< iint 6)
                                   (let ([reg (vector-ref vint iint)])
                                     (loop (cdr types)
                                       (cons (load-int-reg (car types) reg) locs)
                                       (cons reg regs)
                                       (fx+ iint 1) ifp isp))
                                   (loop (cdr types)
                                     (cons (load-int-stack isp) locs)
                                     regs iint ifp (fx+ isp 8)))])))))])
          (define returnem
            (lambda (frame-size locs ccall r-loc)
             ; need to maintain 16-byte alignment, ignoring the return address
             ; pushed by call instruction, which counts as part of callee's frame
             ; tc is callee-save; no need to save
              (let ([frame-size (logand (+ frame-size 15) -16)])
                (values (lambda ()
                          (if (fx= frame-size 0)
                              `(nop)
                              `(set! ,%sp ,(%inline - ,%sp (immediate ,frame-size)))))
                  (reverse locs)
                  ccall
                  r-loc
                  (lambda ()
                    (if (fx= frame-size 0)
                        `(nop)
                        `(set! ,%sp ,(%inline + ,%sp (immediate ,frame-size)))))))))
          (lambda (info)
            (safe-assert (reg-callee-save? %tc)) ; no need to save-restore
            (let ([conv (info-foreign-conv info)]
                  [arg-type* (info-foreign-arg-type* info)]
                  [result-type (info-foreign-result-type info)])
              (with-values (do-args arg-type* (make-vint) (make-vfp))
                (lambda (frame-size locs live*)
                  (returnem frame-size locs
                    (lambda (t0)
                      (if-feature windows
                        (%seq
                          (set! ,%sp ,(%inline - ,%sp (immediate 32)))
                          (inline ,(make-info-kill*-live* (reg-list %rax) live*) ,%c-call ,t0)
                          (set! ,%sp ,(%inline + ,%sp (immediate 32))))
                        `(inline ,(make-info-kill*-live* (reg-list %rax) live*) ,%c-call ,t0)))
                    (nanopass-case (Ltype Type) result-type
                      [(fp-double-float)
                       (lambda (lvalue)
                         `(inline ,(make-info-loadfl %Cfpretval) ,%store-double ,lvalue ,%zero
                            ,(%constant flonum-data-disp)))]
                      [(fp-single-float)
                       (lambda (lvalue)
                         `(inline ,(make-info-loadfl %Cfpretval) ,%store-single->double ,lvalue ,%zero
                            ,(%constant flonum-data-disp)))]
                      [(fp-integer ,bits)
                       (case bits
                         [(8) (lambda (lvalue) `(set! ,lvalue ,(%inline sext8 ,%rax)))]
                         [(16) (lambda (lvalue) `(set! ,lvalue ,(%inline sext16 ,%rax)))]
                         [(32) (lambda (lvalue) `(set! ,lvalue ,(%inline sext32 ,%rax)))]
                         [(64) (lambda (lvalue) `(set! ,lvalue ,%rax))]
                         [else ($oops 'assembler-internal
                                 "unexpected asm-foreign-procedures fp-integer size ~s"
                                 bits)])]
                      [(fp-unsigned ,bits)
                       (case bits
                         [(8) (lambda (lvalue) `(set! ,lvalue ,(%inline zext8 ,%rax)))]
                         [(16) (lambda (lvalue) `(set! ,lvalue ,(%inline zext16 ,%rax)))]
                         [(32) (lambda (lvalue) `(set! ,lvalue ,(%inline zext32 ,%rax)))]
                         [(64) (lambda (lvalue) `(set! ,lvalue ,%rax))]
                         [else ($oops 'assembler-internal
                                 "unexpected asm-foreign-procedures fp-unsigned size ~s"
                                 bits)])]
                      [else (lambda (lvalue) `(set! ,lvalue ,%rax))])))))))))

    (define asm-foreign-callable
      #|
      Windows:
                   Frame Layout
                   +---------------------------+
                   |                           |
                   |    incoming stack args    |
                   |                           |
                   +---------------------------+ <- 16-byte boundary
                   |                           | 
                   |  space for register args  | four quads
            sp+80: |                           | 
                   +---------------------------+ <- 16-byte boundary
                   |   incoming return address | one quad
      incoming sp: +---------------------------+
                   |                           |
                   |   callee-save registers   | RBX, RBP, RDI, RSI, R12, R13, R14, R15 (8 quads)
                   |                           | 
                   +---------------------------+
                   |         pad word          | one quad
             sp+0: +---------------------------+<- 16-byte boundary
      
      
      Standard:
                   Frame Layout
                   +---------------------------+
                   |                           |
                   |    incoming stack args    |
           sp+176: |                           |
                   +---------------------------+ <- 16-byte boundary
                   |   incoming return address | one quad
                   +---------------------------+
                   |         pad word          | one quad
                   +---------------------------+<- 16-byte boundary
                   |                           | 
                   |    saved register args    | space for Carg*, Cfparg* (14 quads)
            sp+48: |                           |
                   +---------------------------+<- 16-byte boundary
                   |                           |
                   |   callee-save registers   | RBX, RBP, R12, R13, R14, R15 (6 quads)
                   |                           | 
             sp+0: +---------------------------+<- 16-byte boundary
      |#
      (with-output-language (L13 Effect)
        (let ()
          (define load-double-stack
            (lambda (offset)
              (lambda (x) ; requires var
                (%seq
                  (inline ,(make-info-loadfl %flreg1) ,%load-double ,%sp ,%zero (immediate ,offset))
                  (inline ,(make-info-loadfl %flreg1) ,%store-double ,x ,%zero ,(%constant flonum-data-disp))))))
          (define load-single-stack
            (lambda (offset)
              (lambda (x) ; requires var
                (%seq
                  (inline ,(make-info-loadfl %flreg1) ,%load-single->double ,%sp ,%zero (immediate ,offset))
                  (inline ,(make-info-loadfl %flreg1) ,%store-double ,x ,%zero ,(%constant flonum-data-disp))))))
          (define load-int-stack
            (lambda (type offset)
              (lambda (lvalue)
                (nanopass-case (Ltype Type) type
                  [(fp-integer ,bits)
                   (case bits
                     [(8) `(set! ,lvalue (inline ,(make-info-load 'integer-8 #f) ,%load ,%sp ,%zero (immediate ,offset)))]
                     [(16) `(set! ,lvalue (inline ,(make-info-load 'integer-16 #f) ,%load ,%sp ,%zero (immediate ,offset)))]
                     [(32) `(set! ,lvalue (inline ,(make-info-load 'integer-32 #f) ,%load ,%sp ,%zero (immediate ,offset)))]
                     [(64) `(set! ,lvalue ,(%mref ,%sp ,offset))]
                     [else ($oops 'assembler-internal
                             "unexpected load-int-stack fp-integer size ~s"
                             bits)])]
                  [(fp-unsigned ,bits)
                   (case bits
                     [(8) `(set! ,lvalue (inline ,(make-info-load 'unsigned-8 #f) ,%load ,%sp ,%zero (immediate ,offset)))]
                     [(16) `(set! ,lvalue (inline ,(make-info-load 'unsigned-16 #f) ,%load ,%sp ,%zero (immediate ,offset)))]
                     [(32) `(set! ,lvalue (inline ,(make-info-load 'unsigned-32 #f) ,%load ,%sp ,%zero (immediate ,offset)))]
                     [(64) `(set! ,lvalue ,(%mref ,%sp ,offset))]
                     [else ($oops 'assembler-internal
                             "unexpected load-int-stack fp-unsigned size ~s"
                             bits)])]
                  [else `(set! ,lvalue ,(%mref ,%sp ,offset))]))))
          (define save-arg-regs
            (lambda (types)
              (define vint (make-vint))
              (define vfp (make-vfp))
              (if-feature windows
                (let f ([types types] [i 0] [isp 8])
                  (if (or (null? types) (fx= i 4))
                      `(nop)
                      (nanopass-case (Ltype Type) (car types)
                        [(fp-double-float)
                         (if (< i 4)
                             (%seq
                               (inline ,(make-info-loadfl (vector-ref vfp i)) ,%store-double
                                 ,%sp ,%zero (immediate ,isp))
                               ,(f (cdr types) (fx+ i 1) (fx+ isp 8)))
                             (f (cdr types) i isp))]
                        [(fp-single-float)
                         (if (< i 4)
                             (%seq
                               (inline ,(make-info-loadfl (vector-ref vfp i)) ,%store-single
                                 ,%sp ,%zero (immediate ,isp))
                               ,(f (cdr types) (fx+ i 1) (fx+ isp 8)))
                             (f (cdr types) i isp))]
                        [else
                         (if (< i 4)
                             (%seq
                               (set! ,(%mref ,%sp ,isp) ,(vector-ref vint i))
                               ,(f (cdr types) (fx+ i 1) (fx+ isp 8)))
                             (f (cdr types) i isp))])))
                (let f ([types types] [iint 0] [ifp 0] [isp 48])
                  (if (or (null? types) (and (fx>= iint 6) (fx>= ifp 8)))
                      `(nop)
                      (nanopass-case (Ltype Type) (car types)
                        [(fp-double-float)
                         (if (< ifp 8)
                             (%seq
                               (inline ,(make-info-loadfl (vector-ref vfp ifp)) ,%store-double
                                 ,%sp ,%zero (immediate ,isp))
                               ,(f (cdr types) iint (fx+ ifp 1) (fx+ isp 8)))
                             (f (cdr types) iint ifp isp))]
                        [(fp-single-float)
                         (if (< ifp 8)
                             (%seq
                               (inline ,(make-info-loadfl (vector-ref vfp ifp)) ,%store-single
                                 ,%sp ,%zero (immediate ,isp))
                               ,(f (cdr types) iint (fx+ ifp 1) (fx+ isp 8)))
                             (f (cdr types) iint ifp isp))]
                        [else
                         (if (< iint 6)
                             (%seq
                               (set! ,(%mref ,%sp ,isp) ,(vector-ref vint iint))
                               ,(f (cdr types) (fx+ iint 1) ifp (fx+ isp 8)))
                             (f (cdr types) iint ifp isp))]))))))
          (define do-stack
            (lambda (types)
             ; risp is where incoming register args are stored
             ; sisp is where incoming stack args are stored
              (if-feature windows
                (let f ([types types] [locs '()] [isp 80])
                  (if (null? types)
                      locs
                      (f (cdr types)
                         (cons
                           (nanopass-case (Ltype Type) (car types)
                             [(fp-double-float) (load-double-stack isp)]
                             [(fp-single-float) (load-single-stack isp)]
                             [else (load-int-stack (car types) isp)])
                           locs)
                         (fx+ isp 8))))
                (let f ([types types] [locs '()] [iint 0] [ifp 0] [risp 48] [sisp 176])
                  (if (null? types)
                      locs
                      (nanopass-case (Ltype Type) (car types)
                        [(fp-double-float)
                         (if (= ifp 8)
                             (f (cdr types)
                               (cons (load-double-stack sisp) locs)
                               iint ifp risp (fx+ sisp 8))
                             (f (cdr types)
                               (cons (load-double-stack risp) locs)
                               iint (fx+ ifp 1) (fx+ risp 8) sisp))]
                        [(fp-single-float)
                         (if (= ifp 8)
                             (f (cdr types)
                               (cons (load-single-stack sisp) locs)
                               iint ifp risp (fx+ sisp 8))
                             (f (cdr types)
                               (cons (load-single-stack risp) locs)
                               iint (fx+ ifp 1) (fx+ risp 8) sisp))]
                        [else
                         (if (= iint 6)
                             (f (cdr types)
                               (cons (load-int-stack (car types) sisp) locs)
                               iint ifp risp (fx+ sisp 8))
                             (f (cdr types)
                               (cons (load-int-stack (car types) risp) locs)
                               (fx+ iint 1) ifp (fx+ risp 8) sisp))]))))))
          (lambda (info)
            (let ([conv (info-foreign-conv info)]
                  [arg-type* (info-foreign-arg-type* info)]
                  [result-type (info-foreign-result-type info)])
              (let ([locs (do-stack arg-type*)])
                (values
                  (lambda ()
                    (%seq
                      ,(if-feature windows
                         (%seq
                           ,(save-arg-regs arg-type*)
                           ,(%inline push ,%rbx)
                           ,(%inline push ,%rbp)
                           ,(%inline push ,%rdi)
                           ,(%inline push ,%rsi)
                           ,(%inline push ,%r12)
                           ,(%inline push ,%r13)
                           ,(%inline push ,%r14)
                           ,(%inline push ,%r15)
                           (set! ,%sp ,(%inline - ,%sp (immediate 8))))
                         (%seq
                           (set! ,%sp ,(%inline - ,%sp (immediate 120)))
                           ,(%inline push ,%rbx)
                           ,(%inline push ,%rbp)
                           ,(%inline push ,%r12)
                           ,(%inline push ,%r13)
                           ,(%inline push ,%r14)
                           ,(%inline push ,%r15)
                           ,(save-arg-regs arg-type*)))
                      ,(if-feature pthreads
                         (%seq 
                           (set! ,%rax ,(%inline get-tc))
                           (set! ,%tc ,%rax))
                         `(set! ,%tc (literal ,(make-info-literal #f 'entry (lookup-c-entry thread-context) 0))))))
                  (reverse locs)
                  (lambda (fv* Scall->result-type)
                    (in-context Tail
                      (%seq
                        ,(if-feature windows
                           (%seq
                             (set! ,%sp ,(%inline + ,%sp (immediate 8)))
                             (set! ,%r15 ,(%inline pop))
                             (set! ,%r14 ,(%inline pop))
                             (set! ,%r13 ,(%inline pop))
                             (set! ,%r12 ,(%inline pop))
                             (set! ,%rsi ,(%inline pop))
                             (set! ,%rdi ,(%inline pop))
                             (set! ,%rbp ,(%inline pop))
                             (set! ,%rbx ,(%inline pop)))
                           (%seq
                             (set! ,%r15 ,(%inline pop))
                             (set! ,%r14 ,(%inline pop))
                             (set! ,%r13 ,(%inline pop))
                             (set! ,%r12 ,(%inline pop))
                             (set! ,%rbp ,(%inline pop))
                             (set! ,%rbx ,(%inline pop))
                             (set! ,%sp ,(%inline + ,%sp (immediate 120)))))
                        (jump (literal ,(make-info-literal #f 'entry Scall->result-type 0))
                          (,%rbx ,%rbp ,%r12 ,%r13 ,%r14 ,%r15 ,fv* ...)))))))))))))
  )
