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/ppc32.ss
2022-07-29 15:12:07 +02:00

3094 lines
132 KiB
Scheme

;;; ppc32.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:
;;; Registers:
;;; r0-r31: general purpose registers, 32 bits each
;;; f0-f31: floating point registers, 64 bits each
;;; cr0-cr7: condition register fields, 4 bits each
;;; lr: link register
;;; ctr: count register
;;; xer: fixed-point exception register
;;; fpscr: floating-point status and control register
;;; Register usage:
;;; r1: stack frame pointer -- callee-saved.
;;; 16-byte aligned, pointing at lowest allocated, valid
;;; stack frame, growing toward low addresses. contents
;;; of address point to the previously allocated stack frame.
;;; r2: system-reserved -- do not use
;;; r3, r4: return values -- caller-saved
;;; r3-r10: parameter passing -- caller-saved
;;; r13: small data area pointer -- shared objects shall not alter
;;; r0, r11, r12: unused -- caller-saved
;;; r14-r30: unused -- callee-saved
;;; r31: used for local variables or "environment pointers" -- callee-saved
;;; f1: floating point return value
;;; f1-f8: floating point parameter passing
;;; f0, f9-f13: unused -- callee-saved
;;; f14-f31: unused -- caller-saved
;;; cr2-cr4: callee-saved
;;; cr1, cr5-cr7: caller-saved
(define-registers
(reserved
[%tc %r29 #t 29]
[%sfp %r23 #t 23]
[%ap %r31 #t 31]
[%esp %r21 #t 21]
[%eap %r26 #t 26]
[%trap %r22 #t 22]
[%real-zero %r0 #f 0])
(allocable
#;[%zero #f 0]
[%ac0 %r11 #f 11]
[%xp %r20 #t 20]
[%ts %r14 #t 14]
[%td %r15 #t 15]
[%ac1 %r12 %deact #f 12]
[%ret %r17 #t 17]
[%cp %r24 #t 24]
[%yp %r27 #t 27]
[%tp %r28 #t 28]
[ %r3 %Carg1 %Cretval %Cretval-high #f 3]
[ %r4 %Carg2 %Cretval-low #f 4]
[ %r5 %Carg3 #f 5]
[ %r6 %Carg4 #f 6]
[ %r7 %Carg5 #f 7]
[ %r8 %Carg6 #f 8]
[ %r9 %Carg7 #f 9]
[ %r10 %Carg8 #f 10]
[ %r16 #t 16]
[ %r18 #t 18]
[ %r19 #t 19]
[ %r25 #t 25]
[ %r30 #t 30]
)
(machine-dependent
[%sp %Csp #t 1]
[%Ctoc #f 2] ;; operating system reserved
[%Csda #f 13] ;; might point to small data area, if used
[%flreg1 #f 0]
[%Cfparg1 %Cfpretval #f 1]
[%Cfparg2 #f 2]
[%Cfparg3 #f 3]
[%Cfparg4 #f 4]
[%Cfparg5 #f 5]
[%Cfparg6 #f 6]
[%Cfparg7 #f 7]
[%Cfparg8 #f 8]
[%flreg2 #f 9]
[%flreg3 #f 10]
[%flreg4 #f 11]
[%flreg5 #f 12]
[%flreg6 #f 13]
[%flreg7 #t 14]
[%flreg8 #t 15]
[%flreg9 #t 16]
[%flreg10 #t 17]
[%flreg11 #t 18]
[%flreg12 #t 19]
[%flreg13 #t 20]
[%flreg14 #t 21]
[%flreg15 #t 22]
[%flreg16 #t 23]
[%flreg17 #t 24]
[%flreg18 #t 25]
[%flreg19 #t 26]
[%flreg20 #t 27]
[%flreg21 #t 28]
[%flreg22 #t 29]
[%flreg23 #t 30]
[%flreg24 #t 31]
))
;;; 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-syntax define-imm-pred
(lambda (x)
(syntax-case x ()
[(_ pred?)
(with-syntax ([imm-pred? (construct-name #'pred? "imm-" #'pred?)])
#'(define imm-pred?
(lambda (x)
(nanopass-case (L15c Triv) x
[(immediate ,imm) (pred? imm)]
[else #f]))))])))
(define-imm-pred integer16?)
(define-imm-pred shifted-integer16?)
(define-imm-pred negatable-integer16?)
(define-imm-pred negatable-shifted-integer16?)
(define-imm-pred unsigned16?)
(define-imm-pred shifted-unsigned16?)
(define-imm-pred shift-count?)
(define imm-constant?
(lambda (x)
(nanopass-case (L15c Triv) x
[(immediate ,imm) #t]
[else #f])))
(define lvalue->ur
(lambda (x k)
(if (mref? x)
(let ([u (make-tmp 'u)])
(seq
(set-ur=mref u x)
(k u)))
(k x))))
(define mref->mref
(lambda (a k)
(define return
(lambda (x0 x1 imm)
; ppc 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) (integer16? imm)) (return x0 %zero imm)]
[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 ,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-pass imm->negative-imm : (L15c Triv) (ir) -> (L15d Triv) ()
(Lvalue : Lvalue (ir) -> Lvalue ()
[(mref ,lvalue1 ,lvalue2 ,imm) (sorry! who "unexpected mref ~s" ir)])
(Triv : Triv (ir) -> Triv ()
[(immediate ,imm) `(immediate ,(- imm))]))
(define-syntax coercible?
(syntax-rules ()
[(_ ?a ?aty*)
(let ([a ?a] [aty* ?aty*])
(or (memq 'ur aty*)
(and (memq 'shift-count aty*) (imm-shift-count? a))
(and (memq 'unsigned16 aty*) (imm-unsigned16? a))
(and (memq 'shifted-unsigned16 aty*) (imm-shifted-unsigned16? a))
(and (memq 'integer16 aty*) (imm-integer16? a))
(and (memq 'shifted-integer16 aty*) (imm-shifted-integer16? a))
(and (memq 'negated-integer16 aty*) (imm-negatable-integer16? a))
(and (memq 'negated-shifted-integer16 aty*) (imm-negatable-shifted-integer16? a))
(and (memq 'imm-constant aty*) (imm-constant? 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)]
[(or (and (memq 'shift-count aty*) (imm-shift-count? a))
(and (memq 'unsigned16 aty*) (imm-unsigned16? a))
(and (memq 'shifted-unsigned16 aty*) (imm-shifted-unsigned16? a))
(and (memq 'integer16 aty*) (imm-integer16? a))
(and (memq 'shifted-integer16 aty*) (imm-shifted-integer16? a))
(and (memq 'imm-constant aty*) (imm-constant? a)))
(k (imm->imm a))]
[(or (and (memq 'negated-integer16 aty*) (imm-negatable-integer16? a))
(and (memq 'negated-shifted-integer16 aty*) (imm-negatable-shifted-integer16? a)))
(k (imm->negative-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 %real-zero 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 %real-zero 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))
; x is not the same as z in any clause that follows a clause where (x z)
; and y is coercible to one of its types, however:
; WARNING: do not assume that if x isn't the same as z then x is independent
; of z, since x might be an mref with z as it's base or index
(define-instruction value (-)
[(op (z ur) (x ur) (y negated-integer16 negated-shifted-integer16))
`(set! ,(make-live-info) ,z (asm ,info ,asm-add ,x ,y))]
[(op (z ur) (x ur) (y ur))
`(set! ,(make-live-info) ,z (asm ,info ,asm-sub-from ,y ,x))])
(define-instruction value (-/ovfl)
[(op (z ur) (x ur) (y ur))
`(set! ,(make-live-info) ,z (asm ,info ,asm-sub-from/ovfl ,y ,x))])
(define-instruction value (-/eq)
[(op (z ur) (x ur) (y ur))
`(set! ,(make-live-info) ,z (asm ,info ,asm-sub-from/eq ,y ,x))])
(define-instruction value (+)
[(op (z ur) (x ur) (y integer16 shifted-integer16))
`(set! ,(make-live-info) ,z (asm ,info ,asm-add ,x ,y))]
[(op (z ur) (x integer16 shifted-integer16) (y ur))
`(set! ,(make-live-info) ,z (asm ,info ,asm-add ,y ,x))]
[(op (z ur) (x ur) (y ur))
`(set! ,(make-live-info) ,z (asm ,info ,asm-add ,x ,y))])
(define-instruction value (+/ovfl)
[(op (z ur) (x ur) (y ur))
`(set! ,(make-live-info) ,z (asm ,info ,asm-add/ovfl ,x ,y))])
(define-instruction value (+/carry)
[(op (z ur) (x ur) (y ur))
`(set! ,(make-live-info) ,z (asm ,info ,asm-add/carry ,x ,y))])
(define-instruction value (*)
[(op (z ur) (x ur) (y integer16))
`(set! ,(make-live-info) ,z (asm ,info ,asm-mul ,x ,y))]
[(op (z ur) (x integer16) (y ur))
`(set! ,(make-live-info) ,z (asm ,info ,asm-mul ,y ,x))]
[(op (z ur) (x ur) (y ur))
`(set! ,(make-live-info) ,z (asm ,info ,asm-mul ,x ,y))])
(define-instruction value (*/ovfl) ; ov flag set iff non-immediate
[(op (z ur) (x ur) (y ur))
`(set! ,(make-live-info) ,z (asm ,info ,asm-mul/ovfl ,x ,y))])
(define-instruction value (/)
[(op (z ur) (x ur) (y ur))
`(set! ,(make-live-info) ,z (asm ,info ,asm-div ,x ,y))])
(let ()
(define select-op
(lambda (op)
(case op
[(logand) asm-logand]
[(logor) asm-logor]
[(logxor) asm-logxor]
[else (sorry! #f "unexpected logical operator ~s" op)])))
(define-instruction value (logand logor logxor)
[(op (z ur) (x unsigned16 shifted-unsigned16) (y ur))
`(set! ,(make-live-info) ,z (asm ,info ,(select-op op) ,y ,x))]
[(op (z ur) (x ur) (y unsigned16 shifted-unsigned16))
`(set! ,(make-live-info) ,z (asm ,info ,(select-op op) ,x ,y))]
[(op (z ur) (x ur) (y ur))
`(set! ,(make-live-info) ,z (asm ,info ,(select-op op) ,x ,y))]))
(define-instruction value (lognot)
[(op (z ur) (x ur))
`(set! ,(make-live-info) ,z (asm ,info ,asm-lognot ,x))])
(define-instruction value (srl)
[(op (z ur) (x ur) (y shift-count))
(if (nanopass-case (L15d Triv) y [(immediate ,imm) (zero? imm)])
`(set! ,(make-live-info) ,z ,x)
`(set! ,(make-live-info) ,z (asm ,info ,asm-srl ,x ,y)))]
[(op (z ur) (x ur) (y ur))
`(set! ,(make-live-info) ,z (asm ,info ,asm-srl ,x ,y))])
(let ()
(define select-op
(lambda (op)
(case op
[(sra) asm-sra]
[(sll) asm-sll])))
(define-instruction value (sra sll)
[(op (z ur) (x ur) (y shift-count ur))
`(set! ,(make-live-info) ,z (asm ,info ,(select-op 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-constant))
`(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))
(let ([offset (info-lea-offset info)])
(if (or (integer16? offset) (shifted-integer16? offset))
`(set! ,(make-live-info) ,z (asm ,info ,asm-add ,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 ,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 (or (integer16? offset) (shifted-integer16? offset))
`(set! ,(make-live-info) ,u (asm ,info ,asm-add ,y (immediate ,offset)))
(seq
`(set! ,(make-live-info) ,u (immediate ,offset))
`(set! ,(make-live-info) ,u (asm ,null-info ,asm-add ,u ,y))))
`(set! ,(make-live-info) ,z (asm ,info ,asm-add ,x ,u))))])
(define-instruction value (sext8 sext16 zext8 zext16)
[(op (z ur) (x mem ur)) `(set! ,(make-live-info) ,z (asm ,info ,(asm-move/extend op) ,x))])
;; load formats:
;; unsigned-16
;; lhzx rD <- [rA + rB]
;; lhz rD <- [rA + d]
;; integer-16
;; lhax rD <- [rA + rB]
;; lha rD <- [rA + d]
;; unsigned-8
;; lbzx rD <- [rA + rB]
;; lbz rD <- [rA + rB]
;; signed-8 (no instructions for, have to load unsigned-8 and sign-extend)
;; lwz rD <- [rA + d]
;; lwzx rD <- [rA + rB]
(let ()
(define imm-zero (with-output-language (L15d Triv) `(immediate 0)))
(define load/store
(lambda (info x y w k) ; x ur, y ur, w ur or imm
(safe-assert (not (eq? w %zero)))
(safe-assert (not (eq? x %zero)))
(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 ,y ,w))
(k x u imm-zero))))
(let ([n (nanopass-case (L15d Triv) w [(immediate ,imm) imm])])
(cond
[(fx= n 0) (k x y w)]
[(and (integer16? n) (not (info-load-swapped? info)))
(if (eq? y %zero)
(k x y w)
(let ([u (make-tmp 'u)])
(seq
`(set! ,(make-live-info) ,u (asm ,null-info ,asm-add ,x ,y))
(k u %zero w))))]
[(and (integer16? n) (not (eq? y %zero))) ; and swapped
(let ([u (make-tmp 'u)])
(seq
`(set! ,(make-live-info) ,u (asm ,null-info ,asm-add ,x ,w))
(k u y imm-zero)))]
[else (let ([u (make-tmp 'u)])
(seq
`(set! ,(make-live-info) ,u ,w)
(if (eq? y %zero)
(k x u imm-zero)
(seq
`(set! ,(make-live-info) ,u (asm ,null-info ,asm-add ,x ,u))
(k u y imm-zero)))))]))))))
(define-instruction value (load)
[(op (z ur) (x ur) (y ur) (w ur imm-constant))
(load/store info x y w
(lambda (x y w)
`(set! ,(make-live-info) ,z (asm ,null-info ,(asm-load info) ,x ,y ,w))))])
(define-instruction effect (store)
[(op (x ur) (y ur) (w ur imm-constant) (z ur))
(load/store info x y w
(lambda (x y w)
`(asm ,null-info ,(asm-store info) ,x ,y ,w ,z)))]))
(define-instruction effect (store-with-update)
[(op (x ur) (y ur) (z ur integer16))
`(asm ,info ,asm-store-with-update ,x ,y ,z)])
(define-instruction effect (load-single load-single->double load-double load-double->single
store-single store-single->double store-double)
[(op (x ur) (y ur) (z integer16 ur))
(if (eq? y %zero)
(if (ur? z)
`(asm ,info ,(asm-fl-load/store op (info-loadfl-flreg info)) ,x ,z (immediate 0))
`(asm ,info ,(asm-fl-load/store op (info-loadfl-flreg info)) ,x ,y ,z))
(if (and (not (ur? z)) (fx= (nanopass-case (L15d Triv) z [(immediate ,imm) imm]) 0))
`(asm ,info ,(asm-fl-load/store op (info-loadfl-flreg info)) ,x ,y ,z)
(let ([u (make-tmp 'u)])
(seq
`(set! ,(make-live-info) ,u (asm ,null-info ,asm-add ,y ,z))
`(asm ,info ,(asm-fl-load/store op (info-loadfl-flreg info)) ,x ,u (immediate 0))))))])
(define-instruction effect (flt)
[(op (x ur) (y ur))
(let ([u (make-tmp 'u)])
(seq
`(set! ,(make-live-info) ,u (asm ,null-info ,asm-kill))
`(asm ,info ,asm-flt ,x ,y ,u)))])
(define-instruction effect (fl+ fl- fl/ fl*)
[(op (x ur) (y ur) (z ur))
`(asm ,info ,(asm-flop-2 op) ,x ,y ,z)])
(define-instruction value (trunc)
[(op (z ur) (x ur))
`(set! ,(make-live-info) ,z (asm ,info ,asm-trunc ,x))])
(define-instruction pred (fl= fl< fl<=)
[(op (x ur) (y ur))
(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 shifted-integer16 integer16 ur) (z ur))
(let ([u1 (make-tmp 'u1)] [u2 (make-tmp 'u2)])
(seq
`(set! ,(make-live-info) ,u1 (asm ,null-info ,asm-add ,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 shifted-integer16 integer16))
(let ([u (make-tmp 'u)])
(seq
`(set! ,(make-live-info) ,u ,x)
`(set! ,(make-live-info) ,u (asm ,null-info ,asm-add ,u ,y))
`(set! ,(make-live-info) ,x ,u)))])
(define-instruction value (read-time-stamp-counter)
[(op (z ur))
(safe-assert (and (info-kill*? info) (memq %real-zero (info-kill*-kill* info))))
`(set! ,(make-live-info) ,z (asm ,info ,asm-read-time-base))])
;; always returns 0 on PPC
(define-instruction value (read-performance-monitoring-counter)
[(op (z ur) (x imm-constant ur)) `(set! ,(make-live-info) ,z (asm ,null-info ,asm-read-counter))])
;; 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)])
(seq
`(set! ,(make-live-info) ,u (asm ,null-info ,asm-kill))
`(set! ,(make-live-info) ,z (asm ,info ,asm-get-tc ,u))))])
;; like get-tc
(define-instruction value (activate-thread)
[(op (z ur))
(safe-assert (eq? z %Cretval))
(let ([u (make-tmp 'u)])
(seq
`(set! ,(make-live-info) ,u (asm ,null-info ,asm-kill))
`(set! ,(make-live-info) ,z (asm ,info ,asm-activate-thread ,u))))])
(define-instruction effect (deactivate-thread)
[(op)
(let ([u (make-tmp 'u)])
(seq
`(set! ,(make-live-info) ,u (asm ,null-info ,asm-kill))
`(asm ,info ,asm-deactivate-thread ,u)))])
(define-instruction effect (unactivate-thread)
[(op (z ur))
(safe-assert (eq? z %Carg1))
(let ([u (make-tmp 'u)])
(seq
`(set! ,(make-live-info) ,u (asm ,null-info ,asm-kill))
`(asm ,info ,asm-unactivate-thread ,u)))])
(define-instruction value (asmlibcall)
[(op (z ur))
(let ([u (make-tmp 'u)])
(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) ...))))])
(define-instruction effect (asmlibcall!)
[(op)
(let ([u (make-tmp 'u)])
(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) ...)))])
(safe-assert (reg-callee-save? %tc)) ; no need to save-restore
(define-instruction effect (c-simple-call)
[(op)
(let ([u (make-tmp 'u)])
(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)))])
(define-instruction pred (eq? < > <= >=)
[(op (y integer16) (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 integer16))
(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 (u<)
[(op (y unsigned16) (x ur))
(let ([info (make-info-condition-code op #t #t)])
(values '() `(asm ,info ,(asm-logrelop info) ,x ,y)))]
[(op (x ur) (y ur unsigned16))
(let ([info (make-info-condition-code op #f #t)])
(values '() `(asm ,info ,(asm-logrelop 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 shifted-unsigned16 unsigned16 ur) (type unsigned16 ur))
(let ([u (make-tmp 'u)])
(values
(with-output-language (L15d Effect)
`(set! ,(make-live-info) ,u (asm ,null-info ,asm-logand ,x ,mask)))
`(asm ,info-cc-eq ,(asm-logrelop info-cc-eq) ,u ,type)))])
(define-instruction pred (logtest log!test)
[(op (x shifted-unsigned16 unsigned16) (y ur))
(values '() `(asm ,info-cc-eq ,(asm-logtest (eq? op 'log!test) info-cc-eq) ,y ,x))]
[(op (x ur) (y ur integer16))
(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)
(let ([n (nanopass-case (L15d Triv) w [(immediate ,imm) imm])])
(with-output-language (L15d Effect)
(cond
[(eqv? n 0)
(if (eq? y %zero)
(k %real-zero x)
(k x y))]
[(or (shifted-integer16? n) (integer16? n))
(let ([u (make-tmp 'u)])
(seq
(if (eq? y %zero)
`(set! ,(make-live-info) ,u ,w)
`(set! ,(make-live-info) ,u (asm ,null-info ,asm-add ,y ,w)))
(k x u)))]
[else
(let ([u (make-tmp 'u)])
(seq
`(set! ,(make-live-info) ,u ,w)
`(set! ,(make-live-info) ,u (asm ,null-info ,asm-add ,y ,u))
(k x u)))])))))
;; compiler implements init-lock! and unlock! as 32-bit store of zero
(define-instruction pred (lock!)
[(op (x ur) (y ur) (w shifted-integer16 integer16))
(lea->reg x y w
(lambda (base index)
(values
'()
`(asm ,info-cc-eq ,(asm-lock info-cc-eq) ,base ,index))))])
(define-instruction effect (locked-incr! locked-decr!)
[(op (x ur) (y ur) (w shifted-integer16 integer16))
(lea->reg x y w
(lambda (base index)
(let ([u (make-tmp 'u)])
(seq
`(set! ,(make-live-info) ,u (asm ,null-info ,asm-kill))
`(asm ,null-info ,(asm-lock+/- op) ,base ,index ,u)))))])
(define-instruction effect (cas)
[(op (x ur) (y ur) (w shifted-integer16 integer16) (old ur) (new ur))
(lea->reg x y w
(lambda (base index)
(let ([u (make-tmp 'u)])
(seq
`(set! ,(make-live-info) ,u (asm ,null-info ,asm-kill))
`(asm ,info ,asm-cas ,base ,index ,old ,new ,u)))))]))
(define-instruction effect (pause)
[(op) `(asm ,info ,asm-isync)])
(define-instruction effect (c-call)
[(op (x ur))
`(asm ,info ,asm-indirect-call ,x ,(info-kill*-live*-live* 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)])
(define-instruction effect (save-lr)
[(op (z integer16))
(let ([n (nanopass-case (L15d Triv) z [(immediate ,imm) imm])])
(seq
`(set! ,(make-live-info) ,%real-zero (asm ,info ,(asm-get-lr)))
`(set! ,(make-live-info) (mref ,%Csp ,%zero ,n) ,%real-zero)))])
(define-instruction effect (restore-lr)
[(op (z integer16))
(let ([n (nanopass-case (L15d Triv) z [(immediate ,imm) imm])])
(seq
`(set! ,(make-live-info) ,%real-zero (mref ,%Csp ,%zero ,n))
`(asm ,info ,(asm-set-lr) ,%real-zero)))])
)
;;; SECTION 3: assembler
(module asm-module ( ; required exports
asm-move asm-move/extend asm-load asm-store asm-library-call asm-library-call! asm-library-jump
asm-div asm-mul asm-mul/ovfl asm-add asm-add/ovfl asm-sub-from asm-sub-from/ovfl
asm-add/carry asm-sub-from/eq
asm-logand asm-logor asm-logxor asm-sra asm-srl asm-sll
asm-logand asm-lognot
asm-logtest asm-fl-relop asm-relop asm-logrelop
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-trunc asm-flt
asm-lock asm-lock+/- asm-cas
asm-fl-load/store
asm-flop-2 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-read-time-base
asm-inc-cc-counter
asm-store-with-update
asm-get-lr asm-set-lr
unsigned16? shifted-unsigned16?
integer16? shifted-integer16?
negatable-integer16? negatable-shifted-integer16?
shift-count?
asm-isync
; threaded version specific
asm-get-tc asm-activate-thread asm-deactivate-thread asm-unactivate-thread
; machine dependent exports
asm-kill)
(define ax-register?
(case-lambda
[(x) (record-case x [(reg) r #t] [else #f])]
[(x reg) (record-case x [(reg) r (eq? r reg)] [else #f])]))
(define-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 ax-condition-helper
(lambda (code)
(define build-code
(lambda (code opnd-code)
(let ([opnd (case opnd-code
[(true) #b01100]
[(false) #b00100]
[(always) #b10100])]
[index (case code
[(lt) 0]
[(gt) 1]
[(eq) 2]
[(so) 3])])
;; always assuming cr = 0, if not calculation is (fx+ (fx* cr 4) index)
(fxlogor (fxsll opnd 5) index))))
(case code
[(al) (build-code 'lt 'always)]
[(lt) (build-code 'lt 'true)]
[(le) (build-code 'gt 'false)]
[(eq) (build-code 'eq 'true)]
[(ge) (build-code 'lt 'false)]
[(gt) (build-code 'gt 'true)]
[(nl) (build-code 'lt 'false)]
[(ne) (build-code 'eq 'false)]
[(ng) (build-code 'gt 'false)]
[(so) (build-code 'so 'true)]
[(ns) (build-code 'so 'false)])))
(define ax-spr-code
(lambda (spr)
(case spr
[(lr) #b0100000000]
[(ctr) #b0100100000]
[(xer) #b0000100000])))
; 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 ...))])))
(define-op add arithmetic-op #b100001010 #b0 #b0)
(define-op add. arithmetic-op #b100001010 #b1 #b0)
(define-op addo. arithmetic-op #b100001010 #b1 #b1)
(define-op addi reg-reg-simm-op #b001110)
(define-op addis reg-reg-simm-op #b001111)
(define-op divw arithmetic-op #b111101011 #b0 #b0)
(define-op mulli reg-reg-simm-op #b000111)
(define-op mullw arithmetic-op #b011101011 #b0 #b0)
(define-op mullwo. arithmetic-op #b011101011 #b1 #b1)
(define-op subf arithmetic-op #b000101000 #b0 #b0)
(define-op subf. arithmetic-op #b000101000 #b1 #b0)
(define-op subfo. arithmetic-op #b000101000 #b1 #b1)
(define-op and logical-op #b0000011100 #b0)
(define-op and. logical-op #b0000011100 #b1)
(define-op andi. reg-reg-uimm-op #b011100)
(define-op andis. reg-reg-uimm-op #b011101)
(define-op or logical-op #b0110111100 #b0)
(define-op nor logical-op #b0001111100 #b0)
(define-op ori reg-reg-uimm-op #b011000)
(define-op oris reg-reg-uimm-op #b011001)
(define-op xor logical-op #b0100111100 #b0)
(define-op xori reg-reg-uimm-op #b011010)
(define-op xoris reg-reg-uimm-op #b011011)
; Note: actually a reg-reg-uimm-op ori r0, r0,0, written this way to get the
; nop to show up in asm output.
(define-op nop nop-op)
(define-op rlwinm rotate-imm-op #b0)
(define-op slw logical-op #b0000011000 #b0)
(define-op sraw logical-op #b1100011000 #b0)
(define-op srawi logical-imm-op #b1100111000 #b0)
(define-op srw logical-op #b1000011000 #b0)
(define-op lbz reg-reg-simm-op #b100010)
(define-op lbzx indexed-op #b0001010111)
(define-op lha reg-reg-simm-op #b101010)
(define-op lhax indexed-op #b0101010111)
(define-op lhbrx indexed-op #b1100010110)
(define-op lhz reg-reg-simm-op #b101000)
(define-op lhzx indexed-op #b0100010111)
(define-op lwarx reserved-op #b010100 #b0)
(define-op lwbrx indexed-op #b1000010110)
(define-op lwz reg-reg-simm-op #b0000100000)
(define-op lwzx indexed-op #b0000010111)
(define-op lfd reg-reg-simm-op #b110010)
(define-op lfdx indexed-op #b1001010111)
(define-op lfs reg-reg-simm-op #b110000)
(define-op lfsx indexed-op #b1000010111)
(define-op stfd reg-reg-simm-op #b110110)
(define-op stfdu reg-reg-simm-op #b110111)
(define-op stfdx indexed-op #b1011010111)
(define-op stfs reg-reg-simm-op #b110100)
(define-op stfsx indexed-op #b1010010111)
(define-op stwcx. reserved-op #b10010110 #b1)
(define-op fctiwz flsingle-op #b001111 #b0)
(define-op frsp flsingle-op #b001100 #b0)
(define-op fadd flreg-op #b010101 #b0)
(define-op fdiv flreg-op #b010010 #b0)
(define-op fmul flmul-op #b0)
(define-op fsub flreg-op #b010100 #b0)
(define-op cror cror-op)
(define-op fcmpu compare-op #b111111 #b0000000000)
(define-op cmp compare-op #b011111 #b0000000000)
(define-op cmpl compare-op #b011111 #b0000100000)
(define-op cmpi compare-imm-op #b001011)
(define-op cmpli compare-imm-op #b001010)
(define-op stb reg-reg-simm-op #b100110)
(define-op stbx indexed-op #b0011010111)
(define-op sth reg-reg-simm-op #b101100)
(define-op sthbrx indexed-op #b1110010110)
(define-op sthx indexed-op #b0110010111)
(define-op stw reg-reg-simm-op #b100100)
(define-op stwbrx indexed-op #b1010010110)
(define-op stwu reg-reg-simm-op #b100101)
(define-op stwux indexed-op #b0010110111)
(define-op stwx indexed-op #b0010010111)
(define-op extsb ext-op #b1110111010 #b0)
(define-op extsh ext-op #b1110011010 #b0)
(define-op b unconditional-branch-op #b0 #b0)
(define-op blt conditional-branch-op #b0 #b0 (ax-condition-helper 'lt))
(define-op ble conditional-branch-op #b0 #b0 (ax-condition-helper 'le))
(define-op beq conditional-branch-op #b0 #b0 (ax-condition-helper 'eq))
(define-op bge conditional-branch-op #b0 #b0 (ax-condition-helper 'ge))
(define-op bgt conditional-branch-op #b0 #b0 (ax-condition-helper 'gt))
(define-op bnl conditional-branch-op #b0 #b0 (ax-condition-helper 'nl))
(define-op bne conditional-branch-op #b0 #b0 (ax-condition-helper 'ne))
(define-op bng conditional-branch-op #b0 #b0 (ax-condition-helper 'ng))
(define-op bso conditional-branch-op #b0 #b0 (ax-condition-helper 'so))
(define-op bns conditional-branch-op #b0 #b0 (ax-condition-helper 'ns))
(define-op bctr conditional-branch-to-spr-op #b1000010000 #b0 (ax-condition-helper 'al))
(define-op bctrl conditional-branch-to-spr-op #b1000010000 #b1 (ax-condition-helper 'al))
(define-op blr conditional-branch-to-spr-op #b0000010000 #b0 (ax-condition-helper 'al))
(define-op mfcr move-from-cr-op)
(define-op mflr move-from-special-reg-op #b0101010011 (ax-spr-code 'lr))
(define-op mftb move-from-special-reg-op #b0101110011 #b0110001000)
(define-op mftbu move-from-special-reg-op #b0101110011 #b0110101000)
(define-op mtcrf move-to-cr-op)
(define-op mtlr move-to-special-reg-op (ax-spr-code 'lr))
(define-op mtctr move-to-special-reg-op (ax-spr-code 'ctr))
(define-op mtxer move-to-special-reg-op (ax-spr-code 'xer))
(define-op isync isync-op)
(define arithmetic-op
(lambda (op opcode set-cr? set-oe? dest-ea opnd0-ea opnd1-ea code*)
(emit-code (op dest-ea opnd0-ea opnd1-ea code*)
[26 #b011111]
[21 (ax-ea-reg-code dest-ea)]
[16 (ax-ea-reg-code opnd0-ea)]
[11 (ax-ea-reg-code opnd1-ea)]
[10 set-oe?]
[1 opcode]
[0 set-cr?])))
(define reg-reg-simm-op
(lambda (op opcode dest-ea opnd0-ea imm code*)
(emit-code (op dest-ea opnd0-ea imm code*)
[26 opcode]
[21 (ax-ea-reg-code dest-ea)]
[16 (ax-ea-reg-code opnd0-ea)]
[0 (fxlogand (ax-imm-data imm) #xFFFF)])))
(define reg-reg-uimm-op
(lambda (op opcode dest-ea opnd0-ea imm code*)
(emit-code (op dest-ea opnd0-ea imm code*)
[26 opcode]
[21 (ax-ea-reg-code opnd0-ea)]
[16 (ax-ea-reg-code dest-ea)]
[0 (fxlogand (ax-imm-data imm) #xFFFF)])))
;; same as reg-reg-uimm-op, if we fixed the operation to ori and provided
;; r0, r0, 0 as the operands
(define nop-op
(lambda (op code*)
(emit-code (op code*)
[26 #b011000]
[21 #b00000]
[16 #b00000]
[0 #b00000])))
(define logical-op
(lambda (op opcode set-cr? dest-ea opnd0-ea opnd1-ea code*)
(emit-code (op dest-ea opnd0-ea opnd1-ea code*)
[26 #b011111]
[21 (ax-ea-reg-code opnd0-ea)]
[16 (ax-ea-reg-code dest-ea)]
[11 (ax-ea-reg-code opnd1-ea)]
[1 opcode]
[0 set-cr?])))
(define logical-imm-op
(lambda (op opcode set-cr? dest-ea opnd0-ea imm code*)
(emit-code (op dest-ea opnd0-ea imm code*)
[26 #b011111]
[21 (ax-ea-reg-code opnd0-ea)]
[16 (ax-ea-reg-code dest-ea)]
[11 (fxlogand (ax-imm-data imm) #xFFFF)]
[1 opcode]
[0 set-cr?])))
(define indexed-op
(lambda (op opcode dest-ea opnd0-ea opnd1-ea code*)
(emit-code (op dest-ea opnd0-ea opnd1-ea code*)
[26 #b011111]
[21 (ax-ea-reg-code dest-ea)]
[16 (ax-ea-reg-code opnd0-ea)]
[11 (ax-ea-reg-code opnd1-ea)]
[1 opcode]
[0 #b0])))
(define ext-op
(lambda (op opcode set-cr? dest-ea opnd0-ea code*)
(emit-code (op dest-ea opnd0-ea code*)
[26 #b011111]
[21 (ax-ea-reg-code opnd0-ea)]
[16 (ax-ea-reg-code dest-ea)]
[11 #b00000]
[1 opcode]
[0 set-cr?])))
(define rotate-imm-op
(lambda (op set-cr? dest-ea opnd0-ea imm-shift imm-mb imm-me code*)
;; imm-shift, imm-mb, and imm-me checked to be between 0 <= imm-{shift,mb,me} <= 31 before calling rotate-imm-op
(emit-code (op dest-ea opnd0-ea imm-shift imm-mb imm-me code*)
[26 #b010101]
[21 (ax-ea-reg-code opnd0-ea)]
[16 (ax-ea-reg-code dest-ea)]
[11 (ax-imm-data imm-shift)]
[6 (ax-imm-data imm-mb)]
[1 (ax-imm-data imm-me)]
[0 set-cr?])))
(define flsingle-op
(lambda (op opcode set-cr? dest-ea opnd0-ea code*)
(emit-code (op dest-ea opnd0-ea code*)
[26 #b111111]
[21 (ax-ea-reg-code dest-ea)]
[11 (ax-ea-reg-code opnd0-ea)]
[1 opcode]
[0 set-cr?])))
(define flreg-op
(lambda (op opcode set-cr? dest-ea opnd0-ea opnd1-ea code*)
(emit-code (op dest-ea opnd0-ea opnd1-ea code*)
[26 #b111111]
[21 (ax-ea-reg-code dest-ea)]
[16 (ax-ea-reg-code opnd0-ea)]
[11 (ax-ea-reg-code opnd1-ea)]
[6 #b00000]
[1 opcode]
[0 set-cr?])))
(define flmul-op
(lambda (op set-cr? dest-ea opnd0-ea opnd1-ea code*)
(emit-code (op dest-ea opnd0-ea opnd1-ea code*)
[26 #b111111]
[21 (ax-ea-reg-code dest-ea)]
[16 (ax-ea-reg-code opnd0-ea)]
[11 #b00000]
[6 (ax-ea-reg-code opnd1-ea)]
[1 #b011001]
[0 set-cr?])))
(define compare-op
(lambda (op opcode0 opcode1 opnd0-ea opnd1-ea code*)
(emit-code (op opnd0-ea opnd1-ea code*)
[26 opcode0]
[23 #b000] ; crfD
[22 #b0]
[21 #b0] ; L-bit (long?), must be 0 on 32-bit
[16 (ax-ea-reg-code opnd0-ea)]
[11 (ax-ea-reg-code opnd1-ea)]
[1 opcode1]
[0 #b0])))
(define compare-imm-op
(lambda (op opcode opnd-ea imm code*)
(emit-code (op opnd-ea imm code*)
[26 opcode]
[23 #b000] ; crfD
[22 #b0]
[21 #b0] ; L bit
[16 (ax-ea-reg-code opnd-ea)]
[0 (fxlogand (ax-imm-data imm) #xFFFF)])))
(define reserved-op
(lambda (op opcode1 opcode2 dest-ea opnd0-ea opnd1-ea code*)
(emit-code (op dest-ea opnd0-ea opnd1-ea code*)
[26 #b011111]
[21 (ax-ea-reg-code dest-ea)]
[16 (ax-ea-reg-code opnd0-ea)]
[11 (ax-ea-reg-code opnd1-ea)]
[1 opcode1]
[0 opcode2])))
(define-who conditional-branch-op
(lambda (op absolute-address link condition-bits branch-dest code*)
(emit-code (op branch-dest code*)
[26 #b010000]
[16 condition-bits]
[2 (if (pair? branch-dest)
(record-case branch-dest
[(label) (offset l)
(fxlogand (fxsrl (fx+ offset 4) 2) #x3FFF)]
[else (sorry! who "unexpected dest ~s" branch-dest)])
(fxlogand branch-dest #x3FFF))]
[1 absolute-address]
[0 link])))
(define conditional-branch-to-spr-op
(lambda (op opcode link condition-bits code*)
(emit-code (op code*)
[26 #b010011]
[16 condition-bits]
[11 #b00000]
[1 opcode]
[0 link])))
(define-who unconditional-branch-op
(lambda (op absolute-address link dest code*)
(record-case dest
[(label) (offset l)
(emit-code (op dest code*)
[26 #b010010]
[2 (fxlogand (fxsrl (fx+ offset 4) 2) #xFFFFFF)]
[1 absolute-address]
[0 link])]
[else (sorry! who "unexpected dest ~s" dest)])))
(define move-from-cr-op
(lambda (op dest-ea code*)
(emit-code (op dest-ea code*)
[26 #b011111]
[21 (ax-ea-reg-code dest-ea)]
[16 #b00000]
[11 #b00000]
[1 #b0000010011]
[0 #b0])))
(define move-from-special-reg-op
(lambda (op opcode spr dest-ea code*)
(emit-code (op dest-ea code*)
[26 #b011111]
[21 (ax-ea-reg-code dest-ea)]
[11 spr]
[1 opcode]
[0 #b0])))
(define move-to-cr-op
(lambda (op mask opnd-ea code*)
(emit-code (op mask opnd-ea code*)
[26 #b011111]
[21 (ax-ea-reg-code opnd-ea)]
[20 #b0]
[12 mask]
[11 #b0]
[1 #b0010010000]
[0 #b0])))
(define move-to-special-reg-op
(lambda (op spr opnd-ea code*)
(emit-code (op opnd-ea code*)
[26 #b011111]
[21 (ax-ea-reg-code opnd-ea)]
[11 spr]
[1 #b0111010011]
[0 #b0])))
(define cror-op
(lambda (op dest-fld opnd0-fld opnd1-fld code*)
(emit-code (op dest-fld opnd0-fld opnd1-fld code*)
[26 #b010011]
[21 dest-fld]
[16 opnd0-fld]
[11 opnd1-fld]
[1 #b0111000001]
[0 #b0])))
(define isync-op
(lambda (op code*)
(emit-code (op code*)
[26 #b010011]
[21 #b00000]
[16 #b00000]
[11 #b00000]
[1 #b0010010110]
[0 #b0])))
(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 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
(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 unsigned16?
(lambda (imm)
(and (fixnum? imm) ($fxu< imm (expt 2 16)))))
(define shifted-unsigned16?
(lambda (imm)
(and (<= (- (expt 2 31)) imm (- (expt 2 31) 1))
(not (logtest imm #xFFFF)))))
(define integer16?
(lambda (imm)
(and (fixnum? imm) (fx< (fx- (expt 2 15)) imm (fx- (expt 2 15) 1)))))
(define shifted-integer16?
(lambda (imm)
(and (<= (- (expt 2 31)) imm (- (expt 2 31) 1))
(not (logtest imm #xFFFF)))))
(define negatable-integer16?
(lambda (imm)
(and (fixnum? imm) (fx<= (fx- 1 (expt 2 15)) imm (expt 2 15)))))
(define negatable-shifted-integer16?
(lambda (imm)
(and (<= (- 1 (expt 2 31)) imm (expt 2 31))
(not (logtest imm #xFFFF)))))
(define shift-count?
(lambda (imm)
(and (fixnum? imm) ($fxu< imm (expt 2 5)))))
(define branch-disp?
(lambda (x)
(and (fixnum? x)
(fx<= (- (expt 2 25)) x (- (expt 2 25) 1))
(not (fxlogtest x #b11)))))
(define conditional-branch-disp?
(lambda (x)
(let ([x (+ x 4)])
(and (fixnum? x)
(fx<= (- (expt 2 15)) x (- (expt 2 15) 1))
(not (fxlogtest x #b11))))))
(define asm-size
(lambda (x)
(case (car x)
[(asm ppc32-abs ppc32-jump ppc32-call) 0]
[else 4])))
(define ax-mov32
(lambda (dest n code*)
(let* ([n (if (< n 0) (+ n (expt 2 32)) n)] ;; signed -> unsigned conversion for 32-bit value
[high (ash n -16)] ;; shift high bits into lower 16
[low (- n (ash high 16))] ;; subtract upper 16 bits off x
[high (cond
[(fx< low #x8000) high] ;; if high bit of low is set, use high
[(fx< high #xFFFF) (fx+ high 1)] ;; else, if high is less than (- (expt 2 16) 1), add 1 to adjust for signed-ness of lower addi
[else 0])]) ;; otherwise high is (- (expt 2 16) 1), we still need to add 1, so we wrap to 0, giving the right final result.
(emit addis dest `(reg . ,%real-zero) `(imm ,high)
(emit addi dest dest `(imm ,low) code*)))))
(define-who ax-move-literal
(lambda (dest src code*)
(record-case src
[(literal) stuff
(ax-mov32 dest 0
(asm-helper-relocation (cons 'ppc32-abs stuff) code*))]
[else (sorry! who "unexpected source ~s" src)])))
(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 or dest src src code*)]
[(imm) (n)
(cond
[(integer16? n) (emit addi dest `(reg . ,%real-zero) `(imm ,n) code*)]
[(shifted-integer16? n)
(emit addis dest `(reg . ,%real-zero)
`(imm ,(bitwise-arithmetic-shift-right n 16))
code*)]
[else (ax-mov32 dest n code*)])]
[(literal) stuff (ax-move-literal dest src code*)]
[(disp) (n breg)
(safe-assert (integer16? n))
(emit lwz dest `(reg . ,breg) `(imm ,n) code*)]
[(index) (n ireg breg)
(safe-assert (eqv? n 0))
(emit lwzx dest `(reg . ,breg) `(reg . ,ireg) code*)]
[else (bad!)])]
[(ax-reg? src)
(record-case dest
[(disp) (n breg)
(safe-assert (or (unsigned16? n) (unsigned16? (- n))))
(emit stw src `(reg . ,breg) `(imm ,n) code*)]
[(index) (n ireg breg)
(safe-assert (eqv? n 0))
(emit stwx src `(reg . ,breg) `(reg . ,ireg) code*)]
[else (bad!)])]
[else (bad!)]))))
(define-who asm-move/extend
(lambda (op)
(lambda (code* dest src)
(Trivit (dest src)
(record-case src
[(reg) ignore
(case op
[(sext8) (emit extsb dest src code*)]
[(sext16) (emit extsh dest src code*)]
[(zext8) (emit andi. dest src `(imm #xff) code*)]
[(zext16) (emit andi. dest src `(imm #xffff) code*)]
[else (sorry! who "unexpected op ~s" op)])]
[(disp) (n breg)
(safe-assert (integer16? n))
(case op
[(sext8) (emit lbz dest breg `(imm ,n)
(emit extsb dest dest code*))]
[(sext16) (emit lha dest breg `(imm ,n) code*)]
[(zext8) (emit lbz dest breg `(imm ,n) code*)]
[(zext16) (emit lhz dest breg `(imm ,n) code*)]
[else (sorry! who "unexpected op ~s" op)])]
[(index) (n ireg breg)
(safe-assert (eqv? n 0))
(case op
[(sext8) (emit lbzx dest breg ireg
(emit extsb dest dest code*))]
[(sext16) (emit lhax dest breg ireg code*)]
[(zext8) (emit lbzx dest breg ireg code*)]
[(zext16) (emit lhzx dest breg ireg code*)]
[else (sorry! who "unexpected op ~s" op)])]
[else (sorry! who "unexpected src ~s" src)])))))
(define asm-add
(lambda (code* dest src0 src1)
(Trivit (dest src0 src1)
(record-case src1
[(imm) (n)
(if (shifted-integer16? n)
(emit addis dest src0
`(imm ,(bitwise-arithmetic-shift-right n 16))
code*)
(emit addi dest src0 `(imm ,n) code*))]
[else (emit add dest src0 src1 code*)]))))
(define asm-add/ovfl
(lambda (code* dest src0 src1)
(Trivit (dest src0 src1)
(let ([zed `(reg . ,%real-zero)])
(emit addi zed zed `(imm 0)
(emit mtxer zed
(emit addo. dest src0 src1 code*)))))))
(define asm-add/carry
(lambda (code* dest src0 src1)
(Trivit (dest src0 src1)
(emit add. dest src0 src1 code*))))
(define asm-sub-from
(lambda (code* dest src0 src1)
(Trivit (dest src0 src1)
(emit subf dest src0 src1 code*))))
(define asm-sub-from/ovfl
(lambda (code* dest src0 src1)
(Trivit (dest src0 src1)
(let ([zed `(reg . ,%real-zero)])
(emit addi zed zed `(imm 0)
(emit mtxer zed
(emit subfo. dest src0 src1 code*)))))))
(define asm-sub-from/eq
(lambda (code* dest src0 src1)
(Trivit (dest src0 src1)
(emit subf. dest src0 src1 code*))))
(module (asm-logand asm-logor asm-logxor)
(define-syntax asm-logicalop
(syntax-rules ()
[(_ opi opis op)
(lambda (code* dest src0 src1)
(Trivit (dest src0 src1)
(record-case src1
[(imm) (n)
(if (unsigned16? n)
(emit opi dest src0 `(imm ,n) code*)
(begin
(safe-assert (shifted-unsigned16? n))
(emit opis dest src0 `(imm ,(ash n -16)) code*)))]
[else (emit op dest src0 src1 code*)])))]))
(define asm-logand (asm-logicalop andi. andis. and))
(define asm-logor (asm-logicalop ori oris or))
(define asm-logxor (asm-logicalop xori xoris xor)))
(define asm-sra
(lambda (code* dest src0 src1)
(Trivit (dest src0 src1)
(record-case src1
[(imm) (n) (emit srawi dest src0 `(imm ,n) code*)]
[else (emit sraw dest src0 src1 code*)]))))
(define asm-srl
(lambda (code* dest src0 src1)
(Trivit (dest src0 src1)
(record-case src1
[(imm) (n) (emit rlwinm dest src0 `(imm ,(fx- 32 n)) `(imm ,n) `(imm 31) code*)]
[else (emit srw dest src0 src1 code*)]))))
(define asm-sll
(lambda (code* dest src0 src1)
(Trivit (dest src0 src1)
(record-case src1
[(imm) (n) (emit rlwinm dest src0 `(imm ,n) `(imm 0) `(imm ,(fx- 31 n)) code*)]
[else (emit slw dest src0 src1 code*)]))))
(define asm-mul
(lambda (code* dest src0 src1)
(Trivit (dest src0 src1)
(record-case src1
[(imm) (n) (emit mulli dest src0 `(imm ,n) code*)]
[else (emit mullw dest src0 src1 code*)]))))
(define asm-mul/ovfl
(lambda (code* dest src0 src1)
(Trivit (dest src0 src1)
(let ([zed `(reg . ,%real-zero)])
(emit addi zed zed `(imm 0)
(emit mtxer zed
(emit mullwo. dest src0 src1 code*)))))))
(define asm-div
(lambda (code* dest src0 src1)
(Trivit (dest src0 src1)
(emit divw dest src0 src1 code*))))
(define-who asm-load
(lambda (info)
(lambda (code* dest base index offset)
(let ([type (info-load-type info)] [swapped? (info-load-swapped? info)])
(let ([n (nanopass-case (L16 Triv) offset
[(immediate ,imm) imm]
[else (sorry! who "unexpected non-immediate offset ~s" offset)])])
(Trivit (dest base)
(cond
[(eqv? n 0)
(let ([index (if (eq? index %zero) %real-zero index)])
(Trivit (index)
(case type
[(integer-32 unsigned-32)
(if swapped?
(emit lwbrx dest index base code*)
(emit lwzx dest index base code*))]
[(integer-16)
(if swapped?
(emit lhbrx dest index base
(emit extsh dest dest code*))
(emit lhax dest index base code*))]
[(unsigned-16)
(if swapped?
(emit lhbrx dest index base code*)
(emit lhzx dest index base code*))]
[(integer-8) (emit lbzx dest index base
(emit extsb dest dest code*))]
[(unsigned-8) (emit lbzx dest index base code*)]
[else (sorry! who "unexpected mref type ~s" type)])))]
[(eq? index %zero)
(case type
[(integer-32 unsigned-32) (emit lwz dest base `(imm ,n) code*)]
[(integer-16) (emit lha dest base `(imm ,n) code*)]
[(unsigned-16) (emit lhz dest base `(imm ,n) code*)]
[(integer-8) (emit lbz dest base `(imm ,n)
(emit extsb dest dest code*))]
[(unsigned-8) (emit lbz dest base `(imm ,n) code*)]
[else (sorry! who "unexpected mref type ~s" type)])]
[else (sorry! who "expected %zero base or 0 offset, got ~s and ~s" base offset)])))))))
(define-who asm-store
(lambda (info)
(lambda (code* base index offset src)
(let ([type (info-load-type info)] [swapped? (info-load-swapped? info)])
(let ([n (nanopass-case (L16 Triv) offset
[(immediate ,imm) imm]
[else (sorry! who "unexpected non-immediate offset ~s" offset)])])
(Trivit (src base)
(cond
[(eqv? n 0)
(let ([index (if (eq? index %zero) %real-zero index)])
(Trivit (index)
(case type
[(integer-32 unsigned-32)
(if swapped?
(emit stwbrx src index base code*)
(emit stwx src index base code*))]
[(integer-16 unsigned-16)
(if swapped?
(emit sthbrx src index base code*)
(emit sthx src index base code*))]
[(integer-8 unsigned-8) (emit stbx src index base code*)]
[else (sorry! who "unexpected mref type ~s" type)])))]
[(eq? index %zero)
(case type
[(integer-32 unsigned-32) (emit stw src base `(imm ,n) code*)]
[(integer-16 unsigned-16) (emit sth src base `(imm ,n) code*)]
[(integer-8 unsigned-8) (emit stb src base `(imm ,n) code*)]
[else (sorry! who "unexpected mref type ~s" type)])]
[else (sorry! who "expected %zero base or 0 offset, got ~s and ~s" base offset)])))))))
;; load single->double
;; lfs frD <- [rA + d]
;; lfsx frD <- [rA + rB]
;; load double
;; lfd frD <- [rA + d]
;; lfdx frD <- [rA + rB]
;; store double
;; stfd [rA + d] <- frS
;; stfdx [rA + rB] <- frS
;; store double->single
;; stfs [rA + d] <- frS
;; stfsx [rA + rB] <- frS
(define asm-fl-load/store
(lambda (op flreg)
(lambda (code* base index offset)
(Trivit (flreg base)
(define-syntax finish
(syntax-rules ()
[(_ op opx code*)
(if (eq? index %zero)
(Trivit (offset)
(emit op flreg base offset code*))
(Trivit (index)
(emit opx flreg base index code*)))]))
(case op
[(load-single load-single->double) (finish lfs lfsx code*)]
[(load-double) (finish lfd lfdx code*)]
[(load-double->single)
(finish lfd lfdx (emit frsp flreg flreg code*))]
[(store-single) (finish stfs stfsx code*)]
[(store-double) (finish stfd stfdx code*)]
[(store-single->double)
(emit frsp flreg flreg
(finish stfd stfdx code*))])))))
(define-who asm-flop-2
(lambda (op)
(lambda (code* src1 src2 dest)
(let ([flreg1 `(reg . ,%flreg1)] [flreg2 `(reg . ,%flreg2)])
(Trivit (src1 src2 dest)
(emit lfd flreg1 src1 `(imm ,(constant flonum-data-disp))
(emit lfd flreg2 src2 `(imm ,(constant flonum-data-disp))
(let ([code* (emit stfd flreg1 dest `(imm ,(constant flonum-data-disp)) code*)])
(case op
[(fl+) (emit fadd flreg1 flreg1 flreg2 code*)]
[(fl-) (emit fsub flreg1 flreg1 flreg2 code*)]
[(fl*) (emit fmul flreg1 flreg1 flreg2 code*)]
[(fl/) (emit fdiv flreg1 flreg1 flreg2 code*)]
[else (sorry! who "unrecognized op ~s" op)])))))))))
(define asm-trunc
(lambda (code* dest src)
(let ([flreg1 `(reg . ,%flreg1)] [Csp `(reg . ,%Csp)])
(Trivit (dest src)
(emit lfd flreg1 src `(imm ,(constant flonum-data-disp))
(emit fctiwz flreg1 flreg1
(emit stfd flreg1 Csp `(imm -8)
(emit lwz dest Csp `(imm -4) code*))))))))
(define asm-flt
(lambda (code* src dest tmp)
(Trivit (src dest tmp)
(let ([flreg1 `(reg . ,%flreg1)]
[flreg2 `(reg . ,%flreg2)]
[flodat-disp `(imm ,(constant flonum-data-disp))])
(emit xoris tmp src `(imm #x8000)
(emit stw tmp dest `(imm ,(+ (constant flonum-data-disp) 4))
(emit addis tmp `(reg . ,%real-zero) `(imm #x4330)
(emit stw tmp dest flodat-disp
(emit lfd flreg1 dest flodat-disp
(ax-move-literal tmp `(literal 0 (object 4503601774854144.0))
(emit lfd flreg2 tmp flodat-disp
(emit fsub flreg1 flreg1 flreg2
(emit stfd flreg1 dest flodat-disp
code*)))))))))))))
(define asm-lock
(lambda (info)
; r0 = lwarx [base, index]
; cmpi r0, 0
; bc (ne) L1 (+3)
; r0 = 1
; strex r0, [base, index]
;L1:
(lambda (l1 l2 offset base index)
(values
(Trivit (base index)
(let ([zed `(reg . ,%real-zero)])
(emit lwarx zed base index
(emit cmpi zed `(imm 0)
(emit bne 3 ;; jumping past 3 instructions: bne, ori, and stwcx.
(emit addi zed zed `(imm 1)
(emit stwcx. zed base index '())))))))
(asm-conditional-jump info l1 l2 offset)))))
(define-who asm-lock+/-
; L:
; tmp = lwarx [base,index]
; tmp = tmp +/- 1
; stwcx. tmp [base,index] -- sets condition code
; bc (ne) L (-3)
; cmpi tmp, 0
(lambda (op)
(lambda (code* base index tmp)
(let ([inc `(imm ,(case op
[(locked-incr!) 1]
[(locked-decr!) -1]
[else (sorry! who "unexpected op ~s" op)]))])
(assert (not (eq? tmp %real-zero)))
(Trivit (base index tmp)
(emit lwarx tmp base index
(emit addi tmp tmp inc
(emit stwcx. tmp base index
;; jumping back to the lwarx
(emit bne -3
(emit cmpi tmp `(imm 0) code*))))))))))
(define-who asm-cas
; tmp = lwarx [base,index]
; cmp tmp, old
; bc (ne) L 2
; stwcx. new [base,index] -- also sets condition code
; L:
(lambda (code* base index old new tmp)
(assert (not (eq? tmp %real-zero)))
(Trivit (base index old new tmp)
(emit lwarx tmp base index
(emit cmpl tmp old
(emit bne 2
(emit stwcx. new base index
code*)))))))
(define asm-fl-relop
(lambda (info)
(lambda (l1 l2 offset x y)
(let ([flreg1 `(reg . ,%flreg1)] [flreg2 `(reg . ,%flreg2)])
(Trivit (x y)
(values
(emit lfd flreg1 x `(imm ,(constant flonum-data-disp))
(emit lfd flreg2 y `(imm ,(constant flonum-data-disp))
(emit fcmpu flreg1 flreg2
(if (eq? (info-condition-code-type info) 'fl<=)
(emit cror 1 1 3 '())
'()))))
(asm-conditional-jump info l1 l2 offset)))))))
(module (asm-relop asm-logrelop)
(define-syntax define-asm-relop
(syntax-rules ()
[(_ name opi op)
(define name
(lambda (info)
(lambda (l1 l2 offset x y)
(Trivit (x y)
(safe-assert (ax-reg? x))
(values
(record-case y
[(imm) (n) (emit opi x `(imm ,n) '())]
[(reg) ignore (emit op x y '())]
[else (sorry! 'name "unexpected second operand ~s" y)])
(asm-conditional-jump info l1 l2 offset))))))]))
(define-asm-relop asm-relop cmpi cmp)
(define-asm-relop asm-logrelop cmpli cmpl))
;; ASM INSTRUCTIONS DONE ABOVE HERE
(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-save-flrv
(lambda (code*)
; could instead stash flrv either in callee-save fl reg (one that we preserve ourselves in invoke-prelude) or in thread context
(let ([Csp `(reg . ,%Csp)])
(emit stfdu `(reg . ,%Cfpretval) Csp `(imm -8)
(emit stwu Csp Csp `(imm -8) code*)))))
(define asm-restore-flrv
(lambda (code*)
(let ([Csp `(reg . ,%Csp)])
(emit lfd `(reg . ,%Cfpretval) Csp `(imm 8)
(emit addi Csp Csp `(imm 16) code*)))))
(define asm-read-time-base
(lambda (code* dest)
(Trivit (dest)
; NB: not atomic => value will be way off on average once every 4 billion times, but we
; NB: don't care since consumers have to deal with incorrect values for other reasons.
(emit mftbu dest
(emit mftb `(reg . ,%real-zero) code*)))))
(define asm-read-counter
(lambda (code* dest)
(Trivit (dest)
;; return zero
(emit ori dest `(reg . ,%real-zero) `(imm 0) code*))))
(define asm-library-jump
(lambda (l)
(asm-helper-jump '()
`(ppc32-jump ,(constant code-data-disp) (library-code ,(libspec-label-libspec l))))))
(define asm-library-call
(lambda (libspec save-ra?)
(let ([target `(ppc32-call ,(constant code-data-disp) (library-code ,libspec))])
(rec asm-asm-call-internal
(lambda (code* dest tmp . ignore) ; ignore arguments, which must be in fixed locations
(asm-helper-call code* target save-ra? tmp))))))
(define asm-library-call!
(lambda (libspec save-ra?)
(let ([target `(ppc32-call ,(constant code-data-disp) (library-code ,libspec))])
(rec asm-asm-call-internal
(lambda (code* tmp . ignore) ; ignore arguments, which must be in fixed locations
(asm-helper-call code* target save-ra? tmp))))))
(define asm-c-simple-call
(lambda (entry save-ra?)
(let ([target `(ppc32-call 0 (entry ,entry))])
(rec asm-c-simple-call-internal
(lambda (code* tmp . ignore)
(asm-helper-call code* target save-ra? tmp))))))
(define-who asm-indirect-call
(lambda (code* dest . ignore)
(Trivit (dest)
(unless (ax-reg? dest) (sorry! who "unexpected dest ~s" dest))
(emit mtctr dest
(emit bctrl code*)))))
(define asm-direct-jump
(lambda (l offset)
(asm-helper-jump '() (make-funcrel 'ppc32-jump l offset))))
(define asm-literal-jump
(lambda (info)
(asm-helper-jump '()
`(ppc32-jump ,(info-literal-offset info) (,(info-literal-type info) ,(info-literal-addr info))))))
;; NB: cleanup asm-indirect-jump call in cpnanopass so that a real tmp is
;; NB: assigned to this when we are jumping to a mref.
;; NB: (currently using %real-zero for a temporary)
(define-who asm-indirect-jump
(lambda (src)
(let ([real-zero-reg `(reg . ,%real-zero)])
(Trivit (src)
(record-case src
[(reg) ignore
(emit mtctr src
(emit bctr '()))]
[(disp) (n breg)
(safe-assert (integer16? n))
(emit lwz real-zero-reg `(reg . ,breg) `(imm ,n)
(emit mtctr real-zero-reg
(emit bctr '())))]
[(index) (n ireg breg)
(safe-assert (eqv? n 0))
(emit lwzx real-zero-reg `(reg . ,breg) `(reg . ,ireg)
(emit mtctr real-zero-reg
(emit bctr '())))]
[else (sorry! who "unexpected src ~s" src)])))))
;; NB: kills real-zero, since it is used as a temporary here
(define asm-logtest
(lambda (i? info)
(lambda (l1 l2 offset x y)
(Trivit (x y)
(values
(record-case y
[(imm) (n)
(if (shifted-unsigned16? n)
(emit andis. `(reg . ,%real-zero) x
`(imm ,(bitwise-arithmetic-shift-right n 16)) '())
(emit andi. `(reg . ,%real-zero) x `(imm ,n) '()))]
[else (emit and. `(reg . ,%real-zero) 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 `(ppc32-call 0 (entry ,(lookup-c-entry get-thread-context)))])
(lambda (code* dest tmp . ignore) ; dest is ignored, since it is always Cretval
(asm-helper-call code* target #f tmp))))
(define asm-activate-thread
(let ([target `(ppc32-call 0 (entry ,(lookup-c-entry activate-thread)))])
(lambda (code* dest tmp . ignore) ; dest is ignored, since it is always Cretval
(asm-helper-call code* target #f tmp))))
(define asm-deactivate-thread
(let ([target `(ppc32-call 0 (entry ,(lookup-c-entry deactivate-thread)))])
(lambda (code* tmp . ignore)
(asm-helper-call code* target #f tmp))))
(define asm-unactivate-thread
(let ([target `(ppc32-call 0 (entry ,(lookup-c-entry unactivate-thread)))])
(lambda (code* tmp . ignore)
(asm-helper-call code* target #f tmp))))
(define-who asm-return-address
(lambda (dest l incr-offset next-addr)
(make-rachunk dest l incr-offset next-addr
(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 b `(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 b `(label 0 ,l) '())]))))
(define-who asm-conditional-jump
(lambda (info l1 l2 next-addr)
(define get-disp
(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"))
disp))]
[else 0])
(sorry! who "unexpected label ~s" l))))
(define-syntax define-pred-emitter
(lambda (x)
(define build-emit
(lambda (op)
(with-syntax ([op op])
#'(emit op disp/opnd code*))))
(define process-r
(lambda (r*)
(if (null? r*)
'()
(syntax-case (car r*) (r?)
[(r? op1 op2)
(with-syntax ([op1 (build-emit #'op1)] [op2 (build-emit #'op2)])
(cons #'(if r? op1 op2) (process-r (cdr r*))))]
[op (identifier? #'op) (cons (build-emit #'op) (process-r (cdr r*)))]))))
(syntax-case x (i?)
[(_ name [(ops ...) (i? r1 r2)] ...)
(with-syntax ([(r1 ...) (process-r #'(r1 ...))]
[(r2 ...) (process-r #'(r2 ...))])
#'(define name
(lambda (op i? r? disp/opnd code*)
(case op
[(ops ...) (if i? r1 r2)] ...))))])))
(define-pred-emitter emit-branch
[(fl= eq?) (i? bne beq)]
[(fl< < u<) (i? (r? ble bge) (r? bgt blt))]
[(fl<= <=) (i? (r? blt bgt) (r? bge ble))]
[(>) (i? (r? bge ble) (r? blt bgt))]
[(>=) (i? (r? bgt blt) (r? ble bge))]
[(carry multiply-overflow overflow) (i? bns bso)])
(let ([type (info-condition-code-type info)]
[reversed? (info-condition-code-reversed? info)])
(make-cgchunk info l1 l2 next-addr
(let ([disp1 (get-disp next-addr l1)] [disp2 (get-disp next-addr l2)])
(cond
;; 1 conditional jump
[(and (fx= disp1 0) (conditional-branch-disp? disp2))
(emit-branch type #t reversed? `(label ,disp2 ,l2) '())]
[(and (fx= disp2 0) (conditional-branch-disp? disp1))
(emit-branch type #f reversed? `(label ,disp1 ,l1) '())]
;; 1 conditional jump, 1 unconditional jump
[(conditional-branch-disp? (fx+ disp2 4))
(emit-branch type #t reversed? `(label ,(fx+ disp2 4) ,l2)
(emit b `(label ,disp1 ,l1) '()))]
[(conditional-branch-disp? (fx+ disp1 4))
(emit-branch type #f reversed? `(label ,(fx+ disp1 4) ,l1)
(emit b `(label ,disp2 ,l2) '()))]
;; jmp<condition> L1
;; jmp dest1
;; L1: jmp dest2
[else
;; jumping past 2 instructions, the branch and b opnd1
(emit-branch type #t reversed? 2
(emit b `(label ,(fx+ disp1 4) ,l1)
(emit b `(label ,disp2 ,l2) '())))]))))))
(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)
(emit nop
(emit nop
(emit nop
(emit nop
(asm-helper-relocation reloc code*)))))))
(define asm-kill
(lambda (code* dest)
code*))
(define asm-helper-call
(lambda (code* reloc save-ra? tmp)
(Trivit (tmp)
;; NB. saves lr into the local variable space for the frame we are
;; NB. creating.
(define ax-save/restore
(lambda (code* tmp p)
(let ([Csp `(reg . ,%Csp)])
(emit mflr tmp
(emit stwu Csp Csp `(imm -16)
(emit stw tmp Csp `(imm 12)
(p (emit lwz tmp Csp `(imm 12)
(emit mtlr tmp
(emit addi Csp Csp `(imm 16) code*))))))))))
(define maybe-save-ra
(lambda (code* p)
(if save-ra?
(ax-save/restore code* tmp p)
(p code*))))
(maybe-save-ra code*
(lambda (code*)
(emit nop
(emit nop
(emit nop
(emit nop
(asm-helper-relocation reloc code*))))))))))
(define asm-helper-relocation
(lambda (reloc code*)
(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*))))))))))
(define asm-return
(lambda ()
(emit blr '())))
(define asm-c-return
(lambda (info)
(emit blr '())))
(define asm-lognot
(lambda (code* dest src)
(Trivit (dest src)
(emit nor dest src src code*))))
(define asm-enter values)
(define-who asm-inc-cc-counter
(lambda (code* addr val tmp)
(assert (not (eq? tmp %zero)))
(Trivit (addr val tmp)
(define do-ldr
(lambda (offset k code*)
(emit lwz tmp addr `(imm ,offset) (k (emit stw tmp addr `(imm ,offset) code*)))))
(define do-add/carry
(lambda (code*)
(emit addo. tmp tmp val code*)))
(do-ldr 4
do-add/carry
(emit bns 4
(do-ldr 0
(lambda (code*)
(emit addi tmp tmp `(imm 1) code*))
code*))))))
(define asm-store-with-update
(lambda (code* src base idx/off)
(Trivit (src base idx/off)
(record-case idx/off
[(imm) (n) (emit stwu src base `(imm ,n) code*)]
[else (emit stwux src base idx/off code*)]))))
(define asm-get-lr
(lambda ()
(lambda (code* dest)
(Trivit (dest)
(emit mflr dest code*)))))
(define asm-set-lr
(lambda ()
(lambda (code* src)
(Trivit (src)
(emit mtlr src code*)))))
(define asm-isync
(lambda (code*)
(emit isync code*)))
(module (asm-foreign-call asm-foreign-callable)
(define align (lambda (b x) (let ([k (- b 1)]) (fxlogand (fx+ x k) (fxlognot k)))))
(define gp-parameter-regs (lambda () (list %Carg1 %Carg2 %Carg3 %Carg4 %Carg5 %Carg6 %Carg7 %Carg8)))
(define fp-parameter-regs (lambda () (list %Cfparg1 %Cfparg2 %Cfparg3 %Cfparg4 %Cfparg5 %Cfparg6 %Cfparg7 %Cfparg8)))
(define fp-result-regs (lambda () (list %Cfpretval)))
(define (indirect-result-that-fits-in-registers? result-type)
(nanopass-case (Ltype Type) result-type
[(fp-ftd& ,ftd) (not ($ftd-compound? ftd))]
[else #f]))
(define (indirect-result-to-pointer? result-type)
(nanopass-case (Ltype Type) result-type
[(fp-ftd& ,ftd) ($ftd-compound? ftd)]
[else #f]))
(module (push-registers pop-registers)
;; stack offset must be 8-byte aligned if fp-reg-count is non-zero
(define (move-registers regs fp-reg-count fp-regs load? offset e)
(with-output-language (L13 Effect)
(cond
[(fx> fp-reg-count 0)
;; Push floating-point first to get correct alignment
(let ([offset (align 8 offset)])
(move-registers regs (fx- fp-reg-count 1) (cdr fp-regs) load? (fx+ offset 8)
(cond
[load? `(seq ,e (inline ,(make-info-loadfl (car fp-regs)) ,%load-double ,%sp ,%zero (immediate ,offset)))]
[else `(seq (inline ,(make-info-loadfl (car fp-regs)) ,%store-double ,%sp ,%zero (immediate ,offset)) ,e)])))]
[(pair? regs)
(move-registers (cdr regs) 0 '() load? (fx+ offset 4)
(cond
[load? `(seq ,e (set! ,(car regs) ,(%mref ,%sp ,offset)))]
[else `(seq (set! ,(%mref ,%sp ,offset) ,(car regs)) ,e)]))]
[else e])))
;; Add "pushes" before e
(define (push-registers regs fp-reg-count fp-regs offset e)
(move-registers regs fp-reg-count fp-regs #f offset e))
;; Add "pops" after e
(define (pop-registers regs fp-reg-count fp-regs offset e)
(move-registers regs fp-reg-count fp-regs #t offset e)))
(define-who asm-foreign-call
(with-output-language (L13 Effect)
(define load-double-stack
(lambda (offset fp-disp)
(lambda (x) ; requires var
(%seq
(inline ,(make-info-loadfl %flreg1) ,%load-double ,x ,%zero (immediate ,fp-disp))
(inline ,(make-info-loadfl %flreg1) ,%store-double ,%sp ,%zero (immediate ,offset))))))
(define load-single-stack
(lambda (offset fp-disp single?)
(lambda (x) ; requires var
(%seq
(inline ,(make-info-loadfl %flreg1) ,(if single? %load-single %load-double->single) ,x ,%zero (immediate ,fp-disp))
(inline ,(make-info-loadfl %flreg1) ,%store-single ,%sp ,%zero (immediate ,offset))))))
(define load-int-stack
(lambda (offset)
(lambda (rhs) ; requires rhs
`(set! ,(%mref ,%sp ,offset) ,rhs))))
(define load-int64-stack
(lambda (offset)
(lambda (lorhs hirhs) ; requires rhs
(%seq
(set! ,(%mref ,%sp ,(fx+ offset 4)) ,lorhs)
(set! ,(%mref ,%sp ,offset) ,hirhs)))))
(define load-indirect-int-stack
(lambda (offset size)
(lambda (rhs) ; requires rhs
(let ([int-type (case size
[(1) 'integer-8]
[(2) 'integer-16]
[else 'integer-32])])
`(set! ,(%mref ,%sp ,offset) (inline ,(make-info-load int-type #f) ,%load ,rhs ,%zero (immediate ,0)))))))
(define load-indirect-int64-stack
(lambda (offset)
(lambda (x) ; requires var
`(seq
(set! ,(%mref ,%sp ,offset) ,(%mref ,x 0))
(set! ,(%mref ,%sp ,(fx+ offset 4)) ,(%mref ,x 4))))))
(define load-double-reg
(lambda (fpreg fp-disp)
(lambda (x) ; requires var
`(inline ,(make-info-loadfl fpreg) ,%load-double ,x ,%zero (immediate ,fp-disp)))))
(define load-soft-double-reg
(lambda (loreg hireg fp-disp)
(lambda (x)
(%seq
(set! ,loreg ,(%mref ,x ,(fx+ fp-disp 4)))
(set! ,hireg ,(%mref ,x ,fp-disp))))))
(define 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)))))
(define load-soft-single-reg
(lambda (ireg fp-disp single?)
(lambda (x)
(%seq
(inline ,(make-info-loadfl %flreg1) ,(if single? %load-single %load-double->single) ,x ,%zero (immediate ,fp-disp))
(inline ,(make-info-loadfl %flreg1) ,%store-single ,%tc ,%zero (immediate ,(constant tc-ac0-disp)))
(set! ,ireg ,(%tc-ref ac0))))))
(define load-int-reg
(lambda (ireg)
(lambda (x) ; requires rhs
`(set! ,ireg ,x))))
(define load-int64-reg
(lambda (loreg hireg)
(lambda (lo hi) ; requires two rhss
(%seq
(set! ,loreg ,lo)
(set! ,hireg ,hi)))))
(define load-indirect-int-reg
(lambda (ireg size category)
(lambda (rhs) ; requires var
(let ([int-type (case category
[(unsigned) (case size
[(1) 'unsigned-8]
[(2) 'unsigned-16]
[else 'unsigned-32])]
[else (case size
[(1) 'integer-8]
[(2) 'integer-16]
[else 'integer-32])])])
`(set! ,ireg (inline ,(make-info-load int-type #f) ,%load ,rhs ,%zero (immediate ,0)))))))
(define load-indirect-int64-reg
(lambda (loreg hireg)
(lambda (x) ; requires var
`(seq
(set! ,hireg ,(%mref ,x 0))
(set! ,loreg ,(%mref ,x 4))))))
(define do-args
(lambda (types)
;; NB: start stack pointer at 8 to put arguments above the linkage area
(let loop ([types types] [locs '()] [live* '()] [int* (gp-parameter-regs)] [flt* (fp-parameter-regs)] [isp 8]
;; needed when adjusting active:
[fp-live-count 0]
;; configured for `ftd-fp&` unpacking of floats:
[fp-disp (constant flonum-data-disp)] [single? #f])
(if (null? types)
(values isp locs live* fp-live-count)
(nanopass-case (Ltype Type) (car types)
[(fp-double-float)
(if (constant software-floating-point)
(let ([int* (if (even? (length int*)) int* (cdr int*))])
(if (null? int*)
(let ([isp (align 8 isp)])
(loop (cdr types)
(cons (load-double-stack isp fp-disp) locs)
live* '() flt* (fx+ isp 8) fp-live-count
(constant flonum-data-disp) #f))
(loop (cdr types)
(cons (load-soft-double-reg (cadr int*) (car int*) fp-disp) locs)
(cons* (car int*) (cadr int*) live*) (cddr int*) flt* isp fp-live-count
(constant flonum-data-disp) #f)))
(if (null? flt*)
(let ([isp (align 8 isp)])
(loop (cdr types)
(cons (load-double-stack isp fp-disp) locs)
live* int* '() (fx+ isp 8) fp-live-count
(constant flonum-data-disp) #f))
(loop (cdr types)
(cons (load-double-reg (car flt*) fp-disp) locs)
live* int* (cdr flt*) isp (fx+ fp-live-count 1)
(constant flonum-data-disp) #f)))]
[(fp-single-float)
(if (constant software-floating-point)
(if (null? int*)
; NB: ABI says singles are passed as doubles on the stack, but gcc/linux doesn't
(loop (cdr types)
(cons (load-single-stack isp fp-disp single?) locs)
live* '() flt* (fx+ isp 4) fp-live-count
(constant flonum-data-disp) #f)
(loop (cdr types)
(cons (load-soft-single-reg (car int*) fp-disp single?) locs)
(cons (car int*) live*) (cdr int*) flt* isp fp-live-count
(constant flonum-data-disp) #f))
(if (null? flt*)
; NB: ABI says singles are passed as doubles on the stack, but gcc/linux doesn't
(let ([isp (align 4 isp)])
(loop (cdr types)
(cons (load-single-stack isp fp-disp single?) locs)
live* int* '() (fx+ isp 4) fp-live-count
(constant flonum-data-disp) #f))
(loop (cdr types)
(cons (load-single-reg (car flt*) fp-disp single?) locs)
live* int* (cdr flt*) isp (fx+ fp-live-count 1)
(constant flonum-data-disp) #f)))]
[(fp-ftd& ,ftd)
(cond
[($ftd-compound? ftd)
;; pass as pointer
(let ([pointer-type (with-output-language (Ltype Type) `(fp-integer 32))])
(loop (cons pointer-type (cdr types)) locs live* int* flt* isp fp-live-count
(constant flonum-data-disp) #f))]
[else
;; extract content and pass that content
(let ([category ($ftd-atomic-category ftd)])
(cond
[(eq? category 'float)
;; piggy-back on unboxed handler
(let ([unpacked-type (with-output-language (Ltype Type)
(case ($ftd-size ftd)
[(4) `(fp-single-float)]
[else `(fp-double-float)]))])
(loop (cons unpacked-type (cdr types)) locs live* int* flt* isp fp-live-count
;; no floating displacement within pointer:
0
;; in case of float, load as single-float:
(= ($ftd-size ftd) 4)))]
[(and (memq category '(integer unsigned))
(fx= 8 ($ftd-size ftd)))
(let ([int* (if (even? (length int*)) int* (cdr int*))])
(if (null? int*)
(let ([isp (align 8 isp)])
(loop (cdr types)
(cons (load-indirect-int64-stack isp) locs)
live* '() flt* (fx+ isp 8) fp-live-count
(constant flonum-data-disp) #f))
(loop (cdr types)
(cons (load-indirect-int64-reg (cadr int*) (car int*)) locs)
(cons* (car int*) (cadr int*) live*) (cddr int*) flt* isp fp-live-count
(constant flonum-data-disp) #f)))]
[else
(if (null? int*)
(loop (cdr types)
(cons (load-indirect-int-stack isp ($ftd-size ftd)) locs)
live* '() flt* (fx+ isp 4) fp-live-count
(constant flonum-data-disp) #f)
(loop (cdr types)
(cons (load-indirect-int-reg (car int*) ($ftd-size ftd) category) locs)
(cons (car int*) live*) (cdr int*) flt* isp fp-live-count
(constant flonum-data-disp) #f))]))])]
[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* '() flt* (fx+ isp 8) fp-live-count
(constant flonum-data-disp) #f))
(loop (cdr types)
(cons (load-int64-reg (cadr int*) (car int*)) locs)
(cons* (car int*) (cadr int*) live*) (cddr int*) flt* isp fp-live-count
(constant flonum-data-disp) #f)))
(if (null? int*)
(loop (cdr types)
(cons (load-int-stack isp) locs)
live* '() flt* (fx+ isp 4) fp-live-count
(constant flonum-data-disp) #f)
(loop (cdr types)
(cons (load-int-reg (car int*)) locs)
(cons (car int*) live*) (cdr int*) flt* isp fp-live-count
(constant flonum-data-disp) #f)))])))))
(define do-indirect-result-from-registers
(lambda (ftd offset)
(let ([tmp %Carg8])
(%seq
(set! ,tmp ,(%mref ,%sp ,offset))
,(cond
[(and (not (constant software-floating-point))
(eq? 'float ($ftd-atomic-category ftd)))
`(inline ,(make-info-loadfl %Cfpretval) ,(if (= 4 ($ftd-size ftd)) %store-single %store-double)
,tmp ,%zero (immediate 0))]
[else
(case ($ftd-size ftd)
[(1) `(inline ,(make-info-load 'integer-8 #f) ,%store ,tmp ,%zero (immediate 0) ,%Cretval)]
[(2) `(inline ,(make-info-load 'integer-16 #f) ,%store ,tmp ,%zero (immediate 0) ,%Cretval)]
[(4) `(inline ,(make-info-load 'integer-32 #f) ,%store ,tmp ,%zero (immediate 0) ,%Cretval)]
[(8)
(%seq
(inline ,(make-info-load 'integer-32 #f) ,%store ,tmp ,%zero (immediate 0) ,%Cretval-high)
(inline ,(make-info-load 'integer-32 #f) ,%store ,tmp ,%zero (immediate 4) ,%Cretval-low))]
[else (sorry! who "unexpected result size")])])))))
(define (add-deactivate t0 offset live* fp-live-count result-live* result-fp-live-count e)
(let ([save-and-restore
(lambda (regs fp-count fp-regs e)
(cond
[(and (null? regs) (fx= 0 fp-count)) e]
[else
(pop-registers regs fp-count fp-regs offset
(push-registers regs fp-count fp-regs offset
e))]))])
(%seq
(set! ,%deact ,t0)
,(save-and-restore (cons %deact live*) fp-live-count (fp-parameter-regs) (%inline deactivate-thread))
,e
,(save-and-restore result-live* result-fp-live-count (fp-result-regs) `(set! ,%Cretval ,(%inline activate-thread))))))
(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)]
[adjust-active? (if-feature pthreads (memq 'adjust-active (info-foreign-conv* info)) #f)])
(with-values (do-args (if fill-result-here? (cdr arg-type*) arg-type*))
(lambda (orig-frame-size locs live* fp-live-count)
;; NB: add 4 to frame size for CR save word
(let* ([fill-stash-offset orig-frame-size]
[base-frame-size (fx+ orig-frame-size (if fill-result-here? 4 0))]
[deactivate-save-offset (if (and adjust-active? (fx> fp-live-count 0))
(align 8 base-frame-size) ; for `double` save
base-frame-size)]
[frame-size (align 16 (fx+ 4 ; for CR save
(if adjust-active?
(fx+ deactivate-save-offset
(fx* fp-live-count 8)
(fx* (length live*) 4))
deactivate-save-offset)))])
(values
(lambda () (%inline store-with-update ,%Csp ,%Csp (immediate ,(fx- frame-size))))
(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 fill-stash-offset) locs)]
[else locs]))
(lambda (t0)
(define (make-call result-live* result-fp-live-count)
(cond
[adjust-active?
(add-deactivate t0 deactivate-save-offset live* fp-live-count result-live* result-fp-live-count
`(inline ,(make-info-kill*-live* result-live* live*) ,%c-call ,%deact))]
[else `(inline ,(make-info-kill*-live* result-live* live*) ,%c-call ,t0)]))
(if (constant software-floating-point)
(let ()
(define handle-64-bit
(lambda ()
(make-call (reg-list %Cretval-high %Cretval-low) 0)))
(define handle-32-bit
(lambda ()
(make-call (reg-list %Cretval) 0)))
(define handle-integer-cases
(lambda (bits)
(case bits
[(8 16 32) (handle-32-bit)]
[(64) (handle-64-bit)]
[else (sorry! who "unexpected asm-foreign-procedures fp-integer size ~s" bits)])))
(define (handle-ftd&-case ftd)
(cond
[fill-result-here?
(%seq
,(if (> ($ftd-size ftd) 4)
(handle-64-bit)
(handle-32-bit))
,(do-indirect-result-from-registers ftd fill-stash-offset))]
[else (make-call (reg-list) 0)]))
(nanopass-case (Ltype Type) result-type
[(fp-double-float) (handle-64-bit)]
[(fp-single-float) (handle-32-bit)]
[(fp-integer ,bits) (handle-integer-cases bits)]
[(fp-integer ,bits) (handle-integer-cases bits)]
[(fp-ftd& ,ftd) (handle-ftd&-case ftd)]
[else (make-call (reg-list %Cretval) 0)]))
(let ()
(define handle-integer-cases
(lambda (bits)
(case bits
[(8 16 32) (make-call (reg-list %Cretval) 0)]
[(64) (make-call (reg-list %Cretval-high %Cretval-low) 0)]
[else (sorry! who "unexpected asm-foreign-procedures fp-integer size ~s" bits)])))
(define (handle-ftd&-case ftd)
(cond
[fill-result-here?
(%seq
,(if (not (eq? 'float ($ftd-atomic-category ftd)))
(handle-integer-cases (* 8 ($ftd-size ftd)))
(make-call (reg-list) 1))
,(do-indirect-result-from-registers ftd fill-stash-offset))]
[else `(inline ,(make-info-kill*-live* (reg-list) live*) ,%c-call ,t0)]))
(nanopass-case (Ltype Type) result-type
[(fp-double-float) (make-call (reg-list) 1)]
[(fp-single-float) (make-call (reg-list) 1)]
[(fp-integer ,bits) (handle-integer-cases bits)]
[(fp-unsigned ,bits) (handle-integer-cases bits)]
[(fp-ftd& ,ftd) (handle-ftd&-case ftd)]
[else (make-call (reg-list %Cretval) 0)]))))
(nanopass-case (Ltype Type) result-type
[(fp-double-float)
(lambda (lvalue)
(if (constant software-floating-point)
(%seq
(set! ,(%mref ,lvalue ,(constant flonum-data-disp)) ,%Cretval-high)
(set! ,(%mref ,lvalue ,(fx+ (constant flonum-data-disp) 4)) ,%Cretval-low))
`(inline ,(make-info-loadfl %Cfpretval) ,%store-double ,lvalue ,%zero
,(%constant flonum-data-disp))))]
[(fp-single-float)
(lambda (lvalue)
(if (constant software-floating-point)
(%seq
(set! ,(%tc-ref ac0) ,%Cretval)
(inline ,(make-info-loadfl %flreg1) ,%load-single->double ,%tc ,%zero (immediate ,(constant tc-ac0-disp)))
(inline ,(make-info-loadfl %flreg1) ,%store-double ,lvalue ,%zero ,(%constant flonum-data-disp)))
`(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 ,%Cretval)))]
[(16) (lambda (lvalue) `(set! ,lvalue ,(%inline sext16 ,%Cretval)))]
[(32) (lambda (lvalue) `(set! ,lvalue ,%Cretval))]
[(64) (lambda (lvlow lvhigh)
`(seq
(set! ,lvhigh ,%Cretval-high)
(set! ,lvlow ,%Cretval-low)))]
[else (sorry! who "unexpected asm-foreign-procedures fp-integer size ~s" bits)])]
[(fp-unsigned ,bits)
(case bits
[(8) (lambda (lvalue) `(set! ,lvalue ,(%inline zext8 ,%Cretval)))]
[(16) (lambda (lvalue) `(set! ,lvalue ,(%inline zext16 ,%Cretval)))]
[(32) (lambda (lvalue) `(set! ,lvalue ,%Cretval))]
[(64) (lambda (lvlow lvhigh)
`(seq
(set! ,lvhigh ,%Cretval-high)
(set! ,lvlow ,%Cretval-low)))]
[else (sorry! who "unexpected asm-foreign-procedures fp-unsigned size ~s" bits)])]
[else (lambda (lvalue) `(set! ,lvalue ,%Cretval))])
(lambda () `(set! ,%sp ,(%inline + ,%sp (immediate ,frame-size))))))))))))
(define-who asm-foreign-callable
#|
PPC general frame layout (f calls g calls h)
+---------------------------+
| |
| parameter list | 0-? words (g's stack arguments from f)
sp+n+8: | |
+---------------------------+
| |
| lr | 1 word (place for g to store lr)
sp+n+4: | |
+---------------------------+
| |
f's frame | back chain | 1 word
sp+n: | | <--------------------------------+
+---------------------------+ |
+---------------------------+ |
| | |
| floating-point regs | 0-18 double-words (g's callee-save fprs) |
sp+8+X+Y+1+Z: | | |
+---------------------------+ |
| | |
| integer regs | 0-18 words (g's callee-saved gprs) |
sp+8+X+Y+1: | | |
+---------------------------+ |
| | |
| control register | 1 word (g's saved cr) |
sp+8+X+Y: | | |
+---------------------------+ |
| | |
| local variable space | 0-? words |
sp+8+X: | (and padding) | |
+---------------------------+ |
| | |
| parameter list | 0-? words (h's stack arguments from g) |
sp+8: | | |
+---------------------------+ |
| | |
| lr | 1 word (place for h to store lr) |
sp+4: | | |
+---------------------------+ |
| | |
g's frame | back chain | 1 word ---------------------------------+
sp+0: | [sp+n] |
+---------------------------+
+---------------------------+
| |
h's frame | |
| |
X = number of bytes for parameters
Y = number of bytes for local variables
Z = number of bytes for callee-save gp regs
n = 8 + X + Y + 1 + Z + [number of bytes for callee-save fp regs]
PPC foreign-callable Frame Layout
sp+188:
+---------------------------+
| |
| lr | 1 word
sp+X+4: | |
+---------------------------+
| |
| back chain | 1 word
sp+X: | |
+---------------------------+ <- 16-byte aligned
+---------------------------+
+---------------------------+ <- 16-byte aligned
| |
| &-return space | 2 words, if needed
| |
+---------------------------+ <- 8-byte aligned
| unactivate mode | 1 word, if needed
+---------------------------+
| |
| callee-save regs |
| |
+---------------------------+
| |
| floating-point arg regs |
| |
+---------------------------+ <- 8-byte aligned
| |
| integer argument regs | Also used to stash results during unactivate
| |
sp+8: +---------------------------+ <- 8-byte aligned
| |
| lr | 1 word (place for get-thread-context to store lr)
| |
+---------------------------+
| |
| back chain | 1 word
sp+0: | [sp+X-4] |
+---------------------------+
FOR foreign callable (nb: assuming flreg1 & flreg2 are caller-save):
decrement sp by 176 & set up back chain (atomically)
save gp arg regs (based on number declared by foreign-callable form) at sp+8
save fp arg regs (based on number declared by foreign-callable form) at sp+40
don't bother saving cr
save callee-save gp registers at sp+108 (could avoid those we don't use during argument conversion, if we knew what they were)
save lr at sp[188] (actually sp 4, before sp is moved)
if threaded:
call get-thread-context
else
tc <- thread-context
endif
...
restore lr from sp[188]
INVARIANTS
stack grows down
each frame 16-byte aligned
|#
(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-soft-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 ,(fx+ offset 3))))]
[(16) `(set! ,lvalue (inline ,(make-info-load 'integer-16 #f) ,%load ,%sp ,%zero (immediate ,(fx+ offset 2))))]
[(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 ,(fx+ offset 3))))]
[(16) `(set! ,lvalue (inline ,(make-info-load 'unsigned-16 #f) ,%load ,%sp ,%zero (immediate ,(fx+ offset 2))))]
[(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 ,(fx+ offset 4)))
(set! ,hilvalue ,(%mref ,%sp ,offset))))))
(define load-stack-address
(lambda (offset)
(lambda (lvalue)
`(set! ,lvalue ,(%inline + ,%sp (immediate ,offset))))))
(define load-stack-address/convert-float
(lambda (offset)
(lambda (lvalue)
(%seq
;; Overwrite argument on stack with single-precision version
;; FIXME: is the callee allowed to do this if the argument is passed on the stack?
(inline ,(make-info-loadfl %flreg1) ,%load-double->single ,%sp ,%zero (immediate ,offset))
(inline ,(make-info-loadfl %flreg1) ,%store-single ,%sp ,%zero (immediate ,offset))
(set! ,lvalue ,(%inline + ,%sp (immediate ,offset)))))))
(define count-reg-args
(lambda (types gp-reg-count fp-reg-count synthesize-first-argument?)
(let f ([types types] [iint (if synthesize-first-argument? -1 0)] [iflt 0])
(if (null? types)
(values iint iflt)
(cond
[(and (not (constant software-floating-point))
(nanopass-case (Ltype Type) (car types)
[(fp-double-float) #t]
[(fp-single-float) #t]
[(fp-ftd& ,ftd) (eq? 'float ($ftd-atomic-category ftd))]
[else #f]))
(f (cdr types) iint (if (fx< iflt fp-reg-count) (fx+ iflt 1) iflt))]
[(or (nanopass-case (Ltype Type) (car types)
[(fp-integer ,bits) (fx= bits 64)]
[(fp-unsigned ,bits) (fx= bits 64)]
[(fp-ftd& ,ftd) (and (not ($ftd-compound? ftd))
(fx= 8 ($ftd-size ftd)))]
[else #f])
(and (constant software-floating-point)
(nanopass-case (Ltype Type) (car types)
[(fp-double-float) #t]
[else #f])))
(let ([iint (align 2 iint)])
(f (cdr types) (if (fx< iint gp-reg-count) (fx+ iint 2) iint) iflt))]
[else (f (cdr types) (if (fx< iint gp-reg-count) (fx+ iint 1) iint) iflt)])))))
(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
(lambda (types gp-reg-count fp-reg-count int-reg-offset float-reg-offset stack-arg-offset
synthesize-first-argument? return-space-offset)
(let loop ([types (if synthesize-first-argument? (cdr types) types)]
[locs '()]
[iint 0]
[iflt 0]
[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-argument?
(cons (load-stack-address return-space-offset)
locs)
locs))
(cond
[(and (not (constant software-floating-point))
(nanopass-case (Ltype Type) (car types)
[(fp-double-float) #t]
[(fp-single-float) #t]
[else #f]))
(if (fx< iflt fp-reg-count)
(loop (cdr types)
(cons (load-double-stack float-reg-offset) locs)
iint (fx+ iflt 1) 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 iflt int-reg-offset float-reg-offset (fx+ stack-arg-offset 8))))]
[(and (constant software-floating-point)
(nanopass-case (Ltype Type) (car types)
[(fp-double-float) #t]
[else #f]))
(let ([iint (align 2 iint)])
(if (fx< iint gp-reg-count)
(let ([int-reg-offset (align 8 int-reg-offset)])
(loop (cdr types)
(cons (load-double-stack int-reg-offset) locs)
(fx+ iint 2) iflt (fx+ int-reg-offset 8) float-reg-offset stack-arg-offset))
(let ([stack-arg-offset (align 8 stack-arg-offset)])
(loop (cdr types)
(cons (load-double-stack stack-arg-offset) locs)
iint iflt int-reg-offset float-reg-offset (fx+ stack-arg-offset 8)))))]
[(and (constant software-floating-point)
(nanopass-case (Ltype Type) (car types)
[(fp-single-float) #t]
[else #f]))
(if (fx< iint gp-reg-count)
(loop (cdr types)
(cons (load-soft-single-stack int-reg-offset) locs)
(fx+ iint 1) iflt (fx+ int-reg-offset 4) float-reg-offset stack-arg-offset)
(loop (cdr types)
(cons (load-soft-single-stack stack-arg-offset) locs)
iint iflt int-reg-offset float-reg-offset (fx+ stack-arg-offset 4)))]
[(nanopass-case (Ltype Type) (car types)
[(fp-ftd& ,ftd) (not ($ftd-compound? ftd))]
[else #f])
;; load pointer to address on the stack
(let ([ftd (nanopass-case (Ltype Type) (car types)
[(fp-ftd& ,ftd) ftd])])
(case (and (not (constant software-floating-point))
($ftd-atomic-category ftd))
[(float)
(let ([load-address (case ($ftd-size ftd)
[(4) load-stack-address/convert-float]
[else load-stack-address])])
(if (fx< iflt fp-reg-count)
(loop (cdr types)
(cons (load-address float-reg-offset) locs)
iint (fx+ iflt 1) 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-address stack-arg-offset) locs)
iint iflt int-reg-offset float-reg-offset (fx+ stack-arg-offset 8)))))]
[else
(case ($ftd-size ftd)
[(8)
(let ([iint (align 2 iint)])
(if (fx< iint gp-reg-count)
(let ([int-reg-offset (align 8 int-reg-offset)])
(loop (cdr types)
(cons (load-stack-address int-reg-offset) locs)
(fx+ iint 2) iflt (fx+ int-reg-offset 8) float-reg-offset stack-arg-offset))
(let ([stack-arg-offset (align 8 stack-arg-offset)])
(loop (cdr types)
(cons (load-stack-address stack-arg-offset) locs)
iint iflt int-reg-offset float-reg-offset (fx+ stack-arg-offset 8)))))]
[else
(let ([byte-offset (- 4 ($ftd-size ftd))])
(if (fx< iint gp-reg-count)
(loop (cdr types)
(cons (load-stack-address (+ int-reg-offset byte-offset)) locs)
(fx+ iint 1) iflt (fx+ int-reg-offset 4) float-reg-offset stack-arg-offset)
(loop (cdr types)
(cons (load-stack-address (+ stack-arg-offset byte-offset)) locs)
iint iflt int-reg-offset float-reg-offset (fx+ stack-arg-offset 4))))])]))]
[(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)])
(if (fx< iint gp-reg-count)
(let ([int-reg-offset (align 8 int-reg-offset)])
(loop (cdr types)
(cons (load-int64-stack int-reg-offset) locs)
(fx+ iint 2) iflt (fx+ int-reg-offset 8) float-reg-offset stack-arg-offset))
(let ([stack-arg-offset (align 8 stack-arg-offset)])
(loop (cdr types)
(cons (load-int64-stack stack-arg-offset) locs)
iint iflt int-reg-offset float-reg-offset (fx+ stack-arg-offset 8)))))]
[else
(if (fx< iint gp-reg-count)
(loop (cdr types)
(cons (load-int-stack (car types) int-reg-offset) locs)
(fx+ iint 1) iflt (fx+ int-reg-offset 4) float-reg-offset stack-arg-offset)
(loop (cdr types)
(cons (load-int-stack (car types) stack-arg-offset) locs)
iint iflt int-reg-offset float-reg-offset (fx+ stack-arg-offset 4)))])))))
(define save-regs
(lambda (regs offset)
(if (null? regs)
`(nop)
(let f ([regs regs] [offset offset])
(let ([inline `(inline ,(make-info-load 'integer-32 #f) ,%store ,%Csp ,%zero (immediate ,offset) ,(car regs))])
(let ([regs (cdr regs)])
(if (null? regs)
inline
(%seq ,inline ,(f regs (fx+ offset 4))))))))))
(define save-fp-regs
(lambda (regs offset)
(if (null? regs)
`(nop)
(let f ([regs regs] [offset offset])
(let ([inline `(inline ,(make-info-loadfl (car regs)) ,%store-double ,%Csp ,%zero (immediate ,offset))])
(let ([regs (cdr regs)])
(if (null? regs)
inline
(%seq ,inline ,(f regs (fx+ offset 8))))))))))
(define restore-regs
(lambda (regs offset)
(if (null? regs)
`(nop)
(let f ([regs regs] [offset offset])
(let ([inline `(set! ,(car regs) (inline ,(make-info-load 'integer-32 #f) ,%load ,%Csp ,%zero (immediate ,offset)))])
(let ([regs (cdr regs)])
(if (null? regs)
inline
(%seq ,inline ,(f regs (fx+ offset 4))))))))))
(define do-result
(lambda (result-type return-space-offset int-reg-offset)
(nanopass-case (Ltype Type) result-type
[(fp-ftd& ,ftd)
(case ($ftd-atomic-category ftd)
[(float)
(values
(lambda ()
(case ($ftd-size ftd)
[(4) `(inline ,(make-info-loadfl %Cfpretval) ,%load-single ,%sp ,%zero (immediate ,return-space-offset))]
[else `(inline ,(make-info-loadfl %Cfpretval) ,%load-double ,%sp ,%zero (immediate ,return-space-offset))]))
'()
1)]
[else
(cond
[($ftd-compound? ftd)
;; return pointer
(values
(lambda () `(set! ,%Cretval ,(%mref ,%sp ,int-reg-offset)))
(list %Cretval)
0)]
[(fx= 8 ($ftd-size ftd))
(values (lambda ()
(%seq
(set! ,%Cretval-high ,(%mref ,%sp ,return-space-offset))
(set! ,%Cretval-low ,(%mref ,%sp ,(fx+ return-space-offset 4)))))
(list %Cretval-high %Cretval-low)
0)]
[else
(values
(lambda ()
(case ($ftd-size ftd)
[(1) `(set! ,%Cretval (inline ,(make-info-load 'integer-8 #f) ,%load ,%sp ,%zero (immediate ,return-space-offset)))]
[(2) `(set! ,%Cretval (inline ,(make-info-load 'integer-16 #f) ,%load ,%sp ,%zero (immediate ,return-space-offset)))]
[else `(set! ,%Cretval ,(%mref ,%sp ,return-space-offset))]))
(list %Cretval)
0)])])]
[(fp-double-float)
(values (lambda (x)
`(inline ,(make-info-loadfl %Cfpretval) ,%load-double ,x ,%zero ,(%constant flonum-data-disp)))
'()
1)]
[(fp-single-float)
(values (lambda (x)
`(inline ,(make-info-loadfl %Cfpretval) ,%load-double->single ,x ,%zero ,(%constant flonum-data-disp)))
'()
1)]
[(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-rhs hi-rhs)
(%seq
(set! ,%Cretval-low ,lo-rhs)
(set! ,%Cretval-high ,hi-rhs)))
(list %Cretval-high %Cretval-low)
0)]
[else
(values (lambda (rhs)
`(set! ,%Cretval ,rhs))
(list %Cretval)
0)])])))
(define (unactivate unactivate-mode-offset result-regs result-num-fp-regs stash-offset)
(let ([e (%seq
(set! ,%Carg1 ,(%mref ,%sp ,unactivate-mode-offset))
,(%inline unactivate-thread ,%Carg1))])
(pop-registers result-regs result-num-fp-regs (fp-result-regs) stash-offset
(push-registers result-regs result-num-fp-regs (fp-result-regs) stash-offset
e))))
(lambda (info)
(define callee-save-regs (list %r14 %r15 %r16 %r17 %r18 %r19 %r20 %r21 %r22 %r23 %r24 %r25 %r26 %r27 %r28 %r29 %r30 %r31))
(define isaved (length callee-save-regs))
(let ([arg-type* (info-foreign-arg-type* info)]
[result-type (info-foreign-result-type info)]
[gp-reg-count (length (gp-parameter-regs))]
[fp-reg-count (length (fp-parameter-regs))])
(let-values ([(iint iflt) (count-reg-args arg-type* gp-reg-count fp-reg-count (indirect-result-that-fits-in-registers? result-type))])
(let* ([int-reg-offset 8] ; initial offset for calling conventions
[float-reg-offset (align 8 (fx+ (fx* gp-reg-count 4) int-reg-offset))]
[callee-save-offset (if (constant software-floating-point)
float-reg-offset
(fx+ (fx* fp-reg-count 8) float-reg-offset))]
[synthesize-first-argument? (indirect-result-that-fits-in-registers? result-type)]
[adjust-active? (if-feature pthreads (memq 'adjust-active (info-foreign-conv* info)) #f)]
[unactivate-mode-offset (fx+ (fx* isaved 4) callee-save-offset)]
[return-space-offset (align 8 (fx+ unactivate-mode-offset (if adjust-active? 4 0)))]
[stack-size (align 16 (fx+ return-space-offset (if synthesize-first-argument? 8 0)))]
[stack-arg-offset (fx+ stack-size 8)])
(let-values ([(get-result result-regs result-num-fp-regs) (do-result result-type return-space-offset int-reg-offset)])
(values
(lambda ()
(%seq
,(%inline save-lr (immediate 4))
,(%inline store-with-update ,%Csp ,%Csp (immediate ,(fx- stack-size)))
,(save-regs (list-head (gp-parameter-regs) iint) int-reg-offset)
,(save-fp-regs (list-head (fp-parameter-regs) iflt) float-reg-offset)
; not bothering with callee-save floating point regs right now
; not bothering with cr, because we don't update nonvolatile fields
,(save-regs callee-save-regs callee-save-offset)
,(if-feature pthreads
((lambda (e)
(if adjust-active?
(%seq
(set! ,%Cretval ,(%inline activate-thread))
(set! ,(%mref ,%sp ,unactivate-mode-offset) ,%Cretval)
,e)
e))
(%seq
(set! ,%Cretval ,(%inline get-tc))
(set! ,%tc ,%Cretval)))
`(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* gp-reg-count fp-reg-count int-reg-offset float-reg-offset stack-arg-offset
synthesize-first-argument? return-space-offset)
get-result
(lambda ()
(in-context Tail
((lambda (e)
(if adjust-active?
(%seq
,(unactivate unactivate-mode-offset result-regs result-num-fp-regs int-reg-offset)
,e)
e))
(%seq
; restore the lr
(inline ,null-info ,%restore-lr (immediate ,(fx+ stack-size 4)))
; restore the callee save registers
,(restore-regs callee-save-regs callee-save-offset)
; deallocate space for pad & arg reg values
(set! ,%Csp ,(%inline + ,%Csp (immediate ,stack-size)))
; done
(asm-c-return ,null-info ,callee-save-regs ... ,result-regs ...)))))))))))))))
)