This repository has been archived on 2022-08-10. You can view files and clone it, but cannot push or open issues or pull requests.
chez-openbsd/s/arm32.ss
2022-07-29 15:12:07 +02:00

3098 lines
135 KiB
Scheme

;;; arm32.ss
;;; Copyright 1984-2017 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
;;; ABI:
;;; Register usage:
;;; r0-r3 aka a1-a4: C argument registers, caller-save
;;; r4-r8, r10, r11 aka v1-v5, v7, v8: callee-save
;;; r9 aka v6, sb, or tr: platform-specific, callee-save
;;; r12 aka ip: caller-save (possibly usurped by linker at call boundaries)
;;; r13 aka sp: C stack pointer
;;; r14 aka lr: link register
;;; r15 aka pc: program counter
;;; --------
;;; s0-s31: single-precision registers (with vfp-v2) overlap with d0-d15
;;; d0-d15: double-precision registers (with vfp-v2)
;;; d16-d31: double-precision registers (with vfp-v3)
;;; Alignment:
;;; double-floats & 64-bit integers are 8-byte aligned in structs
;;; double-floats & 64-bit integers are 8-byte aligned on the stack
;;; stack must be 8-byte aligned at call boundaries (otherwise 4-byte)
;;; Parameter passing:
;;; 8- and 16-bit integer arguments zero- or sign-extended to 32-bits
;;; 32-bit integer arguments passed in a1-a4, then on stack
;;; 64-bit integer arguments passed in a1 or a3, then on stack
;;; little-endian: a1 (a3) holds lsw, a2 (a4) holds msw
;;; big-endian: a1 (a3) holds msw, a2 (a4) holds lsw
;;; 8- and 16-bit integer return value zero- or sign-extended to 32-bits
;;; 32-bit integer return value returned in r0 (aka a1)
;;; 64-bit integer return value passed in r0 & r1 (aka a1 & a2)
;;; little-endian: r0 holds lsw, r1 holds msw
;;; big-endian: r0 holds msw, r1 holds lsw
;;; single-floats passed in s0-s15
;;; double-floats passed in d0-d7 (overlapping single)
;;; float return value returned in s0 or d0
;;; must allocate to a single-float reg if it's passed by for double-float alignment
;;; (e.g., single, double, single => s0, d1, s1)
;;; ... unless a double has been stack-allocated
;;; (e.g., 15 singles, double => s0-s14, stack, stack)
;;; stack grows downwards. first stack args passed at lowest new frame address.
;;; return address passed in LR
;;; questions:
;;; least significant bit is always designated as bit 0...how does this affect
;;; bit fields in big-endian mode?
;;; meta questions:
;;; should we have both little- and big-endian support?
;;; can pidora (or some other linux distribution) run using both little- and big-endian modes?
(define-registers
(reserved
[%tc %r9 #t 9]
[%sfp %r10 #t 10]
[%ap %r5 #t 5]
#;[%esp]
#;[%eap]
[%trap %r8 #t 8])
(allocable
[%ac0 %r4 #t 4]
[%xp %r6 #t 6]
[%ts %ip #f 12]
[%td %r11 #t 11]
#;[%ret]
[%cp %r7 #t 7]
#;[%ac1]
#;[%yp]
[ %r0 %Carg1 %Cretval #f 0]
[ %r1 %Carg2 #f 1]
[ %r2 %Carg3 #f 2]
[ %r3 %Carg4 #f 3]
[ %lr #f 14] ; %lr is trashed by 'c' calls including calls to hand-coded routines like get-room
)
(machine-dependent
[%sp #t 13]
[%pc #f 15]
[%Cfparg1 %Cfpretval %d0 %s0 #f 0] ; < 32: low bit goes in D, N, or M bit, high bits go in Vd, Vn, Vm
[%Cfparg1b %s1 #f 1]
[%Cfparg2 %d1 %s2 #f 2]
[%Cfparg2b %s3 #f 3]
[%Cfparg3 %d2 %s4 #f 4]
[%Cfparg3b %s5 #f 5]
[%Cfparg4 %d3 %s6 #f 6]
[%Cfparg4b %s7 #f 7]
[%Cfparg5 %d4 %s8 #f 8]
[%Cfparg5b %s9 #f 9]
[%Cfparg6 %d5 %s10 #f 10]
[%Cfparg6b %s11 #f 11]
[%Cfparg7 %d6 %s12 #f 12]
[%Cfparg7b %s13 #f 13]
[%Cfparg8 %d7 %s14 #f 14]
[%Cfparg8b %s15 #f 15]
[%flreg1 %d8 %s16 #f 16]
[%flreg2 %d9 %s18 #f 18]
; etc.
#;[ %d16 #f 32] ; >= 32: high bit goes in D, N, or M bit, low bits go in Vd, Vn, Vm
#;[ %d17 #f 33]
; etc.
))
;;; 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 imm-funky12?
(lambda (x)
(nanopass-case (L15c Triv) x
[(immediate ,imm) (and (funky12 imm) #t)]
[else #f])))
(define imm-negate-funky12?
(lambda (x)
(nanopass-case (L15c Triv) x
[(immediate ,imm) (and (funky12 (- imm)) #t)]
[else #f])))
(define imm-lognot-funky12?
(lambda (x)
(nanopass-case (L15c Triv) x
[(immediate ,imm) (and (funky12 (lognot imm)) #t)]
[else #f])))
(define imm-shift-count?
(lambda (x)
(nanopass-case (L15c Triv) x
[(immediate ,imm) (shift-count? imm)]
[else #f])))
(define imm-unsigned8?
(lambda (x)
(nanopass-case (L15c Triv) x
[(immediate ,imm) (unsigned8? imm)]
[else #f])))
(define imm-unsigned12?
(lambda (x)
(nanopass-case (L15c Triv) x
[(immediate ,imm) (unsigned12? imm)]
[else #f])))
(define imm-constant?
(lambda (x)
(nanopass-case (L15c Triv) x
[(immediate ,imm) #t]
[else #f])))
(define uword8?
(lambda (imm)
(and (fixnum? imm) ($fxu< imm (expt 2 10)) (not (fxlogtest imm #b11)))))
(define imm-uword8?
;; immediate is a nonnegative 8-bit word offset
;; effectively 8-bit unsigned left-shifted by 2
(lambda (x)
(nanopass-case (L15c Triv) x
[(immediate ,imm) (uword8? imm)]
[else #f])))
(define-pass imm->negate-imm : (L15c Triv) (ir) -> (L15d Triv) ()
(Triv : Triv (ir) -> Triv ()
[(immediate ,imm) `(immediate ,(- imm))]
[else (sorry! who "~s is not an immediate" ir)]))
(define-pass imm->lognot-imm : (L15c Triv) (ir) -> (L15d Triv) ()
(Triv : Triv (ir) -> Triv ()
[(immediate ,imm) `(immediate ,(lognot imm))]
[else (sorry! who "~s is not an immediate" ir)]))
(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)
; arm load & store instructions support index or offset but not both
(safe-assert (or (eq? x1 %zero) (eqv? imm 0)))
(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)
(cond
[(and (eq? x1 %zero) (or (unsigned12? imm) (unsigned12? (- imm))))
(return x0 %zero imm)]
[(funky12 imm)
; NB: dubious value? check to see if it's exercised
; NB: might should safe-assert x1 is %zero
(let ([u (make-tmp 'u)])
(seq
(build-set! ,u (asm ,null-info ,(asm-add #f) ,x0 (immediate ,imm)))
(return u x1 0)))]
[(funky12 (- imm))
; NB: dubious value? check to see if it's exercised
; NB: might should safe-assert x1 is %zero
(let ([u (make-tmp 'u)])
(seq
(build-set! ,u (asm ,null-info ,(asm-sub #f) ,x0 (immediate ,imm)))
(return u x1 0)))]
[else
(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 #f) ,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*)
(and (memq 'funky12 aty*) (imm-funky12? a))
(and (memq 'negate-funky12 aty*) (imm-negate-funky12? a))
(and (memq 'lognot-funky12 aty*) (imm-lognot-funky12? a))
(and (memq 'shift-count aty*) (imm-shift-count? a))
(and (memq 'unsigned8 aty*) (imm-unsigned8? a))
(and (memq 'unsigned12 aty*) (imm-unsigned12? a))
(and (memq 'imm-constant aty*) (imm-constant? a))
(and (memq 'uword8 aty*) (imm-uword8? 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 'funky12 aty*) (imm-funky12? a)) (k (imm->imm a))]
[(and (memq 'negate-funky12 aty*) (imm-negate-funky12? a)) (k (imm->negate-imm a))]
[(and (memq 'lognot-funky12 aty*) (imm-lognot-funky12? a)) (k (imm->lognot-imm a))]
[(and (memq 'shift-count aty*) (imm-shift-count? a)) (k (imm->imm a))]
[(and (memq 'unsigned8 aty*) (imm-unsigned8? a)) (k (imm->imm a))]
[(and (memq 'unsigned12 aty*) (imm-unsigned12? a)) (k (imm->imm a))]
[(and (memq 'imm-constant aty*) (imm-constant? a)) (k (imm->imm a))]
[(and (memq 'uword8 aty*) (imm-uword8? 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 md-handle-jump
(lambda (t)
(with-output-language (L15d Tail)
(define long-form
(lambda (e)
(let ([tmp (make-tmp 'utmp)])
(values
(in-context Effect `(set! ,(make-live-info) ,tmp ,e))
`(jump ,tmp)))))
(nanopass-case (L15c Triv) t
[,lvalue
(if (mem? lvalue)
(mem->mem lvalue (lambda (e) (values '() `(jump ,e))))
(values '() `(jump ,lvalue)))]
[(literal ,info)
(guard (and (not (info-literal-indirect? info))
(memq (info-literal-type info) '(entry library-code))))
; NB: really need to use unspillable or mark %ip (aka %ts) killed here but can't without extending jump syntax
(values '() `(jump (literal ,info)))]
[(label-ref ,l ,offset)
; NB: really need to use unspillable or mark %ip (aka %ts) killed here but can't without extending jump syntax
(values '() `(jump (label-ref ,l ,offset)))]
[else (long-form t)]))))
(define-syntax define-instruction
(lambda (x)
(define make-value-clause
(lambda (fmt)
(syntax-case fmt (mem ur)
[(op (c mem) (a ur))
#`(lambda (c a)
(if (lmem? c)
(coerce-opnd a '(ur)
(lambda (a)
(mem->mem c
(lambda (c)
(rhs c a)))))
(next c a)))]
[(op (c ur) (a aty ...) ...)
#`(lambda (c a ...)
(if (and (coercible? a '(aty ...)) ...)
#,(let f ([a* #'(a ...)] [aty** #'((aty ...) ...)])
(if (null? a*)
#'(if (ur? c)
(rhs c a ...)
(let ([u (make-tmp 'u)])
(seq
(rhs u a ...)
(mref->mref c
(lambda (c)
(build-set! ,c ,u))))))
#`(coerce-opnd #,(car a*) '#,(car aty**)
(lambda (#,(car a*)) #,(f (cdr a*) (cdr aty**))))))
(next c a ...)))])))
(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-instruction
(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 ...)])))
(define info-cc-eq (make-info-condition-code 'eq? #f #t))
(define asm-eq (asm-relop info-cc-eq))
; 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 (- -/ovfl -/eq)
[(op (z ur) (x ur) (y funky12))
`(set! ,(make-live-info) ,z (asm ,info ,(asm-sub (memq op '(-/ovfl -/eq))) ,x ,y))]
[(op (z ur) (x funky12) (y ur))
`(set! ,(make-live-info) ,z (asm ,info ,(asm-rsb (memq op '(-/ovfl -/eq))) ,y ,x))]
[(op (z ur) (x ur) (y negate-funky12))
`(set! ,(make-live-info) ,z (asm ,info ,(asm-add (memq op '(-/ovfl -/eq))) ,x ,y))]
[(op (z ur) (x ur) (y ur))
`(set! ,(make-live-info) ,z (asm ,info ,(asm-sub (memq op '(-/ovfl -/eq))) ,x ,y))])
(define-instruction value (+ +/ovfl +/carry)
[(op (z ur) (x ur) (y funky12))
`(set! ,(make-live-info) ,z (asm ,info ,(asm-add (memq op '(+/ovfl +/carry))) ,x ,y))]
[(op (z ur) (x funky12) (y ur))
`(set! ,(make-live-info) ,z (asm ,info ,(asm-add (memq op '(+/ovfl +/carry))) ,y ,x))]
[(op (z ur) (x ur) (y negate-funky12))
`(set! ,(make-live-info) ,z (asm ,info ,(asm-sub (memq op '(+/ovfl +/carry))) ,x ,y))]
[(op (z ur) (x negate-funky12) (y ur))
`(set! ,(make-live-info) ,z (asm ,info ,(asm-sub (memq op '(+/ovfl +/carry))) ,y ,x))]
[(op (z ur) (x ur) (y ur))
`(set! ,(make-live-info) ,z (asm ,info ,(asm-add (memq op '(+/ovfl +/carry))) ,x ,y))])
(define-instruction value (*)
; no imm form available
[(op (z ur) (x ur) (y ur))
`(set! ,(make-live-info) ,z (asm ,info ,asm-mul ,x ,y))])
(define-instruction value (*/ovfl) ; z flag set iff no overflow
; no imm form available
[(op (z ur) (x ur) (y ur))
(let ([u (make-tmp 'u)])
(seq
`(set! ,(make-live-info) ,u (asm ,null-info ,asm-kill))
`(set! ,(make-live-info) ,z (asm ,null-info ,asm-smull ,x ,y ,u))
`(asm ,null-info ,(asm-cmp/shift 31 'sra) ,u ,z)))])
; NB: only on ARMv7VE implementations
#;(define-instruction value (/)
; does not affect condition codes
[(op (z ur) (x ur) (y ur))
`(set! ,(make-live-info) ,z (asm ,info ,asm-div ,x ,y))])
(define-instruction value (logand)
[(op (z ur) (x ur) (y funky12))
`(set! ,(make-live-info) ,z (asm ,info ,(asm-logand #f) ,x ,y))]
[(op (z ur) (x funky12) (y ur))
`(set! ,(make-live-info) ,z (asm ,info ,(asm-logand #f) ,y ,x))]
[(op (z ur) (x ur) (y lognot-funky12))
`(set! ,(make-live-info) ,z (asm ,info ,(asm-bic #f) ,x ,y))]
[(op (z ur) (x lognot-funky12) (y ur))
`(set! ,(make-live-info) ,z (asm ,info ,(asm-bic #f) ,y ,x))]
[(op (z ur) (x ur) (y ur))
`(set! ,(make-live-info) ,z (asm ,info ,(asm-logand #f) ,x ,y))])
(let ()
(define select-op (lambda (op) (if (eq? op 'logor) asm-logor asm-logxor)))
(define-instruction value (logor logxor)
[(op (z ur) (x funky12) (y ur))
`(set! ,(make-live-info) ,z (asm ,info ,((select-op op) #f) ,y ,x))]
[(op (z ur) (x ur) (y funky12 ur))
`(set! ,(make-live-info) ,z (asm ,info ,((select-op op) #f) ,x ,y))]))
(define-instruction value (lognot)
[(op (z ur) (x ur))
`(set! ,(make-live-info) ,z (asm ,info ,asm-lognot ,x))])
(define-instruction value (sll srl sra)
[(op (z ur) (x ur) (y ur shift-count))
`(set! ,(make-live-info) ,z (asm ,info ,(asm-shiftop op) ,x ,y))])
(define-instruction value (move)
[(op (z mem) (x ur))
`(set! ,(make-live-info) ,z ,x)]
[(op (z ur) (x ur mem imm))
`(set! ,(make-live-info) ,z ,x)])
(define-instruction value lea1
; NB: would be simpler if offset were explicit operand
; NB: why not one version of lea with %zero for y in lea1 case?
[(op (z ur) (x ur))
(begin
(let ([offset (info-lea-offset info)])
(if (funky12 offset)
`(set! ,(make-live-info) ,z (asm ,info ,(asm-add #f) ,x (immediate ,offset)))
(let ([u (make-tmp 'u)])
(seq
`(set! ,(make-live-info) ,u (immediate ,offset))
`(set! ,(make-live-info) ,z (asm ,info ,(asm-add #f) ,x ,u)))))))])
(define-instruction value lea2
; NB: would be simpler if offset were explicit operand
[(op (z ur) (x ur) (y ur))
(let ([offset (info-lea-offset info)] [u (make-tmp 'u)])
(seq
(if (funky12 offset)
`(set! ,(make-live-info) ,u (asm ,info ,(asm-add #f) ,y (immediate ,offset)))
(seq
`(set! ,(make-live-info) ,u (immediate ,offset))
`(set! ,(make-live-info) ,u (asm ,null-info ,(asm-add #f) ,u ,y))))
`(set! ,(make-live-info) ,z (asm ,info ,(asm-add #f) ,x ,u))))])
(define-instruction value (sext8 sext16 zext8 zext16)
[(op (z ur) (x ur)) `(set! ,(make-live-info) ,z (asm ,info ,(asm-move/extend op) ,x))])
(let ()
(define imm-zero (with-output-language (L15d Triv) `(immediate 0)))
(define load/store
(lambda (x y w imm8? k) ; x ur, y ur, w ur or imm
(with-output-language (L15d Effect)
(if (ur? w)
(if (eq? y %zero)
(k x w imm-zero)
(let ([u (make-tmp 'u)])
(seq
`(set! ,(make-live-info) ,u (asm ,null-info ,(asm-add #f) ,y ,w))
(k x u imm-zero))))
(let ([n (nanopass-case (L15d Triv) w [(immediate ,imm) imm])])
(cond
[(if imm8?
(or (unsigned8? n) (unsigned8? (- n)))
(or (unsigned12? n) (unsigned12? (- n))))
(let ([w (in-context Triv `(immediate ,n))])
(if (or (eq? y %zero) (fx= n 0))
(k x y w)
(let ([u (make-tmp 'u)])
(seq
`(set! ,(make-live-info) ,u (asm ,null-info ,(asm-add #f) ,x ,y))
(k u %zero w)))))]
[(funky12 n) =>
; NB: dubious value? check to see if it's exercised
(lambda (n)
(let ([u (make-tmp 'u)])
(seq
`(set! ,(make-live-info) ,u (asm ,null-info ,(asm-add #f) ,x (immediate ,n)))
(k u y imm-zero))))]
[(funky12 (- n)) =>
; NB: dubious value? check to see if it's exercised
(lambda (n)
(let ([u (make-tmp 'u)])
(seq
`(set! ,(make-live-info) ,u (asm ,null-info ,(asm-sub #f) ,x (immediate ,n)))
(k u y imm-zero))))]
[else
(let ([u (make-tmp 'u)])
(seq
`(set! ,(make-live-info) ,u (immediate ,n))
(if (eq? y %zero)
(k x u imm-zero)
(seq
`(set! ,(make-live-info) ,u (asm ,null-info ,(asm-add #f) ,x ,u))
(k u y imm-zero)))))]))))))
(define-instruction value (load)
[(op (z ur) (x ur) (y ur) (w ur imm-constant))
(let ([type (info-load-type info)])
(load/store x y w (memq type '(integer-16 unsigned-16 integer-8))
(lambda (x y w)
(let ([instr `(set! ,(make-live-info) ,z (asm ,null-info ,(asm-load type) ,x ,y ,w))])
(if (info-load-swapped? info)
(seq
instr
`(set! ,(make-live-info) ,z (asm ,null-info ,(asm-swap type) ,z)))
instr)))))])
(define-instruction effect (store)
[(op (x ur) (y ur) (w ur imm-constant) (z ur))
(let ([type (info-load-type info)])
(load/store x y w (memq type '(integer-16 unsigned-16))
(lambda (x y w)
(if (info-load-swapped? info)
(let ([u (make-tmp 'unique-bob)])
(seq
`(set! ,(make-live-info) ,u (asm ,null-info ,(asm-swap type) ,z))
`(asm ,null-info ,(asm-store type) ,x ,y ,w ,u)))
`(asm ,null-info ,(asm-store type) ,x ,y ,w ,z)))))]))
(let ()
(define pick-asm-op
(lambda (op info)
(let ([flreg (info-loadfl-flreg info)])
(case op
[(load-single->double load-double->single) (asm-fl-load/cvt op flreg)]
[(store-single->double) (asm-fl-store/cvt op flreg)]
[else (asm-fl-load/store op flreg)]))))
(define-instruction effect (load-single->double load-double->single store-single->double
store-single store-double
load-single load-double)
[(op (x ur) (y ur) (z uword8))
(if (eq? y %zero)
`(asm ,info ,(pick-asm-op op info) ,x ,z)
(let ([u (make-tmp 'u)])
(seq
`(set! ,(make-live-info) ,u (asm ,info ,(asm-add #f) ,x ,y))
`(asm ,info ,(pick-asm-op op info) ,u ,z))))]
[(op (x ur) (y ur) (z ur))
(let ([u (make-tmp 'u)])
(seq
`(set! ,(make-live-info) ,u (asm ,info ,(asm-add #f) ,x ,z))
(if (eq? y %zero)
`(asm ,info ,(pick-asm-op op info) ,u (immediate 0))
(seq
`(set! ,(make-live-info) ,u (asm ,info ,(asm-add #f) ,u ,y))
`(asm ,info ,(pick-asm-op op info) ,u (immediate 0))))))]))
(let ()
; vldr, vstr allow only word offsets, and we require byte offset due to the type tag
(module (with-flonum-data-pointers)
(define $flonum-data-pointer
(lambda (x p)
(with-output-language (L15d Effect)
(let ([u (make-tmp 'u)])
(seq
`(set! ,(make-live-info) ,u (asm ,null-info ,(asm-add #f) ,x (immediate ,(constant flonum-data-disp))))
(p u))))))
(define-syntax with-flonum-data-pointers
(syntax-rules ()
[(_ () e1 e2 ...) (begin e1 e2 ...)]
[(_ (x1 x2 ...) e1 e2 ...)
($flonum-data-pointer x1
(lambda (x1)
(with-flonum-data-pointers (x2 ...) e1 e2 ...)))])))
(define-instruction effect (flt)
[(op (x ur) (y ur))
(with-flonum-data-pointers (y)
`(asm ,info ,asm-flt ,x ,y))])
(define-instruction effect (fl+ fl- fl/ fl*)
[(op (x ur) (y ur) (z ur))
(with-flonum-data-pointers (x y z)
`(asm ,info ,(asm-flop-2 op) ,x ,y ,z))])
(define-instruction effect (flsqrt)
[(op (x ur) (y ur))
(with-flonum-data-pointers (x y)
`(asm ,info ,asm-flsqrt ,x ,y))])
(define-instruction value (trunc)
[(op (z ur) (x ur))
(with-flonum-data-pointers (x)
`(set! ,(make-live-info) ,z (asm ,info ,asm-trunc ,x)))])
(define-instruction pred (fl= fl< fl<=)
[(op (x ur) (y ur))
(with-flonum-data-pointers (x y)
(let ([info (make-info-condition-code op #f #f)])
(values '() `(asm ,info ,(asm-fl-relop info) ,x ,y))))]))
(define-instruction effect (inc-cc-counter)
[(op (x ur) (w ur funky12) (z funky12 ur))
(let ([u1 (make-tmp 'u1)] [u2 (make-tmp 'u2)])
(seq
`(set! ,(make-live-info) ,u1 (asm ,null-info ,(asm-add #f) ,x ,w))
`(set! ,(make-live-info) ,u2 (asm ,null-info ,asm-kill))
`(asm ,null-info ,asm-inc-cc-counter ,u1 ,z ,u2)))])
(define-instruction effect (inc-profile-counter)
[(op (x mem) (y ur funky12))
(let ([u (make-tmp 'u)])
(seq
`(set! ,(make-live-info) ,u ,x)
`(set! ,(make-live-info) ,u (asm ,null-info ,(asm-add #f) ,u ,y))
`(set! ,(make-live-info) ,x ,u)))])
(define-instruction value (read-time-stamp-counter)
[(op (z ur)) `(set! ,(make-live-info) ,z (asm ,null-info ,(asm-read-counter 1)))])
(define-instruction value (read-performance-monitoring-counter)
[(op (z ur) (x unsigned8))
; could require unsigned1 and skip the fxmin...but no point burdening instruction scheduler with an additional one-off type
(let ([imm (nanopass-case (L15d Triv) x [(immediate ,imm) (fxmin imm 1)])])
`(set! ,(make-live-info) ,z (asm ,null-info ,(asm-read-counter (fx+ imm 2)))))]
[(op (z ur) (x ur)) `(set! ,(make-live-info) ,z (asm ,null-info ,(asm-read-counter) ,x))])
;; no kills since we expect to be called when all necessary state has already been saved
(define-instruction value (get-tc)
[(op (z ur))
(safe-assert (eq? z %Cretval))
(let ([u (make-tmp 'u)] [ulr (make-precolored-unspillable 'ulr %lr)])
(seq
`(set! ,(make-live-info) ,u (asm ,null-info ,asm-kill))
`(set! ,(make-live-info) ,ulr (asm ,null-info ,asm-kill))
`(set! ,(make-live-info) ,z (asm ,info ,asm-get-tc ,u ,ulr))))])
(define-instruction value (asmlibcall)
[(op (z ur))
(let ([u (make-tmp 'u)])
(if (info-asmlib-save-ra? info)
(seq
`(set! ,(make-live-info) ,u (asm ,null-info ,asm-kill))
`(set! ,(make-live-info) ,z (asm ,info ,(asm-library-call (info-asmlib-libspec info) #t) ,u ,(info-kill*-live*-live* info) ...)))
(let ([ulr (make-precolored-unspillable 'ulr %lr)])
(seq
`(set! ,(make-live-info) ,u (asm ,null-info ,asm-kill))
`(set! ,(make-live-info) ,ulr (asm ,null-info ,asm-kill))
`(set! ,(make-live-info) ,z (asm ,info ,(asm-library-call (info-asmlib-libspec info) #f) ,u ,ulr ,(info-kill*-live*-live* info) ...))))))])
(define-instruction effect (asmlibcall!)
[(op)
(let ([u (make-tmp 'u)])
(if (info-asmlib-save-ra? info)
(let ([ulr (make-precolored-unspillable 'ulr %lr)])
(seq
`(set! ,(make-live-info) ,u (asm ,null-info ,asm-kill))
`(asm ,info ,(asm-library-call! (info-asmlib-libspec info) #t) ,u ,(info-kill*-live*-live* info) ...)))
(let ([ulr (make-precolored-unspillable 'ulr %lr)])
(seq
`(set! ,(make-live-info) ,u (asm ,null-info ,asm-kill))
`(set! ,(make-live-info) ,ulr (asm ,null-info ,asm-kill))
`(asm ,info ,(asm-library-call! (info-asmlib-libspec info) #f) ,u ,ulr ,(info-kill*-live*-live* info) ...)))))])
(safe-assert (reg-callee-save? %tc)) ; no need to save-restore
(define-instruction effect (c-simple-call)
[(op)
(let ([u (make-tmp 'u)])
(if (info-c-simple-call-save-ra? info)
(seq
`(set! ,(make-live-info) ,u (asm ,null-info ,asm-kill))
`(asm ,info ,(asm-c-simple-call (info-c-simple-call-entry info) #t) ,u))
(let ([ulr (make-precolored-unspillable 'ulr %lr)])
(seq
`(set! ,(make-live-info) ,u (asm ,null-info ,asm-kill))
`(set! ,(make-live-info) ,ulr (asm ,null-info ,asm-kill))
`(asm ,info ,(asm-c-simple-call (info-c-simple-call-entry info) #f) ,u ,ulr)))))])
(define-instruction pred (eq? u< < > <= >=)
[(op (y funky12) (x ur))
(let ([info (if (eq? op 'eq?) info-cc-eq (make-info-condition-code op #t #t))])
(values '() `(asm ,info ,(asm-relop info) ,x ,y)))]
[(op (x ur) (y ur funky12))
(let ([info (if (eq? op 'eq?) info-cc-eq (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)))])
(define-instruction pred (type-check?)
[(op (x ur) (mask funky12 ur) (type funky12 ur))
(let ([tmp (make-tmp 'u)])
(values
(with-output-language (L15d Effect)
`(set! ,(make-live-info) ,tmp (asm ,null-info ,(asm-logand #f) ,x ,mask)))
`(asm ,info-cc-eq ,asm-eq ,tmp ,type)))])
(define-instruction pred (logtest log!test)
[(op (x funky12) (y ur))
(values '() `(asm ,info-cc-eq ,(asm-logtest (eq? op 'log!test) info-cc-eq) ,y ,x))]
[(op (x ur) (y ur funky12))
(values '() `(asm ,info-cc-eq ,(asm-logtest (eq? op 'log!test) info-cc-eq) ,x ,y))])
(let ()
(define lea->reg
(lambda (x y w k)
(with-output-language (L15d Effect)
(define add-offset
(lambda (r)
(if (eqv? (nanopass-case (L15d Triv) w [(immediate ,imm) imm]) 0)
(k r)
(let ([u (make-tmp 'u)])
(seq
`(set! ,(make-live-info) ,u (asm ,null-info ,(asm-add #f) ,r ,w))
(k u))))))
(if (eq? y %zero)
(add-offset x)
(let ([u (make-tmp 'u)])
(seq
`(set! ,(make-live-info) ,u (asm ,null-info ,(asm-add #f) ,x ,y))
(add-offset u)))))))
; NB: compiler ipmlements init-lock! and unlock! as 32-bit store of zero
(define-instruction pred (lock!)
[(op (x ur) (y ur) (w funky12))
(let ([u (make-tmp 'u)])
(values
(lea->reg x y w
(lambda (r)
(with-output-language (L15d Effect)
(seq
`(set! ,(make-live-info) ,u (asm ,null-info ,asm-kill))
`(asm ,null-info ,asm-lock ,r ,u)))))
`(asm ,info-cc-eq ,asm-eq ,u (immediate 0))))])
(define-instruction effect (locked-incr! locked-decr!)
[(op (x ur) (y ur) (w funky12))
(lea->reg x y w
(lambda (r)
(let ([u1 (make-tmp 'u1)] [u2 (make-tmp 'u2)])
(seq
`(set! ,(make-live-info) ,u1 (asm ,null-info ,asm-kill))
`(set! ,(make-live-info) ,u2 (asm ,null-info ,asm-kill))
`(asm ,null-info ,(asm-lock+/- op) ,r ,u1 ,u2)))))])
(define-instruction effect (cas)
[(op (x ur) (y ur) (w funky12) (old ur) (new ur))
(lea->reg x y w
(lambda (r)
(let ([u1 (make-tmp 'u1)] [u2 (make-tmp 'u2)])
(seq
`(set! ,(make-live-info) ,u1 (asm ,null-info ,asm-kill))
`(set! ,(make-live-info) ,u2 (asm ,null-info ,asm-kill))
`(asm ,info ,asm-cas ,r ,old ,new ,u1 ,u2)))))]))
(define-instruction effect (pause)
; NB: user sqrt or something like that?
[(op) '()])
(define-instruction effect (c-call)
[(op (x ur))
(let ([ulr (make-precolored-unspillable 'ulr %lr)])
(seq
`(set! ,(make-live-info) ,ulr (asm ,null-info ,asm-kill))
`(asm ,info ,asm-indirect-call ,x ,ulr ,(info-kill*-live*-live* info) ...)))])
(define-instruction effect (pop-multiple)
[(op) `(asm ,info ,(asm-pop-multiple (info-kill*-kill* info)))])
(define-instruction effect (push-multiple)
[(op) `(asm ,info ,(asm-push-multiple (info-kill*-live*-live* info)))])
(define-instruction effect (vpush-multiple)
[(op) `(asm ,info ,(asm-vpush-multiple (info-vpush-reg info) (info-vpush-n info)))])
(define-instruction effect (vpop-multiple)
[(op) `(asm ,info ,(asm-vpop-multiple (info-vpush-reg info) (info-vpush-n info)))])
(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)
[(op) `(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-call! asm-library-jump
asm-mul asm-smull asm-cmp/shift asm-add asm-sub asm-rsb asm-logand asm-logor asm-logxor asm-bic
asm-pop-multiple asm-shiftop asm-logand asm-lognot
asm-logtest asm-fl-relop asm-relop asm-push-multiple asm-vpush-multiple asm-vpop-multiple
asm-indirect-jump asm-literal-jump
asm-direct-jump asm-return-address asm-jump asm-conditional-jump asm-data-label asm-rp-header
asm-indirect-call asm-condition-code
asm-fl-load/store
asm-fl-load/cvt asm-fl-store/cvt asm-flt asm-trunc
asm-lock asm-lock+/- asm-cas
asm-flop-2 asm-flsqrt asm-c-simple-call
asm-save-flrv asm-restore-flrv asm-return asm-c-return asm-size
asm-enter asm-foreign-call asm-foreign-callable
asm-read-counter
asm-inc-cc-counter
funky12
shift-count? unsigned8? unsigned12?
; threaded version specific
asm-get-tc
; machine dependent exports
asm-kill
info-vpush-reg info-vpush-n)
(define-record-type info-vpush (nongenerative)
(parent info)
(sealed #t)
(fields reg n))
(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-who ax-ea-reg-code
(lambda (ea)
(record-case ea
[(reg) r (reg-mdinfo r)]
[else (sorry! who "ea=~s" ea)])))
(define ax-register-list
(lambda (r*)
(fold-left
(lambda (a r) (fx+ a (fxsll 1 (reg-mdinfo r))))
0 r*)))
(define ax-reg?
(lambda (ea)
(record-case ea
[(reg) ignore #t]
[else #f])))
(define ax-imm?
(lambda (ea)
(record-case ea
[(imm) ignore #t]
[else #f])))
(define-who ax-imm-data
(lambda (ea)
(record-case ea
[(imm) (n) n]
[else (sorry! who "ax-imm-data ea=~s" ea)])))
; define-op sets up assembly op macros--
; the opcode and all other expressions are passed to the specified handler--
(define-syntax define-op
(lambda (x)
(syntax-case x ()
[(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 movi1 movi-a1-op #b00111010)
(define-op mvni movi-a1-op #b00111110)
(define-op movi2 movi-a2-op #b00110000) ; ARMv6T, ARMv7
(define-op movt movi-a2-op #b00110100) ; ARMv6T, ARMv7
(define-op addi binary-imm-op #b0010100)
(define-op addci binary-imm-op #b0010101)
(define-op subi binary-imm-op #b0010010)
(define-op rsbi binary-imm-op #b0010011)
(define-op andi binary-imm-op #b0010000)
(define-op orri binary-imm-op #b0011100)
(define-op eori binary-imm-op #b0010001)
(define-op bici binary-imm-op #b0011110)
(define-op add binary-op #b0000100)
(define-op sub binary-op #b0000010)
(define-op rsb binary-op #b0000011)
(define-op and binary-op #b0000000)
(define-op orr binary-op #b0001100)
(define-op eor binary-op #b0000001)
(define-op bic binary-op #b0001110)
(define-op cmp cmp-op #b0001010)
(define-op tst cmp-op #b0001000)
(define-op cmp/shift cmp-op #b0001010)
(define-op cmpi cmp-imm-op #b0011010)
(define-op tsti cmp-imm-op #b0011000)
(define-op mov unary-op #b0001101 #f) ; note: for mov, bits 5-11 must be zero, corresponding to 00 shift type and 00000 shift count
(define-op mvn unary-op #b0001111 #f)
(define-op shifti shifti-op)
(define-op shift shift-op)
(define-op sxtb extend-op #b01101010)
(define-op sxth extend-op #b01101011)
(define-op uxtb extend-op #b01101110)
(define-op uxth extend-op #b01101111)
(define-op mul mul-op #b0000000)
(define-op smull mull-op #b0000110)
(define-op ldri load-imm-op #b1 #b0 #b010 #b0 #b1)
(define-op ldrbi load-imm-op #b1 #b0 #b010 #b1 #b1)
(define-op stri load-imm-op #b1 #b0 #b010 #b0 #b0)
(define-op strbi load-imm-op #b1 #b0 #b010 #b1 #b0)
(define-op str/preidx load-imm-op #b1 #b1 #b010 #b0 #b0)
(define-op ldr/postidx load-imm-op #b0 #b0 #b010 #b0 #b1)
(define-op ldrlit load-lit-op)
(define-op ldrshi load-noshift-imm-op #b1 #b1111)
(define-op ldrhi load-noshift-imm-op #b1 #b1011)
(define-op ldrdi load-noshift-imm-op #b0 #b1101)
(define-op ldrsbi load-noshift-imm-op #b1 #b1101)
(define-op strhi load-noshift-imm-op #b0 #b1011)
(define-op strdi load-noshift-imm-op #b0 #b1111)
(define-op ldr load-op #b011 #b0 #b1)
(define-op ldrb load-op #b011 #b1 #b1)
(define-op str load-op #b011 #b0 #b0)
(define-op strb load-op #b011 #b1 #b0)
(define-op ldrsh load-noshift-op #b0 #b1 #b1111)
(define-op ldrh load-noshift-op #b0 #b1 #b1011)
(define-op ldrd load-noshift-op #b0 #b0 #b1101)
(define-op ldrsb load-noshift-op #b0 #b1 #b1101)
(define-op strh load-noshift-op #b0 #b0 #b1011)
(define-op strd load-noshift-op #b0 #b0 #b1111)
(define-op ldrex ldrex-op #b00011001)
(define-op strex strex-op #b00011000)
(define-op bnei branch-imm-op (ax-cond 'ne))
(define-op brai branch-imm-op (ax-cond 'al))
(define-op bx branch-reg-op (ax-cond 'al) #b0001)
(define-op blx branch-reg-op (ax-cond 'al) #b0011)
(define-op bra branch-label-op (ax-cond 'al))
(define-op beq branch-label-op (ax-cond 'eq))
(define-op bne branch-label-op (ax-cond 'ne))
(define-op blt branch-label-op (ax-cond 'lt))
(define-op ble branch-label-op (ax-cond 'le))
(define-op bgt branch-label-op (ax-cond 'gt))
(define-op bge branch-label-op (ax-cond 'ge))
(define-op bcc branch-label-op (ax-cond 'cc))
(define-op bcs branch-label-op (ax-cond 'cs))
(define-op bvc branch-label-op (ax-cond 'vc))
(define-op bvs branch-label-op (ax-cond 'vs))
(define-op bls branch-label-op (ax-cond 'ls))
(define-op bhi branch-label-op (ax-cond 'hi))
(define-op popm pm-op #b10001011)
(define-op pushm pm-op #b10010010)
(define-op vpushm vpm-op #b11010 #b10)
(define-op vpopm vpm-op #b11001 #b11)
(define-op vldr.sgl vldr/vstr-op #b1010 #b01)
(define-op vldr.dbl vldr/vstr-op #b1011 #b01)
(define-op vstr.sgl vldr/vstr-op #b1010 #b00)
(define-op vstr.dbl vldr/vstr-op #b1011 #b00)
(define-op vmov.gpr->s32 vmov-op #b0)
(define-op vmov.s32->gpr vmov-op #b1)
(define-op vcvt.sgl->dbl vcvt-op #b01 #b110111)
(define-op vcvt.dbl->sgl vcvt-op #b11 #b110111)
(define-op vcvt.s32->dbl vcvt-op #b11 #b111000)
(define-op vcvt.dbl->s32 vcvt-op #b11 #b111101)
(define-op vcmp vcmp-op)
(define-op fpscr->apsr fpscr->apsr-op)
(define-op rev rev-op #b01101011 #b0011)
(define-op rev16 rev-op #b01101011 #b1011)
(define-op revsh rev-op #b01101111 #b1011)
(define-op mrs mrs-op)
(define-op msr msr-op)
(define-op mcr mrc/mcr-op #b0)
(define-op mrc mrc/mcr-op #b1)
(define-op vadd vadd-op #b11 #b0 #b11100)
(define-op vsub vadd-op #b11 #b1 #b11100)
(define-op vmul vadd-op #b10 #b0 #b11100)
(define-op vdiv vadd-op #b00 #b0 #b11101)
(define-op vsqrt vsqrt-op)
(define-who movi-a1-op
(lambda (op opcode dest-ea f12 code*)
(emit-code (op dest-ea f12 code*) ; encoding A1
[28 (ax-cond 'al)]
[20 opcode]
[16 #b0000]
[12 (ax-ea-reg-code dest-ea)]
[0 f12])))
(define-who movi-a2-op ; ARMv6T, ARMv7
(lambda (op opcode dest-ea u16 code*)
(emit-code (op dest-ea u16 code*) ; movi encoding A2
[28 (ax-cond 'al)]
[20 opcode]
[16 (fxsrl u16 12)]
[12 (ax-ea-reg-code dest-ea)]
[0 (fxlogand u16 #xfff)])))
(define shift-op
(lambda (op dest-ea src0-ea src1-ea shift-type code*)
(emit-code (shift-type dest-ea src0-ea src1-ea code*)
[28 (ax-cond 'al)]
[21 #b0001101]
[20 #b0]
[16 #b0000]
[12 (ax-ea-reg-code dest-ea)]
[8 (ax-ea-reg-code src1-ea)]
[7 #b0]
[5 (ax-shift-type shift-type)]
[4 #b1]
[0 (ax-ea-reg-code src0-ea)])))
(define shifti-op
(lambda (op dest-ea src0-ea n shift-type code*)
(emit-code (shift-type dest-ea src0-ea n code*)
[28 (ax-cond 'al)]
[21 #b0001101]
[20 #b0]
[16 #b0000]
[12 (ax-ea-reg-code dest-ea)]
[7 n]
[5 (ax-shift-type shift-type)]
[4 #b0]
[0 (ax-ea-reg-code src0-ea)])))
(define pm-op
(lambda (op opcode regs code*)
(emit-code (op regs code*)
[28 (ax-cond 'al)]
[20 opcode]
[16 #b1101]
[0 (ax-register-list regs)])))
(define binary-imm-op ; 12-bit immediate
(lambda (op opcode set-cc? dest-ea opnd-ea n code*)
(emit-code (op set-cc? dest-ea opnd-ea n code*)
[28 (ax-cond 'al)]
[21 opcode]
[20 (if set-cc? #b1 #b0)]
[16 (ax-ea-reg-code opnd-ea)]
[12 (ax-ea-reg-code dest-ea)]
[0 (or (funky12 n)
($oops 'assembler-internal
"binary-imm-op n=~s" n))])))
(define binary-op
(lambda (op opcode set-cc? dest-ea opnd0-ea opnd1-ea code*)
(emit-code (op set-cc? dest-ea opnd0-ea opnd1-ea code*)
[28 (ax-cond 'al)]
[21 opcode]
[20 (if set-cc? #b1 #b0)]
[16 (ax-ea-reg-code opnd0-ea)]
[12 (ax-ea-reg-code dest-ea)]
[7 #b00000] ; shift value
[5 #b00] ; shift type
[4 #b0]
[0 (ax-ea-reg-code opnd1-ea)])))
(define mull-op
(lambda (op opcode destlo-ea desthi-ea opnd0-ea opnd1-ea code*)
(emit-code (op destlo-ea desthi-ea opnd0-ea opnd1-ea code*)
[28 (ax-cond 'al)]
[21 opcode]
[20 #b0] ; don't need no stinking z & n bits
[16 (ax-ea-reg-code desthi-ea)]
[12 (ax-ea-reg-code destlo-ea)]
[8 (ax-ea-reg-code opnd1-ea)]
[4 #b1001]
[0 (ax-ea-reg-code opnd0-ea)])))
(define unary-op
(lambda (op opcode set-cc? dest-ea opnd-ea code*)
(emit-code (op set-cc? dest-ea opnd-ea code*)
[28 (ax-cond 'al)]
[21 opcode]
[20 (if set-cc? #b1 #b0)]
[16 #b0000]
[12 (ax-ea-reg-code dest-ea)]
[7 #b00000] ; shift value
[5 #b00] ; shift type
[4 #b0]
[0 (ax-ea-reg-code opnd-ea)])))
(define cmp-op
(case-lambda
[(op opcode opnd0-ea opnd1-ea code*)
(emit-code (op opnd0-ea opnd1-ea code*)
[28 (ax-cond 'al)]
[21 opcode]
[20 #b1]
[16 (ax-ea-reg-code opnd0-ea)]
[12 #b0000]
[7 #b00000] ; shift value
[5 (ax-shift-type 'sll)]
[4 #b0]
[0 (ax-ea-reg-code opnd1-ea)])]
[(op opcode shift-count shift-type opnd0-ea opnd1-ea code*)
(emit-code (op opnd0-ea shift-type opnd1-ea shift-count code*)
[28 (ax-cond 'al)]
[21 opcode]
[20 #b1]
[16 (ax-ea-reg-code opnd0-ea)]
[12 #b0000]
[7 shift-count] ; shift value
[5 (ax-shift-type shift-type)]
[4 #b0]
[0 (ax-ea-reg-code opnd1-ea)])]))
(define cmp-imm-op
(lambda (op opcode opnd-ea n code*)
(emit-code (op opnd-ea n code*)
[28 (ax-cond 'al)]
[21 opcode]
[20 #b1]
[16 (ax-ea-reg-code opnd-ea)]
[12 #b0000]
[0 (funky12 n)])))
(define extend-op
(lambda (op opcode dest-ea opnd-ea code*)
(emit-code (op dest-ea opnd-ea code*)
[28 (ax-cond 'al)]
[20 opcode]
[16 #b1111]
[12 (ax-ea-reg-code dest-ea)]
[10 #b00] ; ROR value (0, 8, 16, 24)
[4 #b000111]
[0 (ax-ea-reg-code opnd-ea)])))
(define mul-op
(lambda (op opcode set-cc? dest-ea opnd0-ea opnd1-ea code*)
(emit-code (op set-cc? dest-ea opnd0-ea opnd1-ea code*)
[28 (ax-cond 'al)]
[21 opcode]
[20 (if set-cc? #b1 #b0)]
[16 (ax-ea-reg-code dest-ea)]
[12 #b0000]
[8 (ax-ea-reg-code opnd1-ea)]
[4 #b1001]
[0 (ax-ea-reg-code opnd0-ea)])))
(define ldrex-op
(lambda (op opcode dest-ea opnd-ea code*)
(emit-code (op dest-ea opnd-ea code*)
[28 (ax-cond 'al)]
[20 opcode]
[16 (ax-ea-reg-code opnd-ea)]
[12 (ax-ea-reg-code dest-ea)]
[8 #b1111]
[4 #b1001]
[0 #b1111])))
(define strex-op
(lambda (op opcode dest-ea opnd0-ea opnd1-ea code*)
(emit-code (op dest-ea opnd0-ea opnd1-ea code*)
[28 (ax-cond 'al)]
[20 opcode]
[16 (ax-ea-reg-code opnd1-ea)]
[12 (ax-ea-reg-code dest-ea)]
[8 #b1111]
[4 #b1001]
[0 (ax-ea-reg-code opnd0-ea)])))
(define branch-imm-op
(lambda (op cond-bits disp code*)
(emit-code (op disp code*)
[28 cond-bits]
[24 #b1010]
[0 (fxlogand disp #xffffff)])))
(define-who branch-label-op
(lambda (op cond-bits dest code*)
(record-case dest
[(label) (offset l)
(emit-code (op dest code*)
[28 cond-bits]
[24 #b1010]
[0 (fxlogand (fxsrl (fx- offset 4) 2) #xffffff)])]
[else (sorry! who "unexpected dest ~s" dest)])))
(define-who branch-reg-op
(lambda (op condition-code opcode dest code*)
(emit-code (op dest code*)
[28 condition-code]
[20 #b00010010]
[8 #b111111111111]
[4 opcode]
[0 (ax-ea-reg-code dest)])))
(define mrs-op
(lambda (op dest code*)
(emit-code (op dest code*)
[28 (ax-cond 'al)]
[16 #b000100001111]
[12 (ax-ea-reg-code dest)]
[0 #b000000000000])))
(define msr-op
(lambda (op mask src code*)
(emit-code (op mask src code*)
[28 (ax-cond 'al)]
[20 #b00010010]
[18 mask]
[4 #b00111100000000]
[0 (ax-ea-reg-code src)])))
(define-who mrc/mcr-op
(lambda (op dir cond coproc opc1 dest-ea CRn CRm opc2 code*)
(emit-code (op cond coproc opc1 dest-ea CRn CRm opc2 code*) ; encoding A1
[28 (ax-cond cond)]
[24 #b1110]
[21 opc1]
[20 dir]
[16 CRn]
[12 (ax-ea-reg-code dest-ea)]
[8 coproc]
[5 opc2]
[4 1]
[0 CRm])))
(define vldr/vstr-op
(lambda (op opc1 opc2 flreg reg offset code*)
(let-values ([(d vd) (ax-flreg->bits flreg)])
(emit-code (op flreg reg offset code*)
[28 (ax-cond 'al)]
[24 #b1101]
; NB: what's the source of the following comment?
[23 #b1] ; U bit for adding or subtracting offset. using SP requires offset #-0
[22 d]
[20 opc2]
[16 (ax-ea-reg-code reg)]
[12 vd]
[8 opc1]
[0 (fxsrl offset 2)]))))
(define vmov-op
(lambda (op dir flreg gpreg code*)
(let-values ([(n vn) (ax-flreg->bits flreg)])
(emit-code (op flreg gpreg code*)
[28 (ax-cond 'al)]
[21 #b1110000]
[20 dir]
[16 vn]
[12 (ax-ea-reg-code gpreg)]
[8 #b1010]
[7 n]
[0 #b0010000]))))
(define vcvt-op
(lambda (op szop opc2 dest src code*)
(let-values ([(d vd) (ax-flreg->bits dest)]
[(m vm) (ax-flreg->bits src)])
(emit-code (op dest src code*)
[28 (ax-cond 'al)]
[23 #b11101]
[22 d]
[16 opc2]
[12 vd]
[9 #b101]
[7 szop]
[6 #b1]
[5 m]
[4 #b0]
[0 vm]))))
(define vcmp-op
(lambda (op src1 src2 code*)
(let-values ([(d vd) (ax-flreg->bits src1)]
[(m vm) (ax-flreg->bits src2)])
(emit-code (op src1 src2 code*)
[28 (ax-cond 'al)]
[23 #b11101]
[22 d]
[16 #b110100]
[12 vd]
[9 #b101]
[6 #b101]
[5 m]
[4 #b0]
[0 vm]))))
(define fpscr->apsr-op
(lambda (op code*)
(emit-code (op code*)
[28 (ax-cond 'al)]
[16 #b111011110001]
[12 #b1111]
[0 #b101000010000])))
(define vpm-op
(lambda (op opcode opcode2 flreg n code*)
(let-values ([(d vd) (ax-flreg->bits flreg)])
(emit-code (op flreg n code*)
[28 (ax-cond 'al)]
[23 opcode]
[22 d]
[20 opcode2]
[16 #b1101]
[12 vd]
[8 #b1011]
[0 (fxsll n 1)]))))
(define rev-op
(lambda (op opcode1 opcode2 dest-ea src-ea code*)
(emit-code (op dest-ea src-ea code*)
[28 (ax-cond 'al)]
[20 opcode1]
[16 #b1111]
[12 (ax-ea-reg-code dest-ea)]
[8 #b1111]
[4 opcode2]
[0 (ax-ea-reg-code src-ea)])))
(define load-op
(lambda (op opcode1 opcode2 opcode3 dest-ea base-ea index-ea code*)
(emit-code (op dest-ea base-ea index-ea code*)
[28 (ax-cond 'al)]
[25 opcode1]
[24 #b1] ; P "Pay attention to index register"
[23 #b1] ; U "Upward (add index)"
[22 opcode2]
[21 #b0] ; W "Write back" (post-increment/decrement)
[20 opcode3]
[16 (ax-ea-reg-code base-ea)]
[12 (ax-ea-reg-code dest-ea)]
[7 #b00000] ; shift amount
[5 #b00] ; shift type
[4 #b0]
[0 (ax-ea-reg-code index-ea)])))
(define load-noshift-op
(lambda (op opcode1 opcode2 opcode3 dest-ea base-ea index-ea code*)
(emit-code (op dest-ea base-ea index-ea code*)
[28 (ax-cond 'al)]
[25 #b000]
[24 #b1] ; P "Pay attention to index register"
[23 #b1] ; U "Upward (add index)"
[22 opcode1]
[21 #b0] ; W "Write back" (post-increment/decrement)
[20 opcode2]
[16 (ax-ea-reg-code base-ea)]
[12 (ax-ea-reg-code dest-ea)]
[8 #b0000]
[4 opcode3]
[0 (ax-ea-reg-code index-ea)])))
(define load-imm-op
(lambda (op P W opcode1 opcode2 opcode3 dest-ea base-ea orig-n code*)
(let-values ([(U n) (if (fx< orig-n 0) (values 0 (fx- orig-n)) (values 1 orig-n))])
(emit-code (op dest-ea base-ea orig-n code*)
[28 (ax-cond 'al)]
[25 opcode1]
[24 P] ; P "Pay attention to index register"
[23 U] ; U "Upward (add index)"
[22 opcode2]
[21 W] ; W "Write back" (post-increment/decrement)
[20 opcode3]
[16 (ax-ea-reg-code base-ea)]
[12 (ax-ea-reg-code dest-ea)]
[0 n]))))
(define load-noshift-imm-op
(lambda (op opcode1 opcode2 dest-ea base-ea orig-n code*)
(let-values ([(U n) (if (fx< orig-n 0) (values 0 (fx- orig-n)) (values 1 orig-n))])
(emit-code (op dest-ea orig-n code*)
[28 (ax-cond 'al)]
[25 #b000]
[24 #b1] ; P "Pay attention to index register"
[23 U] ; U "Upward (add index)"
[22 #b1]
[21 #b0] ; W "Write back" (post-increment/decrement)
[20 opcode1]
[16 (ax-ea-reg-code base-ea)]
[12 (ax-ea-reg-code dest-ea)]
[8 (fxsrl n 4)]
[4 opcode2]
[0 (fxlogand n #xf)]))))
(define load-lit-op
(lambda (op dest-ea orig-disp code*)
(let-values ([(U disp) (if (fx< orig-disp 0) (values 0 (fx- orig-disp)) (values 1 orig-disp))])
(emit-code (op dest-ea orig-disp code*)
[28 (ax-cond 'al)]
[25 #b010]
[24 #b1]
[23 U] ; U "Upward (add index)"
[22 #b0]
[21 #b0]
[20 #b1]
[16 #b1111]
[12 (ax-ea-reg-code dest-ea)]
[0 disp]))))
(define vadd-op
(lambda (op opcode1 opcode2 opcode3 dest opnd1 opnd2 code*)
(let-values ([(d vd) (ax-flreg->bits dest)]
[(n vn) (ax-flreg->bits opnd1)]
[(m vm) (ax-flreg->bits opnd2)])
(emit-code (op dest opnd1 opnd2 code*)
[28 (ax-cond 'al)]
[23 opcode3]
[22 d]
[20 opcode1]
[16 vn]
[12 vd]
[9 #b101]
[8 #b1] ;; sz = 1 for double
[7 n]
[6 opcode2]
[5 m]
[4 #b0]
[0 vm]))))
(define vsqrt-op
(lambda (op dest src code*)
(let-values ([(d vd) (ax-flreg->bits dest)]
[(m vm) (ax-flreg->bits src)])
(emit-code (op dest src code*)
[28 (ax-cond 'al)]
[23 #b11101]
[22 d]
[20 #b11]
[16 #b0001]
[12 vd]
[9 #b101]
[8 #b1] ;; sz = 1 for double
[7 #b1]
[6 #b1]
[5 m]
[4 #b0]
[0 vm]))))
; asm helpers
(define-who ax-cond
(lambda (x)
(case x
[(eq) #b0000] ; fl=
[(ne) #b0001]
[(cs) #b0010] ; u<
[(cc) #b0011] ; u>=, fl< (for fl<, do we need this and mi?)
[(mi) #b0100] ; fl< (for fl<, do we need this and cc?)
[(pl) #b0101]
[(vs) #b0110]
[(vc) #b0111]
[(hi) #b1000] ; u>
[(ls) #b1001] ; u<=, fl<=
[(ge) #b1010] ; fl>=
[(lt) #b1011]
[(gt) #b1100] ; fl>
[(le) #b1101]
[(al) #b1110]
[else (sorry! who "unrecognized cond name ~s" x)])))
(define-who ax-shift-type
(lambda (op)
(case op
[(sll) #b00]
[(srl) #b01]
[(sra) #b10]
[(ror) #b11]
[else ($oops who "unsupported op ~s" op)])))
(define ax-flreg->bits
(lambda (flreg)
(let ([n (reg-mdinfo flreg)])
(if (fx< n 32)
(values (fxlogand n 1) (fxsrl n 1))
(values (fxsrl n 4) (fxlogand n #b1111))))))
(define-syntax emit-code
(lambda (x)
; NB: probably won't need emit-code to weed out #f
(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* #'((build long (byte-fields chunk ...)))
#'(aop-cons* `(asm ,op ,opnd ...) ?code*))])))
#;(define-syntax emit-code
(lambda (x)
(syntax-case x ()
[(_ (op opnd ... ?code*) chunk ...)
(fold-right cons #'(aop-cons* `(asm ,op ,opnd ...) ?code*)
#'((build long (byte-fields chunk ...))))])))
(define-who ax-size-code
(lambda (x)
(case x
[(byte) 0]
[(word) 1]
[(long) 1]
[else (sorry! who "invalid size ~s" x)])))
(define-syntax build
(syntax-rules ()
[(_ x e)
(and (memq (datum x) '(byte word long)) (integer? (datum e)))
(quote (x . e))]
[(_ x e)
(memq (datum x) '(byte word long))
(cons 'x e)]))
(define-syntax byte-fields
; NB: make more efficient for fixnums
(syntax-rules ()
[(byte-fields (n e) ...)
(andmap fixnum? (datum (n ...)))
(+ (bitwise-arithmetic-shift-left e n) ...)]))
(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 funky12
(lambda (n)
(define (try limit n)
(let* ([k (fxlogand (bitwise-first-bit-set n) (fxlognot 1))]
[n (bitwise-arithmetic-shift-right n k)])
(and (and (fixnum? n) (#%$fxu< n #x100))
(fxlogor (fxsll (fx- limit k) 7) n))))
(if (and (fixnum? n) (#%$fxu< n #x100))
n
(and (<= (- (expt 2 31)) n (- (expt 2 32) 1)) ; bounds check lets caller be sloppy about, e.g., NOT-ing or negating input
(let ([n (if (< n 0) (+ (expt 2 32) n) n)])
(or (try 32 n)
(try 8
(logor
(bitwise-arithmetic-shift-left (logand n #xffffff) 8)
(bitwise-arithmetic-shift-right n 24)))))))))
;; restrict funky12 so that an code offset n will not fit
;; if a smaller offset wouldn't fit, which prevents bouncing
;; in the loop that computes label offsets
(define code-offset-funky12
(lambda (n)
(safe-assert (and (fixnum? n) (fx= 0 (fxand n 3))))
(and (fixnum? n)
(#%$fxu< n #x400)
(funky12 n))))
(define shift-count?
(lambda (imm)
; can also allow 0 for lsl and 32 (represented as 0) for lsr, asr
; but all three agree on the meaning of [1..31]
(and (fixnum? imm) (fx<= 1 imm 31))))
(define unsigned8?
(lambda (imm)
(and (fixnum? imm) ($fxu< imm (expt 2 8)))))
(define unsigned12?
(lambda (imm)
(and (fixnum? imm) ($fxu< imm (expt 2 12)))))
(define branch-disp?
(lambda (x)
(and (fixnum? x)
; -4 accounts for fact that pc reads as the instruction after next, not next
(fx<= (- (expt 2 25)) (fx- x 4) (- (expt 2 25) 1))
(not (fxlogtest x #b11)))))
(define asm-size
(lambda (x)
(case (car x)
[(asm arm32-abs arm32-jump arm32-call) 0]
[else 4])))
(define ax-mov32
(lambda (dest n code*)
; NB: ARMv6T, ARMv7 only
#;(emit movi dest (logand n #xffff)
(emit movt dest (fxlogand (bitwise-arithmetic-shift-right n 16) #xffff) code*))
; instead place n at pc+8, load from there, and branch around
(emit ldrlit dest 0
(emit brai 0
(cons* `(long . ,n) (aop-cons* `(asm "long:" ,n) code*))))))
(define-who asm-move
(lambda (code* dest src)
; move pseudo instruction used by set! case in select-instruction
; guarantees dest is a reg and src is reg, mem, or imm OR dest is
; mem and src is reg.
(Trivit (dest src)
(define (bad!) (sorry! who "unexpected combination of src ~s and dest ~s" src dest))
(cond
[(ax-reg? dest)
(record-case src
[(reg) ignore (emit mov dest src code*)]
[(imm) (n)
(cond
[(funky12 n) =>
(lambda (f12)
(emit movi1 dest f12 code*))]
[(funky12 (lognot n)) =>
(lambda (f12)
(emit mvni dest f12 code*))]
[else (ax-mov32 dest n code*)])]
[(literal) stuff
(ax-mov32 dest 0
(asm-helper-relocation code* (cons 'arm32-abs stuff)))]
[(disp) (n breg)
(safe-assert (or (unsigned12? n) (unsigned12? (- n))))
(emit ldri dest `(reg . ,breg) n code*)]
[(index) (n ireg breg)
(safe-assert (eqv? n 0))
(emit ldr dest `(reg . ,breg) `(reg . ,ireg) code*)]
[else (bad!)])]
[(ax-reg? src)
(record-case dest
[(disp) (n breg)
(safe-assert (or (unsigned12? n) (unsigned12? (- n))))
(emit stri src `(reg . ,breg) n code*)]
[(index) (n ireg breg)
(safe-assert (eqv? n 0))
(emit str src `(reg . ,breg) `(reg . ,ireg) code*)]
[else (bad!)])]
[else (bad!)]))))
(define-who asm-move/extend
(lambda (op)
(lambda (code* dest src)
(Trivit (dest src)
(case op
[(sext8) (emit sxtb dest src code*)]
[(sext16) (emit sxth dest src code*)]
[(zext8) (emit uxtb dest src code*)]
[(zext16) (emit uxth dest src code*)]
[else (sorry! who "unexpected op ~s" op)])))))
(module (asm-add asm-sub asm-rsb asm-logand asm-logor asm-logxor asm-bic)
(define-syntax define-asm-binop
(syntax-rules ()
[(_ name opi op)
(define name
(lambda (set-cc?)
(rec name
(lambda (code* dest src0 src1)
(Trivit (dest src0 src1)
(record-case src1
[(imm) (n) (emit opi set-cc? dest src0 n code*)]
[else (emit op set-cc? dest src0 src1 code*)]))))))]))
(define-asm-binop asm-add addi add)
(define-asm-binop asm-sub subi sub)
(define-asm-binop asm-rsb rsbi rsb)
(define-asm-binop asm-logand andi and)
(define-asm-binop asm-logor orri orr)
(define-asm-binop asm-logxor eori eor)
(define-asm-binop asm-bic bici bic))
(define asm-mul
(lambda (code* dest src0 src1)
(Trivit (dest src0 src1)
(emit mul #f dest src0 src1 code*))))
(define asm-smull
(lambda (code* dest src0 src1 tmp)
(Trivit (dest src0 src1 tmp)
(emit smull dest tmp src0 src1 code*))))
(define asm-cmp/shift
(lambda (count type)
(lambda (code* src0 src1)
(Trivit (src0 src1)
(emit cmp/shift count type src0 src1 code*)))))
(define-who asm-fl-load/cvt
(lambda (op flreg)
(lambda (code* base offset)
(Trivit (base offset)
(case op
[(load-single->double)
(emit vldr.sgl %flreg2 base (ax-imm-data offset)
(emit vcvt.sgl->dbl flreg %flreg2 code*))]
[(load-double->single)
(emit vldr.dbl %flreg2 base (ax-imm-data offset)
(emit vcvt.dbl->sgl flreg %flreg2 code*))]
[else (sorry! who "unrecognized op ~s" op)])))))
(define-who asm-fl-store/cvt
(lambda (op flreg)
(lambda (code* base offset)
(Trivit (base offset)
(case op
[(store-single->double)
(emit vcvt.sgl->dbl %flreg2 flreg
(emit vstr.dbl %flreg2 base (ax-imm-data offset) code*))]
[else (sorry! who "unrecognized op ~s" op)])))))
(define-who asm-fl-load/store
(lambda (op flreg)
(lambda (code* base offset)
(Trivit (base offset)
(let ([offset (ax-imm-data offset)])
(case op
[(load-single) (emit vldr.sgl flreg base offset code*)]
[(load-double) (emit vldr.dbl flreg base offset code*)]
[(store-single) (emit vstr.sgl flreg base offset code*)]
[(store-double) (emit vstr.dbl flreg base offset code*)]
[else (sorry! who "unrecognized op ~s" op)]))))))
(define-who asm-load
(lambda (type)
(rec asm-load-internal
(lambda (code* dest base index offset)
(let ([n (nanopass-case (L16 Triv) offset
[(immediate ,imm) imm]
[else (sorry! who "unexpected non-immediate offset ~s" offset)])])
(Trivit (dest base)
(cond
[(eq? index %zero)
(case type
[(integer-32 unsigned-32) (emit ldri dest base n code*)]
[(integer-16) (emit ldrshi dest base n code*)]
[(unsigned-16) (emit ldrhi dest base n code*)]
[(integer-8) (emit ldrsbi dest base n code*)]
[(unsigned-8) (emit ldrbi dest base n code*)]
[else (sorry! who "unexpected mref type ~s" type)])]
[(eqv? n 0)
(Trivit (index)
(case type
[(integer-32 unsigned-32) (emit ldr dest base index code*)]
[(integer-16) (emit ldrsh dest base index code*)]
[(unsigned-16) (emit ldrh dest base index code*)]
[(integer-8) (emit ldrsb dest base index code*)]
[(unsigned-8) (emit ldrb dest base index code*)]
[else (sorry! who "unexpected mref type ~s" type)]))]
[else (sorry! who "expected %zero index or 0 offset, got ~s and ~s" index offset)])))))))
(define-who asm-store
(lambda (type)
(rec asm-store-internal
(lambda (code* base index offset src)
(let ([n (nanopass-case (L16 Triv) offset
[(immediate ,imm) imm]
[else (sorry! who "unexpected non-immediate offset ~s" offset)])])
(Trivit (src base)
(cond
[(eq? index %zero)
(case type
[(integer-32 unsigned-32) (emit stri src base n code*)]
[(integer-16 unsigned-16) (emit strhi src base n code*)]
[(integer-8 unsigned-8) (emit strbi src base n code*)]
[else (sorry! who "unexpected mref type ~s" type)])]
[(eqv? n 0)
(Trivit (index)
(case type
[(integer-32 unsigned-32) (emit str src base index code*)]
[(integer-16 unsigned-16) (emit strh src base index code*)]
[(integer-8 unsigned-8) (emit strb src base index code*)]
[else (sorry! who "unexpected mref type ~s" type)]))]
[else (sorry! who "expected %zero index or 0 offset, got ~s and ~s" index offset)])))))))
(define-who asm-flop-2
(lambda (op)
(lambda (code* src1 src2 dest)
(Trivit (src1 src2 dest)
(emit vldr.dbl %flreg1 src1 0
(emit vldr.dbl %flreg2 src2 0
(let ([code* (emit vstr.dbl %flreg1 dest 0 code*)])
(case op
[(fl+) (emit vadd %flreg1 %flreg1 %flreg2 code*)]
[(fl-) (emit vsub %flreg1 %flreg1 %flreg2 code*)]
[(fl*) (emit vmul %flreg1 %flreg1 %flreg2 code*)]
[(fl/) (emit vdiv %flreg1 %flreg1 %flreg2 code*)]
[else (sorry! who "unrecognized op ~s" op)]))))))))
(define asm-flsqrt
(lambda (code* src dest)
(Trivit (src dest)
(emit vldr.dbl %flreg1 src 0
(emit vsqrt %flreg1 %flreg1
(emit vstr.dbl %flreg1 dest 0 code*))))))
(define asm-trunc
(lambda (code* dest flonumreg)
(Trivit (dest flonumreg)
(emit vldr.dbl %flreg1 flonumreg 0
(emit vcvt.dbl->s32 %flreg1 %flreg1
(emit vmov.s32->gpr %flreg1 dest code*))))))
(define asm-flt
(lambda (code* src flonumreg)
(Trivit (src flonumreg)
(emit vmov.gpr->s32 %flreg1 src
(emit vcvt.s32->dbl %flreg1 %flreg1
(emit vstr.dbl %flreg1 flonumreg 0 code*))))))
(define-who asm-swap
(lambda (type)
(rec asm-swap-internal
(lambda (code* dest src)
(Trivit (dest src)
(case type
[(integer-16) (emit revsh dest src code*)]
[(unsigned-16) (emit rev16 dest src code*)]
[(integer-32 unsigned-32) (emit rev dest src code*)]
[else (sorry! who "unexpected asm-swap type argument ~s" type)]))))))
(define asm-lock
; tmp = ldrex src
; cmp tmp, 0
; bne L1 (+2)
; tmp = 1
; tmp = strex tmp, src
;L1:
(lambda (code* src tmp)
(Trivit (src tmp)
(emit ldrex tmp src
(emit cmpi tmp 0
(emit bnei 1
(emit movi1 tmp 1
(emit strex tmp tmp src code*))))))))
(define-who asm-lock+/-
; L:
; tmp1 = ldrex src
; tmp1 = tmp1 +/- 1
; tmp2 = strex tmp1, src
; cmp tmp2, 0
; bne L (-6)
; cmp tmp1, 0
(lambda (op)
(lambda (code* src tmp1 tmp2)
(Trivit (src tmp1 tmp2)
(emit ldrex tmp1 src
(let ([code* (emit strex tmp2 tmp1 src
(emit cmpi tmp2 0
(emit bnei -6
(emit cmpi tmp1 0 code*))))])
(case op
[(locked-incr!) (emit addi #f tmp1 tmp1 1 code*)]
[(locked-decr!) (emit subi #f tmp1 tmp1 1 code*)]
[else (sorry! who "unexpected op ~s" op)])))))))
(define-who asm-cas
; tmp = ldrex src
; cmp tmp, old
; bne L (+2)
; tmp2 = strex new, src
; cmp tmp2, 0
; L:
(lambda (code* src old new tmp1 tmp2)
(Trivit (src old new tmp1 tmp2)
(emit ldrex tmp1 src
(emit cmp tmp1 old
(emit bnei 1
(emit strex tmp2 new src
(emit cmpi tmp2 0
code*))))))))
(define asm-fl-relop
(lambda (info)
(lambda (l1 l2 offset x y)
(Trivit (x y)
(values
(emit vldr.dbl %flreg1 x 0
(emit vldr.dbl %flreg2 y 0
(emit vcmp %flreg1 %flreg2
(emit fpscr->apsr '()))))
(asm-conditional-jump info l1 l2 offset))))))
(define-who asm-relop
(lambda (info)
(rec asm-relop-internal
(lambda (l1 l2 offset x y)
(Trivit (x y)
(unless (ax-reg? x) (sorry! who "unexpected first operand ~s" x))
(values
(record-case y
[(imm) (n) (emit cmpi x n '())]
[(reg) ignore (emit cmp x y '())]
[else (sorry! who "unexpected second operand ~s" y)])
(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))))))
(define asm-pop-multiple
(lambda (regs)
(lambda (code*)
(emit popm regs code*))))
(define asm-push-multiple
(lambda (regs)
(lambda (code*)
(emit pushm regs code*))))
(define asm-vpush-multiple
(lambda (reg n)
(lambda (code*)
(emit vpushm reg n code*))))
(define asm-vpop-multiple
(lambda (reg n)
(lambda (code*)
(emit vpopm reg n code*))))
(define asm-save-flrv
(lambda (code*)
(let ([sp (cons 'reg %sp)])
(emit subi #f sp sp 8
(emit vstr.dbl %Cfpretval sp 0 code*)))))
(define asm-restore-flrv
(lambda (code*)
(let ([sp (cons 'reg %sp)])
(emit vldr.dbl %Cfpretval sp 0
(emit addi #f sp sp 8 code*)))))
(define asm-read-counter
(case-lambda
[(k)
(lambda (code* dest)
(Trivit (dest)
(emit mrc 'al 15 0 dest 15 12 k code*)))]
[()
(lambda (code* dest src)
(Trivit (dest src)
(emit cmpi src 0
(emit mrc 'eq 15 0 dest 15 12 2
(emit mrc 'ne 15 0 dest 15 12 3 code*)))))]))
(define asm-library-jump
(lambda (l)
(asm-helper-jump '()
`(arm32-jump ,(constant code-data-disp) (library-code ,(libspec-label-libspec l))))))
(define asm-library-call
(lambda (libspec save-ra?)
(let ([target `(arm32-call ,(constant code-data-disp) (library-code ,libspec))])
(rec asm-asm-call-internal
(lambda (code* dest jmp-tmp . ignore) ; ignore arguments, which must be in fixed locations
(asm-helper-call code* target save-ra? jmp-tmp))))))
(define asm-library-call!
(lambda (libspec save-ra?)
(let ([target `(arm32-call ,(constant code-data-disp) (library-code ,libspec))])
(rec asm-asm-call-internal
(lambda (code* jmp-tmp . ignore) ; ignore arguments, which must be in fixed locations
(asm-helper-call code* target save-ra? jmp-tmp))))))
(define asm-c-simple-call
(lambda (entry save-ra?)
(let ([target `(arm32-call 0 (entry ,entry))])
(rec asm-c-simple-call-internal
(lambda (code* jmp-tmp . ignore)
(asm-helper-call code* target save-ra? jmp-tmp))))))
(define-who asm-indirect-call
(lambda (code* dest lr . ignore)
(safe-assert (eq? lr %lr))
(Trivit (dest)
(unless (ax-reg? dest) (sorry! who "unexpected dest ~s" dest))
(emit blx dest code*))))
(define asm-direct-jump
(lambda (l offset)
(asm-helper-jump '() (make-funcrel 'arm32-jump l offset))))
(define asm-literal-jump
(lambda (info)
(asm-helper-jump '()
`(arm32-jump ,(info-literal-offset info) (,(info-literal-type info) ,(info-literal-addr info))))))
(define-who asm-indirect-jump
(lambda (src)
(Trivit (src)
(record-case src
[(reg) ignore (emit bx src '())]
[(disp) (n breg)
(safe-assert (or (unsigned12? n) (unsigned12? (- n))))
(emit ldri `(reg . ,%pc) `(reg . ,breg) n '())]
[(index) (n ireg breg)
(safe-assert (eqv? n 0))
(emit ldr `(reg . ,%pc) `(reg . ,breg) `(reg . ,ireg) '())]
[else (sorry! who "unexpected src ~s" src)]))))
(define asm-logtest
(lambda (i? info)
(lambda (l1 l2 offset x y)
(Trivit (x y)
(values
(record-case y
[(imm) (n) (emit tsti x n '())]
[else (emit tst x y '())])
(let-values ([(l1 l2) (if i? (values l2 l1) (values l1 l2))])
(asm-conditional-jump info l2 l1 offset)))))))
(define asm-get-tc
(let ([target `(arm32-call 0 (entry ,(lookup-c-entry get-thread-context)))])
(lambda (code* dest jmp-tmp . ignore) ; dest is ignored, since it is always Cretval
(asm-helper-call code* target #f jmp-tmp))))
(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) 4)])
(cond
[(code-offset-funky12 disp)
(Trivit (dest)
; aka adr, encoding A1
(emit addi #f dest `(reg . ,%pc) disp '()))]
[(code-offset-funky12 (- disp))
(Trivit (dest)
; aka adr, encoding A2
(emit subi #f dest `(reg . ,%pc) (- disp) '()))]
[else #f])))]
[else #f])
(asm-move '() dest (with-output-language (L16 Triv) `(label-ref ,l ,incr-offset)))))))
(define-who asm-jump
(lambda (l next-addr)
(make-gchunk l next-addr
(cond
[(local-label-offset l) =>
(lambda (offset)
(let ([disp (fx- next-addr offset)])
(cond
[(eqv? disp 0) '()]
[(branch-disp? disp) (emit bra `(label ,disp ,l) '())]
; will have to deal with this on architectures with smaller displacements.
; problem is we'll need a temp reg, and we discover this way past register
; allocation. so possibly compute the max possible code-object size at
; instruction selection time. when max possible size exceeds branch range
; (plus or minus), supply asm-jump and others like it an unspillable. don't
; want to supply an unspillable for smaller code objects since this
; unnecessarily constrains the register allocator.
[else (sorry! who "no support for code objects > 32MB in length")])))]
[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)
(if (local-label? l)
(cond
[(local-label-offset l) =>
(lambda (offset)
(let ([disp (fx- next-addr offset)])
(unless (branch-disp? disp) (sorry! who "no support for code objects > 32MB in length"))
(values disp `(label ,disp ,l))))]
[else (values 0 `(label 0 ,l))])
(sorry! who "unexpected label ~s" l))))
(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 ([(ignore #,opnd2) (get-disp-opnd (fx+ next-addr (asm-size* code*)) #,l2)])
#,body))))
(define ops->code
(lambda (bop opnd)
#`(emit #,bop #,opnd code*)))
(define handle-reverse
(lambda (e opnd l)
(syntax-case e (r?)
[(r? c1 c2) #`(if reversed? #,(ops->code #'c1 opnd) #,(ops->code #'c2 opnd))]
[_ (ops->code e opnd)])))
(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
[(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 (sorry! who "~s branch type is currently unsupported" type)]))))])))
(pred-case
[(eq?) (i? bne beq)]
[(u<) (i? (r? bls bcs) (r? bhi bcc))]
[(<) (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) (i? bvc bvs)]
[(multiply-overflow) (i? beq bne)] ; result of comparing sign bit of low word with all bits in high word: eq if no overflow, ne if overflow
[(carry) (i? bcc bcs)]
[(fl<) (i? (r? ble bcs) (r? bgt bcc))]
[(fl<=) (i? (r? blt bhi) (r? bge bls))]
[(fl=) (i? bne beq)]))))))
(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)
; NB: kills %ts, unbeknownst to the instruction scheduler
; NB: jmp-tmp should be included in jump syntax, introduced by md-handle-jump, and passed in from code generator
; NB: probably works despite this since %ts is never live at the jmp point anyway
(let ([jmp-tmp (cons 'reg %ts)])
(ax-mov32 jmp-tmp 0
(emit bx jmp-tmp
(asm-helper-relocation code* reloc))))))
(define asm-kill
(lambda (code* dest)
code*))
(define ax-save/restore
; push/pop while maintaining 8-byte alignment
(lambda (code* reg-ea p)
(let ([sp (cons 'reg %sp)])
(emit str/preidx reg-ea sp -8
(p (emit ldr/postidx reg-ea sp 8 code*))))))
(define asm-helper-call
(lambda (code* reloc save-ra? jmp-tmp)
; NB: kills %lr
(let ([jmp-tmp (cons 'reg jmp-tmp)])
(define maybe-save-ra
(lambda (code* p)
(if save-ra?
(ax-save/restore code* (cons 'reg %lr) p)
(p code*))))
(maybe-save-ra code*
(lambda (code*)
(ax-mov32 jmp-tmp 0
(emit blx jmp-tmp
(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)
(cons*
(if (target-fixnum? lpm)
`(long . ,(fix lpm))
`(abs 0 (object ,lpm)))
(aop-cons* `(asm livemask: ,(format "~b" lpm))
'(code-top-link)
(aop-cons* `(asm code-top-link)
`(long . ,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*))))))))))
; NB: reads from %lr...should be okay if declare-intrinsics sets up return-live* properly
(define asm-return (lambda () (emit bx (cons 'reg %lr) '())))
(define asm-c-return (lambda (info) (emit bx (cons 'reg %lr) '())))
(define-who asm-shiftop
(lambda (op)
(lambda (code* dest src0 src1)
(Trivit (dest src0 src1)
(record-case src1
[(imm) (n) (emit shifti dest src0 n op code*)]
[else (emit shift dest src0 src1 op code*)])))))
(define asm-lognot
(lambda (code* dest src)
(Trivit (dest src)
(emit mvn dest src code*))))
(define asm-enter values)
(define-who asm-inc-cc-counter
(lambda (code* addr val tmp)
(Trivit (addr val tmp)
(define do-ldr
(lambda (offset k code*)
(emit ldri tmp addr offset (k (emit stri tmp addr offset code*)))))
(define do-add/cc
(lambda (code*)
(record-case val
[(imm) (n) (emit addi #t tmp tmp n code*)]
[else (emit add #t tmp tmp val code*)])))
(do-ldr 0
do-add/cc
(emit bnei 2
(do-ldr 4
(lambda (code*)
(emit addi #f tmp tmp 1 code*))
code*))))))
(module (asm-foreign-call asm-foreign-callable)
(define align (lambda (b x) (let ([k (- b 1)]) (fxlogand (fx+ x k) (fxlognot k)))))
(define (double-member? m) (and (eq? (car m) 'float)
(fx= (cadr m) 8)))
(define (float-member? m) (and (eq? (car m) 'float)
(fx= (cadr m) 4)))
(define (indirect-result-that-fits-in-registers? result-type)
(nanopass-case (Ltype Type) result-type
[(fp-ftd& ,ftd)
(let* ([members ($ftd->members ftd)]
[num-members (length members)])
(or (fx<= ($ftd-size ftd) 4)
(and (fx= num-members 1)
;; a struct containing only int64 is not returned in a register
(or (not ($ftd-compound? ftd))))
(and (fx<= num-members 4)
(or (andmap double-member? members)
(andmap float-member? members)))))]
[else #f]))
(define sgl-regs (lambda () (list %Cfparg1 %Cfparg1b %Cfparg2 %Cfparg2b %Cfparg3 %Cfparg3b %Cfparg4 %Cfparg4b
%Cfparg5 %Cfparg5b %Cfparg6 %Cfparg6b %Cfparg7 %Cfparg7b %Cfparg8 %Cfparg8b)))
(define-who asm-foreign-call
(with-output-language (L13 Effect)
(define int-regs (lambda () (list %Carg1 %Carg2 %Carg3 %Carg4)))
(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-int64-stack
(lambda (offset)
(lambda (lorhs hirhs) ; requires rhs
(%seq
(set! ,(%mref ,%sp ,offset) ,lorhs)
(set! ,(%mref ,%sp ,(fx+ offset 4)) ,hirhs))))]
[load-int-indirect-stack
(lambda (offset from-offset size)
(lambda (x) ; requires var
(case size
[(3)
(%seq
(set! ,(%mref ,%sp ,offset) (inline ,(make-info-load 'integer-16 #f) ,%load ,x ,%zero (immediate ,from-offset)))
(set! ,(%mref ,%sp ,(fx+ offset 2)) (inline ,(make-info-load 'integer-8 #f) ,%load ,x ,%zero (immediate ,(fx+ from-offset 2)))))]
[else
`(set! ,(%mref ,%sp ,offset) ,(case size
[(1) `(inline ,(make-info-load 'integer-8 #f) ,%load ,x ,%zero (immediate ,from-offset))]
[(2) `(inline ,(make-info-load 'integer-16 #f) ,%load ,x ,%zero (immediate ,from-offset))]
[(4) (%mref ,x ,from-offset)]))])))]
[load-int64-indirect-stack
(lambda (offset from-offset)
(lambda (x) ; requires var
(%seq
(set! ,(%mref ,%sp ,offset) ,(%mref ,x ,from-offset))
(set! ,(%mref ,%sp ,(fx+ offset 4)) ,(%mref ,x ,(fx+ from-offset 4))))))]
[load-double-reg
(lambda (fpreg fp-disp)
(lambda (x) ; requires var
`(inline ,(make-info-loadfl fpreg) ,%load-double ,x ,%zero (immediate ,fp-disp))))]
[load-single-reg
(lambda (fpreg fp-disp single?)
(lambda (x) ; requires var
`(inline ,(make-info-loadfl fpreg) ,(if single? %load-single %load-double->single) ,x ,%zero (immediate ,fp-disp))))]
[load-int-reg
(lambda (ireg)
(lambda (x)
`(set! ,ireg ,x)))]
[load-int64-reg
(lambda (loreg hireg)
(lambda (lo hi)
(%seq
(set! ,loreg ,lo)
(set! ,hireg ,hi))))]
[load-int-indirect-reg
(lambda (ireg from-offset size)
(lambda (x)
(case size
[(3)
(let ([tmp %lr]) ; ok to use %lr here?
(%seq
(set! ,ireg (inline ,(make-info-load 'integer-16 #f) ,%load ,x ,%zero (immediate ,from-offset)))
(set! ,tmp (inline ,(make-info-load 'integer-8 #f) ,%load ,x ,%zero (immediate ,(fx+ from-offset 2))))
(set! ,tmp ,(%inline sll ,tmp (immediate 16)))
(set! ,ireg ,(%inline + ,ireg ,tmp))))]
[else
`(set! ,ireg ,(case size
[(1) `(inline ,(make-info-load 'integer-8 #f) ,%load ,x ,%zero (immediate ,from-offset))]
[(2) `(inline ,(make-info-load 'integer-16 #f) ,%load ,x ,%zero (immediate ,from-offset))]
[(4) (%mref ,x ,from-offset)]))])))]
[load-int64-indirect-reg
(lambda (loreg hireg from-offset)
(lambda (x)
(%seq
(set! ,loreg ,(%mref ,x ,from-offset))
(set! ,hireg ,(%mref ,x ,(fx+ from-offset 4))))))]
[do-args
(lambda (types)
; sgl* is always of even-length, i.e., has a sgl/dbl reg first
; bsgl is set to "b" single (second half of double) if we have one to fill
(let loop ([types types] [locs '()] [live* '()] [int* (int-regs)] [sgl* (sgl-regs)] [bsgl #f] [isp 0])
(if (null? types)
(values isp locs live*)
(nanopass-case (Ltype Type) (car types)
[(fp-double-float)
(if (null? sgl*)
(let ([isp (align 8 isp)])
(loop (cdr types)
(cons (load-double-stack isp) locs)
live* int* '() #f (fx+ isp 8)))
(loop (cdr types)
(cons (load-double-reg (car sgl*) (constant flonum-data-disp)) locs)
live* int* (cddr sgl*) bsgl isp))]
[(fp-single-float)
(if bsgl
(loop (cdr types)
(cons (load-single-reg bsgl (constant flonum-data-disp) #f) locs)
live* int* sgl* #f isp)
(if (null? sgl*)
(loop (cdr types)
(cons (load-single-stack isp) locs)
live* int* '() #f (fx+ isp 4))
(loop (cdr types)
(cons (load-single-reg (car sgl*) (constant flonum-data-disp) #f) locs)
live* int* (cddr sgl*) (cadr sgl*) isp)))]
[(fp-ftd& ,ftd)
(let ([size ($ftd-size ftd)]
[members ($ftd->members ftd)]
[combine-loc (lambda (loc f)
(if loc
(lambda (x) (%seq ,(loc x) ,(f x)))
f))])
(case ($ftd-alignment ftd)
[(8)
(let* ([int* (if (even? (length int*)) int* (cdr int*))]
[num-members (length members)]
[doubles? (and (fx<= num-members 4)
(andmap double-member? members))])
;; Sequence of up to 4 doubles that fits in registers?
(cond
[(and doubles?
(fx>= (length sgl*) (fx* 2 num-members)))
;; Allocate each double to a register
(let dbl-loop ([size size] [offset 0] [sgl* sgl*] [loc #f])
(cond
[(fx= size 0)
(loop (cdr types) (cons loc locs) live* int* sgl* #f isp)]
[else
(dbl-loop (fx- size 8) (fx+ offset 8) (cddr sgl*)
(combine-loc loc (load-double-reg (car sgl*) offset)))]))]
[else
;; General case; for non-doubles, use integer registers while available,
;; possibly splitting between registers and stack
(let obj-loop ([size size] [offset 0] [loc #f]
[live* live*] [int* int*] [isp isp])
(cond
[(fx= size 0)
(loop (cdr types) (cons loc locs) live* int* sgl* bsgl isp)]
[else
(if (or (null? int*) doubles?)
(let ([isp (align 8 isp)])
(obj-loop (fx- size 8) (fx+ offset 8)
(combine-loc loc (load-int64-indirect-stack isp offset))
live* int* (fx+ isp 8)))
(obj-loop (fx- size 8) (fx+ offset 8)
(combine-loc loc (load-int64-indirect-reg (car int*) (cadr int*) offset))
(cons* (car int*) (cadr int*) live*) (cddr int*) isp))]))]))]
[else
(let* ([num-members (length members)]
[floats? (and (fx<= num-members 4)
(andmap float-member? members))])
;; Sequence of up to 4 floats that fits in registers?
(cond
[(and floats?
(fx>= (fx+ (length sgl*) (if bsgl 1 0)) num-members))
;; Allocate each float to register
(let flt-loop ([size size] [offset 0] [sgl* sgl*] [bsgl bsgl] [loc #f])
(cond
[(fx= size 0)
(loop (cdr types) (cons loc locs) live* int* sgl* bsgl isp)]
[else
(flt-loop (fx- size 4) (fx+ offset 4)
(if bsgl sgl* (cddr sgl*))
(if bsgl #f (cadr sgl*))
(combine-loc loc (load-single-reg (or bsgl (car sgl*)) offset #t)))]))]
[else
;; General case; use integer registers while available,
;; possibly splitting between registers and stack
(let obj-loop ([size size] [offset 0] [loc #f]
[live* live*] [int* int*] [isp isp])
(cond
[(fx<= size 0)
(loop (cdr types) (cons loc locs) live* int* sgl* bsgl isp)]
[else
(if (or (null? int*) floats?)
(obj-loop (fx- size 4) (fx+ offset 4)
(combine-loc loc (load-int-indirect-stack isp offset (fxmin size 4)))
live* int* (fx+ isp 4))
(obj-loop (fx- size 4) (fx+ offset 4)
(combine-loc loc (load-int-indirect-reg (car int*) offset (fxmin size 4)))
(cons (car int*) live*) (cdr int*) isp))]))]))]))]
[else
(if (nanopass-case (Ltype Type) (car types)
[(fp-integer ,bits) (fx= bits 64)]
[(fp-unsigned ,bits) (fx= bits 64)]
[else #f])
(let ([int* (if (even? (length int*)) int* (cdr int*))])
(if (null? int*)
(let ([isp (align 8 isp)])
(loop (cdr types)
(cons (load-int64-stack isp) locs)
live* '() sgl* bsgl (fx+ isp 8)))
(loop (cdr types)
(cons (load-int64-reg (car int*) (cadr int*)) locs)
(cons* (car int*) (cadr int*) live*) (cddr int*) sgl* bsgl isp)))
(if (null? int*)
(loop (cdr types)
(cons (load-int-stack isp) locs)
live* '() sgl* bsgl (fx+ isp 4))
(loop (cdr types)
(cons (load-int-reg (car int*)) locs)
(cons (car int*) live*) (cdr int*) sgl* bsgl isp)))]))))]
[add-fill-result
(lambda (fill-result-here? result-type args-frame-size e)
(cond
[fill-result-here?
(nanopass-case (Ltype Type) result-type
[(fp-ftd& ,ftd)
(let* ([members ($ftd->members ftd)]
[num-members (length members)]
;; result pointer is stashed on the stack after all arguments:
[dest-x %r2]
[init-dest-e `(seq ,e (set! ,dest-x ,(%mref ,%sp ,args-frame-size)))])
(cond
[(and (fx<= num-members 4)
(or (andmap double-member? members)
(andmap float-member? members)))
;; double/float results are in floating-point registers
(let ([double? (and (pair? members) (double-member? (car members)))])
(let loop ([members members] [sgl* (sgl-regs)] [offset 0] [e init-dest-e])
(cond
[(null? members) e]
[else
(loop (cdr members)
(if double? (cddr sgl*) (cdr sgl*))
(fx+ offset (if double? 8 4))
`(seq
,e
(inline ,(make-info-loadfl (car sgl*)) ,(if double? %store-double %store-single)
,dest-x ,%zero (immediate ,offset))))])))]
[else
;; result is in %Cretval and maybe %r1
`(seq
,init-dest-e
,(case ($ftd-size ftd)
[(1) `(inline ,(make-info-load 'integer-8 #f) ,%store ,dest-x ,%zero (immediate 0) ,%Cretval)]
[(2) `(inline ,(make-info-load 'integer-16 #f) ,%store ,dest-x ,%zero (immediate 0) ,%Cretval)]
[(3) (%seq
(inline ,(make-info-load 'integer-16 #f) ,%store ,dest-x ,%zero (immediate 0) ,%Cretval)
(set! ,%Cretval ,(%inline srl ,%Cretval (immediate 16)))
(inline ,(make-info-load 'integer-8 #f) ,%store ,dest-x ,%zero (immediate 2) ,%Cretval))]
[(4) `(set! ,(%mref ,dest-x ,0) ,%Cretval)]
[(8) `(seq
(set! ,(%mref ,dest-x ,0) ,%Cretval)
(set! ,(%mref ,dest-x ,4) ,%r1))]))]))])]
[else e]))])
(lambda (info)
(safe-assert (reg-callee-save? %tc)) ; no need to save-restore
(let* ([arg-type* (info-foreign-arg-type* info)]
[result-type (info-foreign-result-type info)]
[fill-result-here? (indirect-result-that-fits-in-registers? result-type)])
(with-values (do-args (if fill-result-here? (cdr arg-type*) arg-type*))
(lambda (args-frame-size locs live*)
(let* ([frame-size (align 8 (+ args-frame-size
(if fill-result-here?
4
0)))]
[adjust-frame (lambda (op)
(lambda ()
(if (fx= frame-size 0)
`(nop)
`(set! ,%sp (inline ,null-info ,op ,%sp (immediate ,frame-size))))))])
(values
(adjust-frame %-)
(let ([locs (reverse locs)])
(cond
[fill-result-here?
;; stash extra argument on the stack to be retrieved after call and filled with the result:
(cons (load-int-stack args-frame-size) locs)]
[else locs]))
(lambda (t0)
(add-fill-result fill-result-here? result-type args-frame-size
`(inline ,(make-info-kill*-live* (reg-list %r0) 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 ,%r0)))]
[(16) (lambda (lvalue) `(set! ,lvalue ,(%inline sext16 ,%r0)))]
[(32) (lambda (lvalue) `(set! ,lvalue ,%r0))]
[(64) (lambda (lvlow lvhigh)
`(seq
(set! ,lvhigh ,%r1)
(set! ,lvlow ,%r0)))]
[else (sorry! who "unexpected asm-foreign-procedures fp-integer size ~s" bits)])]
[(fp-unsigned ,bits)
(case bits
[(8) (lambda (lvalue) `(set! ,lvalue ,(%inline zext8 ,%r0)))]
[(16) (lambda (lvalue) `(set! ,lvalue ,(%inline zext16 ,%r0)))]
[(32) (lambda (lvalue) `(set! ,lvalue ,%r0))]
[(64) (lambda (lvlow lvhigh)
`(seq
(set! ,lvhigh ,%r1)
(set! ,lvlow ,%r0)))]
[else (sorry! who "unexpected asm-foreign-procedures fp-unsigned size ~s" bits)])]
[else (lambda (lvalue) `(set! ,lvalue ,%r0))])
(adjust-frame %+)))
)))))))
(define-who asm-foreign-callable
#|
Frame Layout
+---------------------------+
| |
| incoming stack args |
sp+52+R+X+Y+Z+W: | |
+---------------------------+<- 8-byte boundary
| |
| saved int reg args | 0-4 words
sp+52+R+X+Y+Z: | |
+---------------------------+
| |
| pad word if necessary | 0-1 words
sp+52+R+X+Y: | |
+---------------------------+<- 8-byte boundary
| |
| saved float reg args | 0-16 words
sp+52+R+X: | |
+---------------------------+<- 8-byte boundary
| |
| &-return space | up to 8 words
sp+52+R: | |
+---------------------------+<- 8-byte boundary
| |
| pad word if necessary | 0-1 words
sp+52: | |
+---------------------------+
| |
| callee-save regs + lr | 13 words
sp+0: | callee-save fpregs |
+---------------------------+<- 8-byte boundary
X = 0 or 4 (depending on whether pad is present)
Y = int-reg-bytes
Z = float-reg-bytes
|#
(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 ,(%mref ,%sp ,offset))]
[else (sorry! who "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 ,(%mref ,%sp ,offset))]
[else (sorry! who "unexpected load-int-stack fp-unsigned size ~s" bits)])]
[else `(set! ,lvalue ,(%mref ,%sp ,offset))]))))
(define load-int64-stack
(lambda (offset)
(lambda (lolvalue hilvalue)
(%seq
(set! ,lolvalue ,(%mref ,%sp ,offset))
(set! ,hilvalue ,(%mref ,%sp ,(fx+ offset 4)))))))
(define load-stack-address
(lambda (offset)
(lambda (lvalue)
`(set! ,lvalue ,(%inline + ,%sp (immediate ,offset))))))
(define count-reg-args
(lambda (types synthesize-first?)
; bsgl? is #t iff we have a "b" single (second half of double) float reg to fill
(let f ([types types] [iint (if synthesize-first? -1 0)] [idbl 0] [bsgl? #f])
(if (null? types)
(values iint idbl)
(nanopass-case (Ltype Type) (car types)
[(fp-double-float)
(if (fx< idbl 8)
(f (cdr types) iint (fx+ idbl 1) bsgl?)
(f (cdr types) iint idbl #f))]
[(fp-single-float)
(if bsgl?
(f (cdr types) iint idbl #f)
(if (fx< idbl 8)
(f (cdr types) iint (fx+ idbl 1) #t)
(f (cdr types) iint idbl #f)))]
[(fp-ftd& ,ftd)
(let* ([size ($ftd-size ftd)]
[members ($ftd->members ftd)]
[num-members (length members)])
(cond
[(and (fx<= num-members 4)
(andmap double-member? members))
;; doubles are either in registers or all on stack
(if (fx<= (fx+ idbl num-members) 8)
(f (cdr types) iint (fx+ idbl num-members) #f)
;; no more floating-point registers should be used, but ok if we count more
(f (cdr types) iint idbl #f))]
[(and (fx<= num-members 4)
(andmap float-member? members))
;; floats are either in registers or all on stack
(let ([amt (fxsrl (align 2 (fx- num-members (if bsgl? 1 0))) 1)])
(if (fx<= (fx+ idbl amt) 8)
(let ([odd-floats? (fxodd? num-members)])
(if bsgl?
(f (cdr types) iint (+ idbl amt) (not odd-floats?))
(f (cdr types) iint (+ idbl amt) odd-floats?)))
;; no more floating-point registers should be used, but ok if we count more
(f (cdr types) iint idbl #f)))]
[(fx= 8 ($ftd-alignment ftd))
(f (cdr types) (fxmin 4 (fx+ (align 2 iint) (fxsrl size 2))) idbl bsgl?)]
[else
(let ([size (align 4 size)])
(f (cdr types) (fxmin 4 (fx+ iint (fxsrl size 2))) idbl bsgl?))]))]
[else
(if (nanopass-case (Ltype Type) (car types)
[(fp-integer ,bits) (fx= bits 64)]
[(fp-unsigned ,bits) (fx= bits 64)]
[else #f])
(let ([iint (align 2 iint)])
(f (cdr types) (if (fx< iint 4) (fx+ iint 2) iint) idbl bsgl?))
(f (cdr types) (if (fx< iint 4) (fx+ iint 1) iint) idbl bsgl?))])))))
(define do-stack
; all of the args are on the stack at this point, though not contiguous since
; we push all of the int reg args with one push instruction and all of the
; float reg args with another (v)push instruction; the saved int regs
; continue on into the stack variables, which is convenient when a struct
; argument is split across registers and the stack
(lambda (types saved-reg-bytes pre-pad-bytes return-bytes float-reg-bytes post-pad-bytes int-reg-bytes
synthesize-first?)
(let* ([return-space-offset (fx+ saved-reg-bytes pre-pad-bytes)]
[float-reg-offset (fx+ return-space-offset return-bytes)]
[int-reg-offset (fx+ float-reg-offset float-reg-bytes post-pad-bytes)]
[stack-arg-offset (fx+ int-reg-offset int-reg-bytes)])
(let loop ([types (if synthesize-first? (cdr types) types)]
[locs '()]
[iint 0]
[idbl 0]
[bsgl-offset #f]
[int-reg-offset int-reg-offset]
[float-reg-offset float-reg-offset]
[stack-arg-offset stack-arg-offset])
(if (null? types)
(let ([locs (reverse locs)])
(if synthesize-first?
(cons (load-stack-address return-space-offset)
locs)
locs))
(nanopass-case (Ltype Type) (car types)
[(fp-double-float)
(if (< idbl 8)
(loop (cdr types)
(cons (load-double-stack float-reg-offset) locs)
iint (fx+ idbl 1) bsgl-offset int-reg-offset (fx+ float-reg-offset 8) stack-arg-offset)
(let ([stack-arg-offset (align 8 stack-arg-offset)])
(loop (cdr types)
(cons (load-double-stack stack-arg-offset) locs)
iint 8 #f int-reg-offset float-reg-offset (fx+ stack-arg-offset 8))))]
[(fp-single-float)
(if bsgl-offset
(loop (cdr types)
(cons (load-single-stack bsgl-offset) locs)
iint idbl #f int-reg-offset float-reg-offset stack-arg-offset)
(if (< idbl 8)
(loop (cdr types)
; with big-endian ARM might need to adjust offset +/- 4 since pair of
; single floats in a pushed double float might be reversed
(cons (load-single-stack float-reg-offset) locs)
iint (fx+ idbl 1) (fx+ float-reg-offset 4) int-reg-offset (fx+ float-reg-offset 8) stack-arg-offset)
(loop (cdr types)
(cons (load-single-stack stack-arg-offset) locs)
iint 8 #f int-reg-offset float-reg-offset (fx+ stack-arg-offset 4))))]
[(fp-ftd& ,ftd)
(let* ([size ($ftd-size ftd)]
[members ($ftd->members ftd)]
[num-members (length members)])
(cond
[(and (fx<= num-members 4)
(andmap double-member? members))
;; doubles are either in registers or all on stack
(if (fx<= (fx+ idbl num-members) 8)
(loop (cdr types)
(cons (load-stack-address float-reg-offset) locs)
iint (fx+ idbl num-members) #f int-reg-offset (fx+ float-reg-offset size) stack-arg-offset)
(let ([stack-arg-offset (align 8 stack-arg-offset)])
(loop (cdr types)
(cons (load-stack-address stack-arg-offset) locs)
iint 8 #f int-reg-offset #f (fx+ stack-arg-offset size))))]
[(and (fx<= num-members 4)
(andmap float-member? members))
;; floats are either in registers or all on stack
(let ([amt (fxsrl (align 2 (fx- num-members (if bsgl-offset 1 0))) 1)])
(if (fx<= (fx+ idbl amt) 8)
(let ([odd-floats? (fxodd? num-members)])
(if bsgl-offset
(let ([dbl-size (align 8 (fx- size 4))])
(loop (cdr types)
(cons (load-stack-address bsgl-offset) locs)
iint (fx+ idbl amt) (if odd-floats? #f (+ bsgl-offset size)) int-reg-offset
(fx+ float-reg-offset dbl-size) stack-arg-offset))
(let ([dbl-size (align 8 size)])
(loop (cdr types)
(cons (load-stack-address float-reg-offset) locs)
iint (fx+ idbl amt) (and odd-floats? (fx+ float-reg-offset size)) int-reg-offset
(fx+ float-reg-offset dbl-size) stack-arg-offset))))
(loop (cdr types)
(cons (load-stack-address stack-arg-offset) locs)
iint 8 #f int-reg-offset float-reg-offset (fx+ stack-arg-offset size))))]
[(fx= 8 ($ftd-alignment ftd))
(let ([int-reg-offset (if (fxeven? iint) int-reg-offset (fx+ int-reg-offset 4))]
[iint (align 2 iint)]
[amt (fxsrl size 2)])
(if (fx< iint 4) ; argument starts in registers, may continue on stack
(loop (cdr types)
(cons (load-stack-address int-reg-offset) locs)
(fxmin 4 (fx+ iint amt)) idbl bsgl-offset (fx+ int-reg-offset size) float-reg-offset
(fx+ stack-arg-offset (fxmax 0 (fx* 4 (fx- (fx+ iint amt) 4)))))
(let ([stack-arg-offset (align 8 stack-arg-offset)])
(loop (cdr types)
(cons (load-stack-address stack-arg-offset) locs)
iint idbl bsgl-offset int-reg-offset float-reg-offset (fx+ stack-arg-offset size)))))]
[else
(let* ([size (align 4 size)]
[amt (fxsrl size 2)])
(if (fx< iint 4) ; argument starts in registers, may continue on stack
(loop (cdr types)
(cons (load-stack-address int-reg-offset) locs)
(fxmin 4 (fx+ iint amt)) idbl bsgl-offset (fx+ int-reg-offset size) float-reg-offset
(fx+ stack-arg-offset (fxmax 0 (fx* 4 (fx- (fx+ iint amt) 4)))))
(loop (cdr types)
(cons (load-stack-address stack-arg-offset) locs)
iint idbl bsgl-offset int-reg-offset float-reg-offset (fx+ stack-arg-offset size))))]))]
[else
(if (nanopass-case (Ltype Type) (car types)
[(fp-integer ,bits) (fx= bits 64)]
[(fp-unsigned ,bits) (fx= bits 64)]
[else #f])
(let ([int-reg-offset (if (fxeven? iint) int-reg-offset (fx+ int-reg-offset 4))]
[iint (align 2 iint)])
(if (fx= iint 4)
(let ([stack-arg-offset (align 8 stack-arg-offset)])
(loop (cdr types)
(cons (load-int64-stack stack-arg-offset) locs)
iint idbl bsgl-offset int-reg-offset float-reg-offset (fx+ stack-arg-offset 8)))
(loop (cdr types)
(cons (load-int64-stack int-reg-offset) locs)
(fx+ iint 2) idbl bsgl-offset (fx+ int-reg-offset 8) float-reg-offset stack-arg-offset)))
(if (fx= iint 4)
(loop (cdr types)
(cons (load-int-stack (car types) stack-arg-offset) locs)
iint idbl bsgl-offset int-reg-offset float-reg-offset (fx+ stack-arg-offset 4))
(loop (cdr types)
(cons (load-int-stack (car types) int-reg-offset) locs)
(fx+ iint 1) idbl bsgl-offset (fx+ int-reg-offset 4) float-reg-offset stack-arg-offset)))]))))))
(define do-result
(lambda (result-type synthesize-first? return-stack-offset)
(nanopass-case (Ltype Type) result-type
[(fp-ftd& ,ftd)
(let* ([members ($ftd->members ftd)]
[num-members (length members)])
(cond
[(and (fx<= 1 num-members 4)
(or (andmap double-member? members)
(andmap float-member? members)))
;; double/float results returned in floating-point registers
(values
(lambda ()
(let ([double? (and (pair? members) (double-member? (car members)))])
(let loop ([members members] [sgl* (sgl-regs)] [offset return-stack-offset] [e #f])
(cond
[(null? members) e]
[else
(loop (cdr members)
(if double? (cddr sgl*) (cdr sgl*))
(fx+ offset (if double? 8 4))
(let ([new-e
`(inline ,(make-info-loadfl (car sgl*)) ,(if double? %load-double %load-single)
,%sp ,%zero (immediate ,offset))])
(if e `(seq ,e ,new-e) new-e)))]))))
'()
($ftd-size ftd))]
[else
(case ($ftd-size ftd)
[(8)
(values (lambda ()
`(seq
(set! ,%Cretval ,(%mref ,%sp ,return-stack-offset))
(set! ,%r1 ,(%mref ,%sp ,(fx+ 4 return-stack-offset)))))
(list %Cretval %r1)
8)]
[else
(values (lambda ()
(case ($ftd-size ftd)
[(1)
(let ([rep (if ($ftd-unsigned? ftd) 'unsigned-8 'integer-8)])
`(set! ,%Cretval (inline ,(make-info-load rep #f) ,%load ,%sp ,%zero (immediate ,return-stack-offset))))]
[(2)
(let ([rep (if ($ftd-unsigned? ftd) 'unsigned-16 'integer-16)])
`(set! ,%Cretval (inline ,(make-info-load rep #f) ,%load ,%sp ,%zero (immediate ,return-stack-offset))))]
[else `(set! ,%Cretval ,(%mref ,%sp ,return-stack-offset))]))
(list %Cretval)
4)])]))]
[(fp-double-float)
(values (lambda (rhs)
`(inline ,(make-info-loadfl %Cfpretval) ,%load-double
,rhs ,%zero ,(%constant flonum-data-disp)))
'()
0)]
[(fp-single-float)
(values (lambda (rhs)
`(inline ,(make-info-loadfl %Cfpretval) ,%load-double->single
,rhs ,%zero ,(%constant flonum-data-disp)))
'()
0)]
[(fp-void)
(values (lambda () `(nop))
'()
0)]
[else
(cond
[(nanopass-case (Ltype Type) result-type
[(fp-integer ,bits) (fx= bits 64)]
[(fp-unsigned ,bits) (fx= bits 64)]
[else #f])
(values (lambda (lo hi)
`(seq
(set! ,%Cretval ,lo)
(set! ,%r1 ,hi)))
(list %Cretval %r1)
0)]
[else
(values (lambda (x)
`(set! ,%Cretval ,x))
(list %Cretval)
0)])])))
(lambda (info)
(define callee-save-regs+lr (list %r4 %r5 %r6 %r7 %r8 %r9 %r10 %r11 %lr))
(define callee-save-fpregs (list %flreg1 %flreg2)) ; must be consecutive
(define isaved (length callee-save-regs+lr))
(define fpsaved (length callee-save-fpregs))
(let* ([arg-type* (info-foreign-arg-type* info)]
[result-type (info-foreign-result-type info)]
[synthesize-first? (indirect-result-that-fits-in-registers? result-type)])
(let-values ([(iint idbl) (count-reg-args arg-type* synthesize-first?)])
(let ([saved-reg-bytes (fx+ (fx* isaved 4) (fx* fpsaved 8))]
[pre-pad-bytes (if (fxeven? isaved) 0 4)]
[int-reg-bytes (fx* iint 4)]
[post-pad-bytes (if (fxeven? iint) 0 4)]
[float-reg-bytes (fx* idbl 8)])
(let-values ([(get-result result-regs return-bytes) (do-result result-type synthesize-first?
(fx+ saved-reg-bytes pre-pad-bytes))])
(let ([return-bytes (align 8 return-bytes)])
(values
(lambda ()
(%seq
; save argument register values to the stack so we don't lose the values
; across possible calls to C while setting up the tc and allocating memory
,(if (fx= iint 0) `(nop) `(inline ,(make-info-kill*-live* '() (list-head (list %Carg1 %Carg2 %Carg3 %Carg4) iint)) ,%push-multiple))
; pad if necessary to force 8-byte boundary, and make room for indirect return:
,(let ([len (+ post-pad-bytes return-bytes)])
(if (fx= len 0) `(nop) `(set! ,%sp ,(%inline - ,%sp (immediate ,len)))))
,(if (fx= idbl 0) `(nop) `(inline ,(make-info-vpush %Cfparg1 idbl) ,%vpush-multiple))
; pad if necessary to force 8-byte boundary after saving callee-save-regs+lr
,(if (fx= pre-pad-bytes 0) `(nop) `(set! ,%sp ,(%inline - ,%sp (immediate 4))))
; save the callee save registers & return address
(inline ,(make-info-kill*-live* '() callee-save-regs+lr) ,%push-multiple)
(inline ,(make-info-vpush (car callee-save-fpregs) fpsaved) ,%vpush-multiple)
; set up tc for benefit of argument-conversion code, which might allocate
,(if-feature pthreads
(%seq
(set! ,%r0 ,(%inline get-tc))
(set! ,%tc ,%r0))
`(set! ,%tc (literal ,(make-info-literal #f 'entry (lookup-c-entry thread-context) 0))))))
; list of procedures that marshal arguments from their C stack locations
; to the Scheme argument locations
(do-stack arg-type* saved-reg-bytes pre-pad-bytes return-bytes float-reg-bytes post-pad-bytes int-reg-bytes
synthesize-first?)
get-result
(lambda ()
(in-context Tail
(%seq
; restore the callee save registers
(inline ,(make-info-vpush (car callee-save-fpregs) fpsaved) ,%vpop-multiple)
(inline ,(make-info-kill* callee-save-regs+lr) ,%pop-multiple)
; deallocate space for pad & arg reg values
(set! ,%sp ,(%inline + ,%sp (immediate ,(fx+ pre-pad-bytes int-reg-bytes post-pad-bytes float-reg-bytes return-bytes))))
; done
(asm-c-return ,null-info ,callee-save-regs+lr ... ,result-regs ...)))))))))))))))
)