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/ta6ob/s/cpnanopass.ss
2022-08-09 23:28:25 +02:00

16128 lines
843 KiB
Scheme

;;; cpnanopass.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.
(let ()
(include "np-languages.ss")
(define track-dynamic-closure-counts ($make-thread-parameter #f (lambda (x) (and x #t))))
(define track-static-closure-counts
($make-thread-parameter
#f
(lambda (x)
(include "types.ss")
(cond
[(or (not x) (static-closure-info? x)) x]
[(eq? x #t) (make-static-closure-info)]
[else ($oops '$trace-static-closure-counts "~s is not a static-closure-info record or #f" x)]))))
(module ()
(include "types.ss")
(set-who! $dynamic-closure-counts
(lambda ()
(vector
(profile-counter-count #{raw-ref-count bhowt6w0coxl0s2y-1})
(profile-counter-count #{raw-create-count bhowt6w0coxl0s2y-2})
(profile-counter-count #{raw-alloc-count bhowt6w0coxl0s2y-3})
(profile-counter-count #{ref-count bhowt6w0coxl0s2y-4})
(profile-counter-count #{pair-create-count bhowt6w0coxl0s2y-5})
(profile-counter-count #{vector-create-count bhowt6w0coxl0s2y-6})
(profile-counter-count #{vector-alloc-count bhowt6w0coxl0s2y-8})
(profile-counter-count #{padded-vector-alloc-count bhowt6w0coxl0s2y-11})
(profile-counter-count #{closure-create-count bhowt6w0coxl0s2y-7})
(profile-counter-count #{closure-alloc-count bhowt6w0coxl0s2y-9})
(profile-counter-count #{padded-closure-alloc-count bhowt6w0coxl0s2y-10}))))
(set-who! $clear-dynamic-closure-counts
(lambda ()
(profile-counter-count-set! #{raw-ref-count bhowt6w0coxl0s2y-1} 0)
(profile-counter-count-set! #{raw-create-count bhowt6w0coxl0s2y-2} 0)
(profile-counter-count-set! #{raw-alloc-count bhowt6w0coxl0s2y-3} 0)
(profile-counter-count-set! #{ref-count bhowt6w0coxl0s2y-4} 0)
(profile-counter-count-set! #{pair-create-count bhowt6w0coxl0s2y-5} 0)
(profile-counter-count-set! #{vector-create-count bhowt6w0coxl0s2y-6} 0)
(profile-counter-count-set! #{vector-alloc-count bhowt6w0coxl0s2y-8} 0)
(profile-counter-count-set! #{padded-vector-alloc-count bhowt6w0coxl0s2y-11} 0)
(profile-counter-count-set! #{closure-create-count bhowt6w0coxl0s2y-7} 0)
(profile-counter-count-set! #{closure-alloc-count bhowt6w0coxl0s2y-9} 0)
(profile-counter-count-set! #{padded-closure-alloc-count bhowt6w0coxl0s2y-10} 0))))
(define-syntax traceit
(syntax-rules (x)
[(_ name) (set! name (let ([t name]) (trace-lambda name args (apply t args))))]))
(define-syntax architecture
(let ([fn (format "~a.ss" (constant architecture))])
(with-source-path 'architecture fn
(lambda (fn)
(let* ([p ($open-file-input-port 'include fn)]
[sfd ($source-file-descriptor fn p)]
[p (transcoded-port p (current-transcoder))])
(let ([do-read ($make-read p sfd 0)])
(let* ([regs (do-read)] [inst (do-read)] [asm (do-read)])
(when (eof-object? asm) ($oops #f "too few expressions in ~a" fn))
(unless (eof-object? (do-read)) ($oops #f "too many expressions in ~a" fn))
(close-input-port p)
(lambda (x)
(syntax-case x (registers instructions assembler)
[(k registers) (datum->syntax #'k regs)]
[(k instructions) (datum->syntax #'k inst)]
[(k assembler) (datum->syntax #'k asm)])))))))))
; version in cmacros uses keyword as template and should
; probably be changed to use the id
(define-syntax define-who
(lambda (x)
(syntax-case x ()
[(_ (id . args) b1 b2 ...)
(identifier? #'id)
#'(define-who id (lambda args b1 b2 ...))]
[(_ id e)
(identifier? #'id)
(with-implicit (id who)
#'(define id (let ([who 'id]) e)))])))
(module (get-passes pass xpass pass-time?)
(define-syntax passes-loc (make-compile-time-value (box '())))
(define-syntax get-passes
(lambda (x)
(lambda (r)
(syntax-case x ()
[(_) #`(unbox (quote #,(datum->syntax #'* (r #'passes-loc))))]))))
(module (pass)
(define ir-printer
(lambda (unparser)
(lambda (val*)
(safe-assert (not (null? val*)))
(pretty-print (flatten-seq (unparser (car val*)))))))
(define values-printer
(lambda (val*)
(if (null? val*)
(printf "no output\n")
(pretty-print (car val*)))))
(define-syntax pass
(syntax-rules ()
[(_ (pass-name ?arg ...) ?unparser)
(identifier? #'pass-name)
(let ([pass-name (pass-name ?arg ...)])
(lambda args (xpass pass-name (ir-printer ?unparser) args)))]
[(_ pass-name ?unparser)
(identifier? #'pass-name)
(lambda args (xpass pass-name (ir-printer ?unparser) args))]
[(_ (pass-name ?arg ...))
(identifier? #'pass-name)
(let ([pass-name (pass-name ?arg ...)])
(lambda args (xpass pass-name values-printer args)))]
[(_ pass-name)
(identifier? #'pass-name)
(lambda args (xpass pass-name values-printer args))])))
(module (xpass pass-time?)
(define-threaded pass-time?)
(define $xpass
(lambda (printer pass-name pass arg*)
(let-values ([val* (let ([th (lambda () (apply pass arg*))])
(if pass-time? ($pass-time pass-name th) (th)))])
(when (memq pass-name (tracer))
(printf "output of ~s:\n" pass-name)
(printer val*))
(apply values val*))))
(define-syntax xpass
(lambda (x)
(syntax-case x ()
[(_ pass-name ?printer ?args)
(lambda (r)
(let ([loc (r #'passes-loc)])
(set-box! loc (cons (datum pass-name) (unbox loc))))
#`($xpass ?printer 'pass-name pass-name ?args))]))))
(define flatten-seq
(lambda (x)
(define helper
(lambda (x*)
(if (null? x*)
'()
(let ([x (car x*)])
(if (and (pair? x) (eq? (car x) 'seq))
(append (helper (cdr x)) (helper (cdr x*)))
(cons (flatten-seq x) (helper (cdr x*))))))))
(cond
[(null? x) '()]
[(and (pair? x) (eq? (car x) 'seq))
(let ([x* (helper (cdr x))])
(if (fx= (length x*) 1)
(car x*)
(cons 'begin x*)))]
[(and (pair? x) (eq? (car x) 'quote)) x]
[(list? x) (map flatten-seq x)]
[else x]))))
(define compose
(lambda (v p . p*)
(let loop ([v* (list v)] [p p] [p* p*])
(if (null? p*)
(apply p v*)
(let-values ([v* (apply p v*)])
(loop v* (car p*) (cdr p*)))))))
(define-syntax with-virgin-quasiquote
(lambda (x)
(syntax-case x ()
[(k e1 e2 ...)
#`(let-syntax ([#,(datum->syntax #'k 'quasiquote)
(syntax-rules () [(_ x) `x])])
e1 e2 ...)])))
(define valid-pass?
(lambda (x)
(memq x (get-passes))))
(define last-pass ; potentially not thread-safe, but currently unused
(make-parameter #f
(lambda (x)
(unless (or (eq? x #f) (valid-pass? x))
(errorf 'last-pass "~s is not a valid pass" x))
x)))
(define tracer ; potentially not thread-safe, but currently unused
(let ([ls '()])
(case-lambda
[() ls]
[(x)
(cond
[(or (null? x) (not x)) (set! ls '())]
[(eq? x #t) (set! ls (get-passes))]
[(valid-pass? x) (set! ls (cons x ls))]
[(list? x) (for-each tracer x)]
[else (errorf 'tracer "invalid trace list or pass name: ~s" x)])])))
(define maybe-cons
(lambda (x ls)
(if x (cons x ls) ls)))
(define unannotate
(lambda (x)
(if (annotation? x)
(annotation-expression x)
x)))
(let ()
(import (nanopass) np-languages)
(define signed-32?
(let ([n (bitwise-arithmetic-shift-left 1 (fx- 32 1))])
(let ([low (- n)] [high (- n 1)])
(if (fixnum? low)
(lambda (x) (and (fixnum? x) (fx<= low x high)))
(lambda (x) (or (fixnum? x) (<= low x high)))))))
(define nodups
(lambda x**
(let ([x* (apply append x**)])
(let ([ans (andmap (lambda (x) (and (not (uvar-seen? x)) (uvar-seen! x #t) #t)) x*)])
(for-each (lambda (x) (uvar-seen! x #f)) x*)
ans))))
(define chunked-bytevector-bitcount
; assumes "chunked" bytevector a multiple of 2 in size
(let ([bitcount-bv (make-bytevector #x10000)])
(do ([i 0 (fx+ i 1)])
((fx= i #x10000))
(bytevector-u8-set! bitcount-bv i (fxbit-count i)))
(lambda (bv)
(let loop ([n (bytevector-length bv)] [count 0])
(if (fx= n 0)
count
(let ([n (fx- n 2)])
(loop n (fx+ (bytevector-u8-ref bitcount-bv
(bytevector-u16-native-ref bv n))
count))))))))
(module (empty-tree full-tree tree-extract tree-for-each tree-fold-left tree-bit-set? tree-bit-set tree-bit-unset tree-bit-count tree-same? tree-merge)
; tree -> fixnum | (tree-node tree tree)
; 0 represents any tree or subtree with no bits set, and a tree or subtree
; with no bits set is always 0
(define empty-tree 0)
; any tree or subtree with all bits set
(define full-tree #t)
(define (full-fixnum size) (fxsrl (most-positive-fixnum) (fx- (fx- (fixnum-width) 1) size)))
(define compute-split
(lambda (size)
(fxsrl size 1)
; 2015/03/15 rkd: tried the following under the theory that we'd allocate
; fewer nodes. for example, say fixmun-width is 30 and size is 80. if we
; split 40/40 we create two nodes under the current node. if instead we
; split 29/51 we create just one node and one fixnum under the current
; node. this worked as planned; however, it reduced the number of nodes
; created by only 3.3% on the x86 and made compile times slightly worse.
#;(if (fx<= size (fx* (fx- (fixnum-width) 1) 3)) (fx- (fixnum-width) 1) (fxsrl size 1))))
(meta-cond
[(fx= (optimize-level) 3)
(module (make-tree-node tree-node? tree-node-left tree-node-right)
(define make-tree-node cons)
(define tree-node? pair?)
(define tree-node-left car)
(define tree-node-right cdr))]
[else
(module (make-tree-node tree-node? tree-node-left tree-node-right)
(define-record-type tree-node
(nongenerative)
(sealed #t)
(fields left right)
(protocol
(lambda (new)
(lambda (left right)
(new left right)))))
(record-writer (record-type-descriptor tree-node)
(lambda (r p wr)
(define tree-node->s-exp
(lambda (tn)
(with-virgin-quasiquote
(let ([left (tree-node-left tn)] [right (tree-node-right tn)])
`(tree-node
,(if (tree-node? left) (tree-node->s-exp left) left)
,(if (tree-node? right) (tree-node->s-exp right) right))))))
(wr (tree-node->s-exp r) p))))])
(define tree-extract ; assumes empty-tree is 0
(lambda (st size v)
(let extract ([st st] [size size] [offset 0] [x* '()])
(cond
[(fixnum? st)
(do ([st st (fxsrl st 1)]
[offset offset (fx+ offset 1)]
[x* x* (if (fxodd? st) (cons (vector-ref v offset) x*) x*)])
((fx= st 0) x*))]
[(eq? st full-tree)
(do ([size size (fx- size 1)]
[offset offset (fx+ offset 1)]
[x* x* (cons (vector-ref v offset) x*)])
((fx= size 0) x*))]
[else
(let ([split (compute-split size)])
(extract (tree-node-right st) (fx- size split) (fx+ offset split)
(extract (tree-node-left st) split offset x*)))]))))
(define tree-for-each ; assumes empty-tree is 0
(lambda (st size start end action)
(let f ([st st] [size size] [start start] [end end] [offset 0])
(cond
[(fixnum? st)
(unless (eq? st empty-tree)
(do ([st (fxbit-field st start end) (fxsrl st 1)] [offset (fx+ offset start) (fx+ offset 1)])
((fx= st 0))
(when (fxodd? st) (action offset))))]
[(eq? st full-tree)
(do ([start start (fx+ start 1)] [offset offset (fx+ offset 1)])
((fx= start end))
(action offset))]
[else
(let ([split (compute-split size)])
(when (fx< start split)
(f (tree-node-left st) split start (fxmin end split) offset))
(when (fx> end split)
(f (tree-node-right st) (fx- size split) (fxmax (fx- start split) 0) (fx- end split) (fx+ offset split))))]))))
(define tree-fold-left ; assumes empty-tree is 0
(lambda (proc size init st)
(let f ([st st] [size size] [offset 0] [init init])
(cond
[(fixnum? st)
(do ([st st (fxsrl st 1)]
[offset offset (fx+ offset 1)]
[init init (if (fxodd? st) (proc init offset) init)])
((fx= st 0) init))]
[(eq? st full-tree)
(do ([size size (fx- size 1)]
[offset offset (fx+ offset 1)]
[init init (proc init offset)])
((fx= size 0) init))]
[else
(let ([split (compute-split size)])
(f (tree-node-left st) split offset
(f (tree-node-right st) (fx- size split) (fx+ offset split) init)))]))))
(define tree-bit-set? ; assumes empty-tree is 0
(lambda (st size bit)
(let loop ([st st] [size size] [bit bit])
(cond
[(fixnum? st)
(and (not (eqv? st empty-tree))
; fxlogbit? is unnecessarily general, so roll our own
(fxlogtest st (fxsll 1 bit)))]
[(eq? st full-tree) #t]
[else
(let ([split (compute-split size)])
(if (fx< bit split)
(loop (tree-node-left st) split bit)
(loop (tree-node-right st) (fx- size split) (fx- bit split))))]))))
(define tree-bit-set ; assumes empty-tree is 0
(lambda (st size bit)
; set bit in tree. result is eq? to tr if result is same as tr.
(cond
[(eq? st full-tree) st]
[(fx< size (fixnum-width))
(let ([st (fxlogbit1 bit st)])
(if (fx= st (full-fixnum size))
full-tree
st))]
[else
(let ([split (compute-split size)])
(if (eqv? st empty-tree)
(if (fx< bit split)
(make-tree-node (tree-bit-set empty-tree split bit) empty-tree)
(make-tree-node empty-tree (tree-bit-set empty-tree (fx- size split) (fx- bit split))))
(let ([lst (tree-node-left st)] [rst (tree-node-right st)])
(if (fx< bit split)
(let ([new-lst (tree-bit-set lst split bit)])
(if (eq? new-lst lst)
st
(if (and (eq? new-lst full-tree) (eq? rst full-tree))
full-tree
(make-tree-node new-lst rst))))
(let ([new-rst (tree-bit-set rst (fx- size split) (fx- bit split))])
(if (eq? new-rst rst)
st
(if (and (eq? lst full-tree) (eq? new-rst full-tree))
full-tree
(make-tree-node lst new-rst))))))))])))
(define tree-bit-unset ; assumes empty-tree is 0
(lambda (st size bit)
; reset bit in tree. result is eq? to tr if result is same as tr.
(cond
[(fixnum? st)
(if (eqv? st empty-tree)
empty-tree
(fxlogbit0 bit st))]
[(eq? st full-tree)
(if (fx< size (fixnum-width))
(fxlogbit0 bit (full-fixnum size))
(let ([split (compute-split size)])
(if (fx< bit split)
(make-tree-node (tree-bit-unset full-tree split bit) full-tree)
(make-tree-node full-tree (tree-bit-unset full-tree (fx- size split) (fx- bit split))))))]
[else
(let ([split (compute-split size)] [lst (tree-node-left st)] [rst (tree-node-right st)])
(if (fx< bit split)
(let ([new-lst (tree-bit-unset lst split bit)])
(if (eq? new-lst lst)
st
(if (and (eq? new-lst empty-tree) (eq? rst empty-tree))
empty-tree
(make-tree-node new-lst rst))))
(let ([new-rst (tree-bit-unset rst (fx- size split) (fx- bit split))])
(if (eq? new-rst rst)
st
(if (and (eq? lst empty-tree) (eq? new-rst empty-tree))
empty-tree
(make-tree-node lst new-rst))))))])))
(define tree-bit-count ; assumes empty-tree is 0
(lambda (st size)
(cond
[(fixnum? st) (fxbit-count st)]
[(eq? st full-tree) size]
[else
(let ([split (compute-split size)])
(fx+
(tree-bit-count (tree-node-left st) split)
(tree-bit-count (tree-node-right st) (fx- size split))))])))
(define tree-same? ; assumes empty-tree is 0
(lambda (st1 st2)
(or (eq? st1 st2) ; assuming fixnums and full trees are eq-comparable
(and (tree-node? st1)
(tree-node? st2)
(tree-same? (tree-node-left st1) (tree-node-left st2))
(tree-same? (tree-node-right st1) (tree-node-right st2))))))
(define tree-merge
; merge tr1 and tr2. result is eq? to tr1 if result is same as tr1.
(lambda (st1 st2 size)
(cond
[(or (eq? st1 st2) (eq? st2 empty-tree)) st1]
[(eq? st1 empty-tree) st2]
[(or (eq? st1 full-tree) (eq? st2 full-tree)) full-tree]
[(fixnum? st1)
(safe-assert (fixnum? st2))
(let ([st (fxlogor st1 st2)])
(if (fx= st (full-fixnum size))
full-tree
st))]
[else
(let ([lst1 (tree-node-left st1)]
[rst1 (tree-node-right st1)]
[lst2 (tree-node-left st2)]
[rst2 (tree-node-right st2)])
(let ([split (compute-split size)])
(let ([l (tree-merge lst1 lst2 split)] [r (tree-merge rst1 rst2 (fx- size split))])
(cond
[(and (eq? l lst1) (eq? r rst1)) st1]
[(and (eq? l lst2) (eq? r rst2)) st2]
[(and (eq? l full-tree) (eq? r full-tree)) full-tree]
[else (make-tree-node l r)]))))]))))
(define-syntax tc-disp
(lambda (x)
(syntax-case x ()
[(_ name)
(case (datum name)
[(%ac0) (constant tc-ac0-disp)]
[(%ac1) (constant tc-ac1-disp)]
[(%sfp) (constant tc-sfp-disp)]
[(%cp) (constant tc-cp-disp)]
[(%esp) (constant tc-esp-disp)]
[(%ap) (constant tc-ap-disp)]
[(%eap) (constant tc-eap-disp)]
[(%trap) (constant tc-trap-disp)]
[(%xp) (constant tc-xp-disp)]
[(%yp) (constant tc-yp-disp)]
[else #f])])))
(define-syntax define-reserved-registers
(lambda (x)
(syntax-case x ()
[(_ [regid alias ... callee-save? mdinfo] ...)
(syntax-case #'(regid ...) (%tc %sfp) [(%tc %sfp . others) #t] [_ #f])
#'(begin
(begin
(define regid (make-reg 'regid 'mdinfo (tc-disp regid) callee-save?))
(module (alias ...) (define x regid) (define alias x) ...))
...)])))
(define-syntax define-allocable-registers
(lambda (x)
(assert (fx<= (constant asm-arg-reg-cnt) (constant asm-arg-reg-max)))
(syntax-case x ()
[(_ regvec arg-registers extra-registers with-initialized-registers [regid reg-alias ... callee-save? mdinfo] ...)
(with-syntax ([((tc-disp ...) (arg-regid ...) (extra-regid ...))
(syntax-case #'(regid ...) (%ac0 %xp %ts %td)
[(%ac0 %xp %ts %td other ...)
(let f ([other* #'(other ...)]
[rtc-disp* '()]
[arg-offset (constant tc-arg-regs-disp)]
[rextra* '()])
(if (null? other*)
(if (fx= (length rextra*) (constant asm-arg-reg-max))
(let ([extra* (reverse rextra*)])
(list
(list*
(constant tc-ac0-disp)
(constant tc-xp-disp)
(constant tc-ts-disp)
(constant tc-td-disp)
(reverse rtc-disp*))
(list-head extra* (constant asm-arg-reg-cnt))
(list-tail extra* (constant asm-arg-reg-cnt))))
(syntax-error x (format "asm-arg-reg-max extra registers are not specified ~s" (syntax->datum rextra*))))
(let ([other (car other*)])
(if (memq (syntax->datum other) '(%ac1 %yp %cp %ret))
(f (cdr other*) (cons #`(tc-disp #,other) rtc-disp*)
arg-offset rextra*)
(f (cdr other*) (cons arg-offset rtc-disp*)
(fx+ arg-offset (constant ptr-bytes)) (cons other rextra*))))))]
[_ (syntax-error x "missing or out-of-order required registers")])]
[(regid-loc ...) (generate-temporaries #'(regid ...))])
#'(begin
(define-syntax define-squawking-parameter
(syntax-rules ()
[(_ (id (... ...)) loc)
(begin
(define loc ($make-thread-parameter #f))
(define-syntax id
(lambda (q)
(unless (identifier? q) (syntax-error q))
#`(let ([x (loc)])
(unless x (syntax-error #'#,q "uninitialized"))
x)))
(... ...))]
[(_ id loc) (define-squawking-parameter (id) loc)]))
(define-squawking-parameter (regid reg-alias ...) regid-loc)
...
(define-squawking-parameter regvec regvec-loc)
(define-squawking-parameter arg-registers arg-registers-loc)
(define-squawking-parameter extra-registers extra-registers-loc)
(define-syntax with-initialized-registers
(syntax-rules ()
[(_ b1 b2 (... ...))
(parameterize ([regid-loc (make-reg 'regid 'mdinfo tc-disp callee-save?)] ...)
(parameterize ([regvec-loc (vector regid ...)]
[arg-registers-loc (list arg-regid ...)]
[extra-registers-loc (list extra-regid ...)])
(let () b1 b2 (... ...))))]))))])))
(define-syntax define-machine-dependent-registers
(lambda (x)
(syntax-case x ()
[(_ [regid alias ... callee-save? mdinfo] ...)
#'(begin
(begin
(define regid (make-reg 'regid 'mdinfo #f callee-save?))
(module (alias ...) (define x regid) (define alias x) ...))
...)])))
(define-syntax define-registers
(lambda (x)
(syntax-case x (reserved allocable machine-dependent)
[(k (reserved [rreg rreg-alias ... rreg-callee-save? rreg-mdinfo] ...)
(allocable [areg areg-alias ... areg-callee-save? areg-mdinfo] ...)
(machine-dependent [mdreg mdreg-alias ... mdreg-callee-save? mdreg-mdinfo] ...))
(with-implicit (k regvec arg-registers extra-registers real-register? with-initialized-registers)
#`(begin
(define-reserved-registers [rreg rreg-alias ... rreg-callee-save? rreg-mdinfo] ...)
(define-allocable-registers regvec arg-registers extra-registers with-initialized-registers [areg areg-alias ... areg-callee-save? areg-mdinfo] ...)
(define-machine-dependent-registers [mdreg mdreg-alias ... mdreg-callee-save? mdreg-mdinfo] ...)
(define-syntax real-register?
(with-syntax ([real-reg* #''(rreg ... rreg-alias ... ... areg ... areg-alias ... ... mdreg ... mdreg-alias ... ...)])
(syntax-rules ()
[(_ e) (memq e real-reg*)])))))])))
(architecture registers)
; pseudo register used for mref's with no actual index
(define %zero (make-reg 'zero #f #f #f))
; define %ref-ret to be sfp[0] on machines w/no ret register
(define-syntax %ref-ret
(lambda (x)
(meta-cond
[(real-register? '%ret) #'%ret]
[else (with-syntax ([%mref (datum->syntax x '%mref)])
#'(%mref ,%sfp 0))])))
(define make-Ldoargerr
(lambda ()
(make-libspec-label 'doargerr (lookup-libspec doargerr)
(reg-list %ret %ac0 %cp))))
(define make-Ldomvleterr
(lambda ()
(make-libspec-label 'domvleterr (lookup-libspec domvleterr)
(reg-list %ret %ac0))))
(define make-Lcall-error
(lambda ()
(make-libspec-label 'call-error (lookup-libspec call-error)
(reg-list %ret %cp))))
(module (frame-vars get-fv)
(define-threaded frame-vars)
(define get-fv
(lambda (x)
(let ([n (vector-length frame-vars)])
(when (fx>= x n)
(let ([new-vec (make-vector (fxmax (fx+ x 1) (fx* n 2)) #f)])
(let loop ([n n])
(unless (fx= n 0)
(let ([n (fx- n 1)])
(vector-set! new-vec n (vector-ref frame-vars n))
(loop n))))
(set! frame-vars new-vec))))
(or (vector-ref frame-vars x)
(let ([fv ($make-fv x)])
(vector-set! frame-vars x fv)
fv)))))
(define-syntax reg-cons*
(lambda (x)
(syntax-case x ()
[(_ ?reg ... ?reg*)
(fold-right
(lambda (reg reg*)
(if (real-register? (syntax->datum reg))
#`(cons #,reg #,reg*)
reg*))
#'?reg* #'(?reg ...))])))
(define-syntax reg-list
(syntax-rules ()
[(_ ?reg ...) (reg-cons* ?reg ... '())]))
(define-syntax with-saved-ret-reg
(lambda (x)
(syntax-case x ()
[(k ?e)
(if (real-register? '%ret)
(with-implicit (k %seq %mref)
#'(%seq
(set! ,(%mref ,%sfp 0) ,%ret)
,?e
(set! ,%ret ,(%mref ,%sfp 0))))
#'?e)])))
(module (restore-scheme-state save-scheme-state with-saved-scheme-state)
(define-syntax build-reg-list
; TODO: create reg records at compile time, and build these lists at compile time
; TODO: include ts & td
; TODO: specify three lists: things that need to be saved/restored via the thread context,
; things that need to be saved/restored somehow, and things that can be trashed
(lambda (x)
(syntax-case x (base-in in out)
[(_ orig-x (base-in base-inreg ...) (in inreg ...) (out outreg ...))
(let ([all* '(%ts %td %ac0 %ac1 %cp %xp %yp scheme-args extra-regs)]
[in* (datum (inreg ...))]
[out* (datum (outreg ...))])
(define remove*
(lambda (x* ls)
(if (null? x*)
ls
(remove* (cdr x*) (remq (car x*) ls)))))
(let ([bogus* (remove* all* in*)])
(unless (equal? bogus* '()) (syntax-error #'orig-x (format "bogus in registers ~s" bogus*))))
(let ([bogus* (remove* all* out*)])
(unless (equal? bogus* '()) (syntax-error #'orig-x (format "bogus out registers ~s" bogus*))))
(unless (equal? (remove* in* out*) out*)
(syntax-error #'orig-x "non-empty intersection"))
(let ([other* (remove* in* (remove* out* all*))])
(unless (null? other*)
(syntax-error #'orig-x (format "registers not mentioned: ~s" other*))))
(with-syntax ([(in ...) (datum->syntax #'*
(filter (lambda (x) (real-register? x))
(append (datum (base-inreg ...)) in*)))])
#`(cons* (ref-reg in) ...
#,(if (memq 'scheme-args in*)
(if (memq 'extra-regs in*)
#'(append arg-registers extra-registers)
#'arg-registers)
(if (memq 'extra-regs in*)
#'extra-registers
#''())))))])))
(define-syntax get-tcslot
(lambda (x)
(syntax-case x ()
[(_ k reg)
(with-implicit (k in-context %mref)
#'(in-context Lvalue
(%mref ,%tc ,(reg-tc-disp reg))))])))
(define-syntax $save-scheme-state
(lambda (x)
(syntax-case x ()
[(_ k orig-x in out)
(with-implicit (k quasiquote)
; although eap might be changed by dirty writes, and esp might be changed by
; one-shot continuation handling, we always write through to the tc so that
; we never need to save eap or esp and also so that eap, which serves as the
; base of the current dirty list, is always accurate, even when an invalid
; memory reference or invalid instruction occurs. so we leave eap and esp
; out of the save list (but not the restore list below).
#'(let ([regs-to-save (build-reg-list orig-x (base-in %sfp %ap %trap) in out)])
(fold-left (lambda (body reg)
`(seq (set! ,(get-tcslot k reg) ,reg) ,body))
`(nop) regs-to-save)))])))
(define-syntax $restore-scheme-state
(lambda (x)
(syntax-case x ()
[(_ k orig-x in out)
(with-implicit (k quasiquote)
#'(let ([regs-to-restore (build-reg-list orig-x (base-in %sfp %ap %trap %eap %esp) in out)])
(fold-left (lambda (body reg)
`(seq (set! ,reg ,(get-tcslot k reg)) ,body))
`(nop) regs-to-restore)))])))
(define-syntax save-scheme-state
(lambda (x)
(syntax-case x ()
[(k in out) #`($save-scheme-state k #,x in out)])))
(define-syntax restore-scheme-state
(lambda (x)
(syntax-case x ()
[(k in out) #`($restore-scheme-state k #,x in out)])))
(define-syntax with-saved-scheme-state
(lambda (x)
(syntax-case x ()
[(k in out ?body)
(with-implicit (k quasiquote %seq)
#`(%seq
,($save-scheme-state k #,x in out)
,?body
,($restore-scheme-state k #,x in out)))]))))
(define-record-type ctci ; compile-time version of code-info
(nongenerative)
(sealed #t)
(fields (mutable live) (mutable rpi*) (mutable closure-fv-names))
(protocol
(lambda (new)
(lambda ()
(new #f '() #f)))))
(define-record-type ctrpi ; compile-time version of rp-info
(nongenerative)
(sealed #t)
(fields label src sexpr mask))
(define-threaded next-lambda-seqno)
(define-record-type info-lambda (nongenerative)
(parent info)
(sealed #t)
(fields src sexpr libspec interface* (mutable dcl*) (mutable flags) (mutable fv*) (mutable name)
(mutable well-known?) (mutable closure-rep) ctci (mutable pinfo*) seqno)
(protocol
(lambda (pargs->new)
(define next-seqno
(lambda ()
(let ([seqno next-lambda-seqno])
(set! next-lambda-seqno (fx+ seqno 1))
seqno)))
(rec cons-info-lambda
(case-lambda
[(src sexpr libspec interface*) (cons-info-lambda src sexpr libspec interface* #f 0)]
[(src sexpr libspec interface* name) (cons-info-lambda src sexpr libspec interface* name 0)]
[(src sexpr libspec interface* name flags)
((pargs->new) src sexpr libspec interface*
(map (lambda (iface) (make-direct-call-label 'dcl)) interface*)
(if (eq? (subset-mode) 'system) (fxlogor flags (constant code-flag-system)) flags)
'() name #f 'closure (and (generate-inspector-information) (make-ctci)) '() (next-seqno))])))))
(define-record-type info-call (nongenerative)
(parent info)
(sealed #t)
(fields src sexpr (mutable check?) pariah? error?)
(protocol
(lambda (pargs->new)
(lambda (src sexpr check? pariah? error?)
((pargs->new) src sexpr check? pariah? error?)))))
(define-record-type info-newframe (nongenerative)
(parent info)
(sealed #t)
(fields
src
sexpr
cnfv*
nfv*
nfv**
(mutable weight)
(mutable call-live*)
(mutable frame-words)
(mutable local-save*))
(protocol
(lambda (pargs->new)
(lambda (src sexpr cnfv* nfv* nfv**)
((pargs->new) src sexpr cnfv* nfv* nfv** 0 #f #f #f)))))
(define-record-type info-kill* (nongenerative)
(parent info)
(fields kill*))
(define-record-type info-kill*-live* (nongenerative)
(parent info-kill*)
(fields live*)
(protocol
(lambda (new)
(case-lambda
[(kill* live*)
((new kill*) live*)]
[(kill*)
((new kill*) (reg-list))]))))
(define-record-type info-asmlib (nongenerative)
(parent info-kill*-live*)
(sealed #t)
(fields libspec save-ra?)
(protocol
(lambda (new)
(case-lambda
[(kill* libspec save-ra? live*)
((new kill* live*) libspec save-ra?)]
[(kill* libspec save-ra?)
((new kill*) libspec save-ra?)]))))
(module (intrinsic-info-asmlib intrinsic-return-live* intrinsic-entry-live* dorest-intrinsics)
; standing on our heads here to avoid referencing registers at
; load time...would be cleaner if registers were immutable,
; i.e., mutable fields (direct and inherited from var) were kept
; in separate tables...but that might add more cost to register
; allocation, which is already expensive.
(define-record-type intrinsic (nongenerative)
(sealed #t)
(fields libspec get-kill* get-live* get-rv*))
(define intrinsic-info-asmlib
(lambda (intrinsic save-ra?)
(make-info-asmlib ((intrinsic-get-kill* intrinsic))
(intrinsic-libspec intrinsic)
save-ra?
((intrinsic-get-live* intrinsic)))))
(define intrinsic-return-live*
; used a handful of times, just while compiling library.ss...don't bother optimizing
(lambda (intrinsic)
(fold-left (lambda (live* kill) (remq kill live*))
(vector->list regvec) ((intrinsic-get-kill* intrinsic)))))
(define intrinsic-entry-live*
; used a handful of times, just while compiling library.ss...don't bother optimizing
(lambda (intrinsic) ; return-live* - rv + live*
(fold-left (lambda (live* live) (if (memq live live*) live* (cons live live*)))
(fold-left (lambda (live* rv) (remq rv live*))
(intrinsic-return-live* intrinsic)
((intrinsic-get-rv* intrinsic)))
((intrinsic-get-live* intrinsic)))))
(define-syntax declare-intrinsic
(syntax-rules (unquote)
[(_ name entry-name (kill ...) (live ...) (rv ...))
(begin
(define name
(make-intrinsic
(lookup-libspec entry-name)
(lambda () (reg-list kill ...))
(lambda () (reg-list live ...))
(lambda () (reg-list rv ...))))
(export name))]))
; must include in kill ... any register explicitly assigned by the intrinsic
; plus additional registers as needed to avoid spilled unspillables. the
; list could be machine-dependent but at this point it doesn't matter.
(declare-intrinsic dofargint32 dofargint32 (%ts %td %xp) (%ac0) (%ac0))
(constant-case ptr-bits
[(32) (declare-intrinsic dofargint64 dofargint64 (%ts %td %xp) (%ac0) (%ac0 %ac1))]
[(64) (declare-intrinsic dofargint64 dofargint64 (%ts %td %xp) (%ac0) (%ac0))])
(declare-intrinsic dofretint32 dofretint32 (%ts %td %xp) (%ac0) (%ac0))
(constant-case ptr-bits
[(32) (declare-intrinsic dofretint64 dofretint64 (%ts %td %xp) (%ac0 %ac1) (%ac0))]
[(64) (declare-intrinsic dofretint64 dofretint64 (%ts %td %xp) (%ac0) (%ac0))])
(declare-intrinsic dofretuns32 dofretuns32 (%ts %td %xp) (%ac0) (%ac0))
(constant-case ptr-bits
[(32) (declare-intrinsic dofretuns64 dofretuns64 (%ts %td %xp) (%ac0 %ac1) (%ac0))]
[(64) (declare-intrinsic dofretuns64 dofretuns64 (%ts %td %xp) (%ac0) (%ac0))])
(declare-intrinsic dofretu8* dofretu8* (%ac0 %ts %td %cp %ac1) (%ac0) (%xp))
(declare-intrinsic dofretu16* dofretu16* (%ac0 %ts %td %cp %ac1) (%ac0) (%xp))
(declare-intrinsic dofretu32* dofretu32* (%ac0 %ts %td %cp %ac1) (%ac0) (%xp))
(declare-intrinsic get-room get-room () (%xp) (%xp))
(declare-intrinsic scan-remembered-set scan-remembered-set () () ())
(declare-intrinsic dooverflow dooverflow () () ())
(declare-intrinsic dooverflood dooverflood () (%xp) ())
; a dorest routine takes all of the register and frame arguments from the rest
; argument forward and also modifies the rest argument. for the rest argument,
; this is a wash (it's live both before and after). the others should also be
; listed as live. it's inconvenient and currently unnecessary to do so.
; (actually currently impossible to list the infinite set of frame arguments)
(define-syntax dorest-intrinsic-max (identifier-syntax 5))
(export dorest-intrinsic-max)
(define (list-xtail ls n)
(if (or (null? ls) (fx= n 0))
ls
(list-xtail (cdr ls) (fx1- n))))
(define dorest-intrinsics
(let ()
(define-syntax dorests
(lambda (x)
#`(vector #,@
(let f ([i 0])
(if (fx> i dorest-intrinsic-max)
'()
(cons #`(make-intrinsic
(lookup-libspec #,(construct-name #'k "dorest" i))
(lambda () (reg-list %ac0 %xp %ts %td))
(lambda () (reg-cons* %ac0 (list-xtail arg-registers #,i)))
(lambda () (let ([ls (list-xtail arg-registers #,i)]) (if (null? ls) '() (list (car ls))))))
(f (fx+ i 1))))))))
dorests)))
(define-record-type info-alloc (nongenerative)
(parent info)
(sealed #t)
(fields tag save-flrv? save-ra?))
(define-record-type info-foreign (nongenerative)
(parent info)
(sealed #t)
(fields conv* arg-type* result-type (mutable name))
(protocol
(lambda (pargs->new)
(lambda (conv* arg-type* result-type)
((pargs->new) conv* arg-type* result-type #f)))))
(define-record-type info-literal (nongenerative)
(parent info)
(sealed #t)
(fields indirect? type addr offset))
(define-record-type info-lea (nongenerative)
(parent info)
(sealed #t)
(fields offset))
(define-record-type info-load (nongenerative)
(parent info)
(sealed #t)
(fields type swapped?))
(define-record-type info-loadfl (nongenerative)
(parent info)
(sealed #t)
(fields flreg))
(define-record-type info-condition-code (nongenerative)
(parent info)
(sealed #t)
(fields type reversed? invertible?))
(define-record-type info-c-simple-call (nongenerative)
(parent info-kill*-live*)
(sealed #t)
(fields save-ra? entry)
(protocol
(lambda (new)
(case-lambda
[(save-ra? entry) ((new '() '()) save-ra? entry)]
[(live* save-ra? entry) ((new '() live*) save-ra? entry)]))))
(define-record-type info-c-return (nongenerative)
(parent info)
(sealed #t)
(fields offset))
(module ()
(record-writer (record-type-descriptor info-load)
(lambda (x p wr)
(fprintf p "#<info-load ~s>" (info-load-type x))))
(record-writer (record-type-descriptor info-lambda)
(lambda (x p wr)
(fprintf p "#<info-lambda ~s ~s ~s ~s ~s>"
(info-lambda-libspec x) (info-lambda-interface* x) (info-lambda-name x)
(info-lambda-well-known? x)
(info-lambda-fv* x))))
(record-writer (record-type-descriptor info-foreign)
(lambda (x p wr)
(fprintf p "#<info-foreign~@[ ~a~]>" (info-foreign-name x))))
(record-writer (record-type-descriptor info-literal)
(lambda (x p wr)
(fprintf p "#<literal ~s>" (info-literal-addr x))))
)
(define-pass cpnanopass : Lsrc (ir) -> L1 ()
(definitions
(define-syntax with-uvars
(syntax-rules ()
[(_ (x* id*) b1 b2 ...)
(and (identifier? #'x*) (identifier? #'id*))
(let ([uvar* (map prelex->uvar id*)] [name* (map prelex-name id*)])
(dynamic-wind
(lambda () (for-each prelex-name-set! id* uvar*))
(lambda () (let ([x* uvar*]) b1 b2 ...))
(lambda () (for-each prelex-name-set! id* name*))))]))
(define extract-uvar
(lambda (id)
(let ([x (prelex-name id)])
(unless (uvar? x)
(sorry! 'extract-uvar "~s is not a uvar" x))
x))))
(CaseLambdaExpr : Expr (ir x) -> CaseLambdaExpr ()
[(case-lambda ,preinfo (clause (,x** ...) ,interface* ,body*) ...)
(let ([info (make-info-lambda (preinfo-src preinfo) (preinfo-sexpr preinfo) (preinfo-lambda-libspec preinfo) interface*
(preinfo-lambda-name preinfo) (preinfo-lambda-flags preinfo))])
(when x (uvar-info-lambda-set! x info))
`(case-lambda ,info
,(map (lambda (x* interface body)
(with-uvars (uvar* x*)
(in-context CaseLambdaClause
`(clause (,uvar* ...) ,interface ,(Expr body)))))
x** interface* body*) ...))]
[(case-lambda ,preinfo ,cl* ...)
(sorry! who "found unreachable clause" ir)])
(Expr : Expr (ir) -> Expr ()
[(ref ,maybe-src ,x) (extract-uvar x)]
[(set! ,maybe-src ,x ,[e]) `(set! ,(extract-uvar x) ,e)]
[(case-lambda ,preinfo ,cl* ...) (CaseLambdaExpr ir #f)]
[(letrec ([,x* ,e*] ...) ,body)
(with-uvars (uvar* x*)
(let ([e* (map CaseLambdaExpr e* uvar*)])
`(letrec ([,uvar* ,e*] ...) ,(Expr body))))]
[(call ,preinfo ,e ,[e*] ...)
`(call ,(make-info-call (preinfo-src preinfo) (preinfo-sexpr preinfo) (fx< (optimize-level) 3) #f #f)
,(Expr e) ,e* ...)]
[(foreign (,conv* ...) ,name ,[e] (,arg-type* ...) ,result-type)
(let ([info (make-info-foreign conv* arg-type* result-type)])
(info-foreign-name-set! info name)
`(foreign ,info ,e))]
[(fcallable (,conv* ...) ,[e] (,arg-type* ...) ,result-type)
`(fcallable ,(make-info-foreign conv* arg-type* result-type) ,e)])
(CaseLambdaExpr ir #f))
(define find-matching-clause
(lambda (len x** interface* body* kfixed kvariable kfail)
(let f ([x** x**] [interface* interface*] [body* body*])
(if (null? interface*)
(kfail)
(let ([interface (car interface*)])
(if (fx< interface 0)
(let ([nfixed (fxlognot interface)])
(if (fx>= len nfixed)
(kvariable nfixed (car x**) (car body*))
(f (cdr x**) (cdr interface*) (cdr body*))))
(if (fx= interface len)
(kfixed (car x**) (car body*))
(f (cdr x**) (cdr interface*) (cdr body*)))))))))
(define-syntax define-$type-check
(lambda (x)
(syntax-case x ()
[(k L) (with-implicit (k $type-check)
#'(define $type-check
(lambda (mask type expr)
(with-output-language L
(cond
[(fx= type 0) (%inline log!test ,expr (immediate ,mask))]
[(= mask (constant byte-constant-mask)) (%inline eq? ,expr (immediate ,type))]
[else (%inline type-check? ,expr (immediate ,mask) (immediate ,type))])))))])))
(define-syntax %type-check
(lambda (x)
(syntax-case x ()
[(k mask type expr)
(with-implicit (k $type-check quasiquote)
#'($type-check (constant mask) (constant type) `expr))])))
(define-syntax %typed-object-check ; NB: caller must bind e
(lambda (x)
(syntax-case x ()
[(k mask type expr)
(with-implicit (k quasiquote %type-check %constant %mref)
#'`(if ,(%type-check mask-typed-object type-typed-object expr)
,(%type-check mask type
,(%mref expr ,(constant typed-object-type-disp)))
,(%constant sfalse)))])))
(define-syntax %seq
(lambda (x)
(syntax-case x ()
[(k e1 ... e2)
(with-implicit (k quasiquote)
#``#,(fold-right (lambda (x body) #`(seq #,x #,body))
#'e2 #'(e1 ...)))])))
(define-syntax %mref
(lambda (x)
(syntax-case x ()
[(k e0 e1 imm)
(with-implicit (k quasiquote)
#'`(mref e0 e1 imm))]
[(k e0 imm)
(with-implicit (k quasiquote)
#'`(mref e0 ,%zero imm))])))
(define-syntax %inline
(lambda (x)
(syntax-case x ()
[(k name e ...)
(with-implicit (k quasiquote)
#'`(inline ,null-info ,(%primitive name) e ...))])))
(define-syntax %lea
(lambda (x)
(syntax-case x ()
[(k base offset)
(with-implicit (k quasiquote)
#'`(inline ,(make-info-lea offset) ,%lea1 base))]
[(k base index offset)
(with-implicit (k quasiquote)
#'`(inline ,(make-info-lea offset) ,%lea2 base index))])))
(define-syntax %constant
(lambda (x)
(syntax-case x ()
[(k x)
(with-implicit (k quasiquote)
#'`(immediate ,(constant x)))])))
(define-syntax %tc-ref
(lambda (x)
(define-who field-type
(lambda (struct field)
(cond
[(assq field (getprop struct '*fields* '())) =>
(lambda (a)
(apply
(lambda (field type disp len) type)
a))]
[else ($oops who "undefined field ~s-~s" struct field)])))
(syntax-case x ()
[(k field) #'(k ,%tc field)]
[(k e-tc field)
(if (memq (field-type 'tc (datum field)) '(ptr void* uptr iptr))
(with-implicit (k %mref)
#`(%mref e-tc
#,(lookup-constant
(string->symbol
(format "tc-~a-disp" (datum field))))))
(syntax-error x "non-ptr-size tc field"))])))
(define-syntax %constant-alloc
(lambda (x)
(syntax-case x ()
[(k tag size) #'(k tag size #f #f)]
[(k tag size save-flrv?) #'(k tag size save-flrv? #f)]
[(k tag size save-flrv? save-asm-ra?)
(with-implicit (k quasiquote)
#'`(alloc
,(make-info-alloc (constant tag) save-flrv? save-asm-ra?)
(immediate ,(c-alloc-align size))))])))
(define-pass np-recognize-let : L1 (ir) -> L2 ()
(definitions
(define seqs-and-profiles?
(lambda (e)
(nanopass-case (L1 Expr) e
[(profile ,src) #t]
[(seq ,e1 ,e2) (and (seqs-and-profiles? e1) (seqs-and-profiles? e2))]
[else #f])))
(define Profile
(lambda (e)
(let f ([e e] [profile* '()])
(nanopass-case (L1 Expr) e
[(seq ,e1 ,e2)
(guard (seqs-and-profiles? e1))
(f e2 (cons e1 profile*))]
[else (values e profile*)]))))
(define build-seq (lambda (e1 e2) (with-output-language (L2 Expr) `(seq ,(Expr e1) ,e2))))
(define build-seq* (lambda (e* e) (fold-right build-seq e e*))))
(Expr : Expr (ir) -> Expr ()
[(call ,info1 ,[Profile : e profile1*] ,[e*] ...)
(nanopass-case (L1 Expr) e
[(case-lambda ,info2 (clause (,x* ...) ,interface ,[Expr : body]))
(guard (fx= (length e*) interface))
(build-seq* profile1* `(let ([,x* ,e*] ...) ,body))]
[(letrec ([,x1 ,[Expr : le*]]) ,[Profile : body profile2*])
; can't use a guard, since body isn't bound in guard.
(if (eq? body x1)
(build-seq* profile1*
(build-seq* profile2*
`(letrec ([,x1 ,le*]) (call ,info1 ,x1 ,e* ...))))
`(call ,info1 ,(build-seq* profile1* (Expr e)) ,e* ...))]
[else
`(call ,info1 ,(build-seq* profile1* (Expr e)) ,e* ...)])]))
(define-pass np-discover-names : L2 (ir) -> L3 ()
(definitions
(define ->name
(lambda (x)
(cond
[(uvar? x) (->name (uvar-name x))]
[(string? x) x]
[(symbol? x)
(let ([name ($symbol-name x)])
(if (pair? name) (cdr name) name))]
[(eq? #f x) #f]
[else (error 'np-discover-names "x is not a name" x)]))))
(Expr : Expr (ir name moi) -> Expr ()
[(letrec ([,x* ,le*] ...) ,[body])
(let ([le* (map (lambda (le x) (CaseLambdaExpr le (->name x) moi)) le* x*)])
`(letrec ([,x* ,le*] ...) ,body))]
[(let ([,x* ,e*] ...) ,[body])
(let ([e* (map (lambda (e x) (Expr e (->name x) moi)) e* x*)])
`(let ([,x* ,e*] ...) ,body))]
; handle top-level set! (i.e. $set-top-level-value)
[(call ,info ,pr (quote ,d) ,e0)
(guard (and (eq? (primref-name pr) '$set-top-level-value!) (symbol? d)))
(let ([e0 (Expr e0 (->name d) moi)])
`(call ,info ,pr (quote ,d) ,e0))]
[(call ,info ,[e0 #f moi -> e0] ,[e1* #f moi -> e1*] ...)
`(call ,info ,e0 ,e1* ...)]
[(if ,[e0 #f moi -> e0] ,[e1] ,[e2])
`(if ,e0 ,e1 ,e2)]
[(seq ,[e0 #f moi -> e0] ,[e1])
`(seq ,e0 ,e1)]
[(foreign ,info ,[e #f moi -> e])
(when name (info-foreign-name-set! info name))
`(foreign ,info ,e)]
[(fcallable ,info ,[e #f moi -> e])
(info-foreign-name-set! info name)
`(fcallable ,info ,e)]
[(set! ,x ,e0)
(let ([e0 (Expr e0 (->name x) moi)]) `(set! ,x ,e0))]
[(moi) `(quote ,moi)])
(CaseLambdaExpr : CaseLambdaExpr (ir [name #f] [moi #f]) -> CaseLambdaExpr ()
[(case-lambda ,info ,[cl #f name -> cl] ...)
(unless (info-lambda-name info) (info-lambda-name-set! info name))
`(case-lambda ,info ,cl ...)])
(CaseLambdaClause : CaseLambdaClause (ir name moi) -> CaseLambdaClause ()))
(define-pass np-convert-assignments : L3 (ir) -> L4 ()
(definitions
(define-syntax %primcall
(lambda (x)
(syntax-case x ()
[(k src sexpr prim arg ...)
(identifier? #'prim)
(with-implicit (k quasiquote)
#``(call ,(make-info-call src sexpr #f #f #f)
,(lookup-primref 3 'prim)
arg ...))])))
(define unbound-object ($unbound-object))
(define partition-assigned
(lambda (x*)
(if (null? x*)
(values '() '() '())
(let ([x (car x*)] [x* (cdr x*)])
(let-values ([(x* t* a*) (partition-assigned x*)])
(if (uvar-assigned? x)
(let ([t (make-tmp 't)])
(uvar-assigned! x #f)
(values (cons t x*) (cons t t*) (cons x a*)))
(values (cons x x*) t* a*)))))))
(define handle-assigned
(lambda (x* body k)
(let-values ([(x* t* a*) (partition-assigned x*)])
(k x* (if (null? a*)
body
(with-output-language (L4 Expr)
`(let ([,a* ,(map (lambda (t) (%primcall #f #f cons ,t (quote ,unbound-object))) t*)] ...)
,body))))))))
(Expr : Expr (ir) -> Expr ()
[,x (if (uvar-assigned? x) (%primcall #f #f car ,x) x)]
[(set! ,x ,[e]) (%primcall #f #f set-car! ,x ,e)]
[(let ([,x* ,[e*]] ...) ,[body])
(handle-assigned x* body
(lambda (x* body)
`(let ([,x* ,e*] ...) ,body)))])
(CaseLambdaClause : CaseLambdaClause (ir) -> CaseLambdaClause ()
[(clause (,x* ...) ,interface ,[body])
(handle-assigned x* body
(lambda (x* body)
`(clause (,x* ...) ,interface ,body)))]))
; for use only after mdcl field has been added to the call syntax
(define-syntax %primcall
(lambda (x)
(syntax-case x ()
[(k src sexpr prim arg ...)
(identifier? #'prim)
(with-implicit (k quasiquote)
#``(call ,(make-info-call src sexpr #f #f #f) #f
,(lookup-primref 3 'prim)
arg ...))])))
(define-pass np-sanitize-bindings : L4 (ir) -> L4 ()
; must come before suppress-procedure-checks and recognize-mrvs
; since it sets up uvar-info-lambda, but after convert-assignments
(definitions
(define maybe-build-let
(lambda (x* e* body)
(if (null? x*)
body
(with-output-language (L4 Expr)
`(let ([,x* ,e*] ...) ,body)))))
(define maybe-build-letrec
(lambda (x* e* body)
(if (null? x*)
body
(with-output-language (L4 Expr)
`(letrec ([,x* ,e*] ...) ,body))))))
(Expr : Expr (ir) -> Expr ()
[(let ([,x* ,[e*]] ...) ,[body])
(with-values
(let f ([x* x*] [e* e*])
(if (null? x*)
(values '() '() '() '())
(let-values ([(ex* ee* lx* le*) (f (cdr x*) (cdr e*))])
(nanopass-case (L4 Expr) (car e*)
[(case-lambda ,info ,cl ...)
(uvar-info-lambda-set! (car x*) info)
(values ex* ee* (cons (car x*) lx*) (cons (car e*) le*))]
[else (values (cons (car x*) ex*) (cons (car e*) ee*) lx* le*)]))))
(lambda (ex* ee* lx* le*)
(maybe-build-let ex* ee*
(maybe-build-letrec lx* le*
body))))]))
(define-pass np-suppress-procedure-checks : L4 (ir) -> L4 ()
; N.B. check must be done after e and e* have been evaluated, so we attach
; a flag to the call syntax rather than introducing explicit checks.
; if we could introduce explicit checks instead, we could avoid doing
; so along some branches of an if in call context, even if others
; need the check. c'est la vie.
(Proc : Expr (ir) -> * (#f)
[,x (uvar-info-lambda x)]
[(quote ,d) (procedure? d)]
[,pr #t]
[(seq ,[] ,[* suppress?]) suppress?]
[(if ,[] ,[* suppress1?] ,[* suppress2?]) (and suppress1? suppress2?)]
[(letrec ([,x* ,[]] ...) ,[* suppress?]) suppress?]
[(let ([,x* ,[]] ...) ,[* suppress?]) suppress?]
[(case-lambda ,info ,[] ...) #t]
[else #f])
(CaseLambdaExpr : CaseLambdaExpr (ir) -> * ()
[(case-lambda ,info ,[] ...) (values)])
(CaseLambdaClause : CaseLambdaClause (ir) -> * ()
[(clause (,x* ...) ,interface ,[]) (values)])
; NB: explicitly handling every form because the nanopass infrastructure can't autofill when the output is *
(Expr : Expr (ir) -> * ()
[,x (values)]
[(quote ,d) (values)]
[(case-lambda ,info ,[] ...) (values)]
[(call ,info0
(call ,info1 ,pr (quote ,d))
,[] ...)
(guard (and (eq? (primref-name pr) '$top-level-value) (symbol? d)))
(info-call-check?-set! info0 #f)
(info-call-check?-set! info1 #f)
(values)]
[(call ,info ,[* suppress?] ,[] ...)
(when suppress? (info-call-check?-set! info #f))
(values)]
[(if ,[] ,[] ,[]) (values)]
[(seq ,[] ,[]) (values)]
[,pr (values)]
[(let ([,x ,[]] ...) ,[]) (values)]
[(letrec ([,x ,[]] ...) ,[]) (values)]
[(foreign ,info ,[]) (values)]
[(fcallable ,info ,[]) (values)]
[(profile ,src) (values)]
[(pariah) (values)]
[else (sorry! who "unhandled expression ~s" ir)])
(begin (CaseLambdaExpr ir) ir))
(define-pass np-recognize-mrvs : L4 (ir) -> L4.5 ()
(definitions
(define insert-procedure-check
(lambda (check? tmp e)
(with-output-language (L4.5 Expr)
(if check?
`(seq
(if ,(%primcall #f #f procedure? ,tmp)
(quote ,(void))
,(%primcall #f #f $oops (quote #f) (quote "attempt to apply non-procedure ~s") ,tmp))
,e)
e)))))
(Expr : Expr (ir) -> Expr ()
[(call ,info ,pr ,e1 ,e2)
(guard (eq? (primref-name pr) 'call-with-values))
(let ([check? (not (all-set? (prim-mask unsafe) (primref-flags pr)))])
(Producer e1 check? (info-call-src info) (info-call-sexpr info)
(lambda (e1 src sexpr)
(Consumer e2 e1 check? src sexpr))))]
[(call ,info ,[e] ,[e*] ...) `(call ,info #f ,e ,e* ...)])
(Producer : Expr (ir check? src sexpr k) -> Expr ()
[,x (k `(call ,(make-info-call src sexpr check? #f #f) #f ,x) src sexpr)]
[(case-lambda ,info (clause (,x** ...) ,interface* ,body*) ...)
(find-matching-clause 0 x** interface* body*
(lambda (x* body) (k (Expr body) src sexpr))
(lambda (nfixed x* body) `(let ([,(car x*) (quote ())]) ,(k (Expr body) src sexpr)))
(lambda ()
(let ([tmp (make-tmp 'tp)])
(uvar-info-lambda-set! tmp info)
`(letrec ([,tmp ,(Expr ir)])
,(k tmp src sexpr)))))]
[(seq ,[Expr : e1] ,[Producer : e2]) `(seq ,e1 ,e2)]
[(let ([,x* ,[Expr : e*]] ...) ,[Producer : e]) `(let ([,x* ,e*] ...) ,e)]
[(letrec ([,x* ,[le*]] ...) ,[Producer : e]) `(letrec ([,x* ,le*] ...) ,e)]
[,pr (k `(call ,(make-info-call src sexpr #f #f #f) #f ,pr) src sexpr)]
[else (let ([tmp (make-tmp 'tp)])
; force last part of producer to be evaluated before consumer, to
; avoid interleaved evaluation of producer and consumer
`(let ([,tmp ,(Expr ir)])
,(k `(call ,(make-info-call #f #f check? #f #f) #f ,tmp) src sexpr)))])
(Consumer : Expr (ir producer-or check? src sexpr) -> Expr ()
; generate same code for single-value let-values as for let
[(case-lambda ,info (clause (,x) ,interface ,[Expr : body]))
(guard (= interface 1))
`(let ([,x ,producer-or]) ,body)]
[(case-lambda ,info (clause (,x** ...) ,interface* ,[Expr : body*]) ...)
`(mvlet ,producer-or ((,x** ...) ,interface* ,body*) ...)]
[,x (cond
[(uvar-info-lambda x) =>
(lambda (info)
(define make-tmps
(lambda (n)
(do ([n (if (fx< n 0) (fx- n) n) (fx- n 1)]
[tmp* '() (cons (make-tmp 't) tmp*)])
((fx= n 0) tmp*))))
(let ([interface* (info-lambda-interface* info)])
(let ([info* (map (lambda (dcl) (make-info-call src sexpr #f #f #f)) (info-lambda-dcl* info))]
[x* (make-list (length interface*) x)]
[x** (map make-tmps interface*)])
`(mvlet ,producer-or
((,x** ...) ,interface* (call ,info* ,(info-lambda-dcl* info) ,x* ,x** ...))
...))))]
[else (insert-procedure-check check? x `(mvcall ,(make-info-call src sexpr #f #f #f) ,producer-or ,x))])]
[(seq ,[Expr : e1] ,[Consumer : e2]) `(seq ,e1 ,e2)]
[(let ([,x* ,[Expr : e*]] ...) ,[Consumer : e]) `(let ([,x* ,e*] ...) ,e)]
[(letrec ([,x* ,[le*]] ...) ,[Consumer : e]) `(letrec ([,x* ,le*] ...) ,e)]
[,pr `(mvcall ,(make-info-call src sexpr #f #f #f) ,producer-or ,pr)]
[(quote ,d) (guard (procedure? d)) `(mvcall ,(make-info-call src sexpr #f #f #f) ,producer-or (quote ,d))]
[else (let ([tmp (make-tmp 'tc)])
; force consumer expression to be evaluated before producer body
; this includes references to top-level variables: since they can
; be altered by the producer, we can use a pvalue call
`(let ([,tmp ,(Expr ir)])
,(insert-procedure-check check? tmp
`(mvcall ,(make-info-call src sexpr #f #f #f) ,producer-or ,tmp))))]))
(define-pass np-expand-foreign : L4.5 (ir) -> L4.75 ()
(Expr : Expr (ir) -> Expr ()
[(foreign ,info ,[e])
(let ([iface (length (info-foreign-arg-type* info))]
[t (make-tmp 'tentry 'uptr)]
[t* (map (lambda (x) (make-tmp 't)) (info-foreign-arg-type* info))])
(let ([lambda-info (make-info-lambda #f #f #f (list iface) (info-foreign-name info))])
`(let ([,t ,e])
(case-lambda ,lambda-info
(clause (,t* ...) ,iface
(foreign-call ,info ,t ,t* ...))))))]
[(fcallable ,info ,[e])
(%primcall #f #f $instantiate-code-object
(fcallable ,info)
(quote 0) ; hard-wiring "cookie" to 0
,e)]))
(define-pass np-recognize-loops : L4.75 (ir) -> L4.875 ()
; TODO: also recognize andmap/for-all, ormap/exists, for-each
; and remove inline handlers
(definitions
(define make-assigned-tmp
(lambda (x)
(let ([t (make-tmp 'tloop)])
(uvar-assigned! t #t)
t))))
(Expr : Expr (ir [tail* '()]) -> Expr ()
[,x (uvar-referenced! x #t) (uvar-loop! x #f) x]
[(letrec ([,x1 (case-lambda ,info1
(clause (,x* ...) ,interface
,body))])
(call ,info2 ,mdcl ,x2 ,e* ...))
(guard (eq? x2 x1) (eq? (length e*) interface))
(uvar-referenced! x1 #f)
(uvar-loop! x1 #t)
(let ([tref?* (map uvar-referenced? tail*)])
(for-each (lambda (x) (uvar-referenced! x #f)) tail*)
(let ([e* (map (lambda (e) (Expr e '())) e*)]
[body (Expr body (cons x1 tail*))])
(let ([body-tref?* (map uvar-referenced? tail*)])
(for-each (lambda (x tref?) (when tref? (uvar-referenced! x #t))) tail* tref?*)
(if (uvar-referenced? x1)
(if (uvar-loop? x1)
(let ([t* (map make-assigned-tmp x*)])
`(let ([,t* ,e*] ...)
(loop ,x1 (,t* ...)
(let ([,x* ,t*] ...)
,body))))
(begin
(for-each (lambda (x body-tref?)
(when body-tref? (uvar-loop! x #f)))
tail* body-tref?*)
`(letrec ([,x1 (case-lambda ,info1
(clause (,x* ...) ,interface
,body))])
(call ,info2 ,mdcl ,x2 ,e* ...))))
`(let ([,x* ,e*] ...) ,body)))))]
[(letrec ([,x* ,[le*]] ...) ,[body])
`(letrec ([,x* ,le*] ...) ,body)]
[(call ,info ,mdcl ,x ,[e* '() -> e*] ...)
(guard (memq x tail*))
(uvar-referenced! x #t)
(let ([interface* (info-lambda-interface* (uvar-info-lambda x))])
(unless (and (fx= (length interface*) 1) (fx= (length e*) (car interface*)))
(uvar-loop! x #f)))
`(call ,info ,mdcl ,x ,e* ...)]
[(call ,info ,mdcl ,[e '() -> e] ,[e* '() -> e*] ...)
`(call ,info ,mdcl ,e ,e* ...)]
[(foreign-call ,info ,[e '() -> e] ,[e* '() -> e*] ...)
`(foreign-call ,info ,e ,e* ...)]
[(fcallable ,info) `(fcallable ,info)]
[(label ,l ,[body]) `(label ,l ,body)]
[(mvlet ,[e '() -> e] ((,x** ...) ,interface* ,[body*]) ...)
`(mvlet ,e ((,x** ...) ,interface* ,body*) ...)]
[(mvcall ,info ,[e1 '() -> e1] ,[e2 '() -> e2])
`(mvcall ,info ,e1 ,e2)]
[(let ([,x ,[e* '() -> e*]] ...) ,[body])
`(let ([,x ,e*] ...) ,body)]
[(case-lambda ,info ,[cl] ...) `(case-lambda ,info ,cl ...)]
[(quote ,d) `(quote ,d)]
[(if ,[e0 '() -> e0] ,[e1] ,[e2]) `(if ,e0 ,e1 ,e2)]
[(seq ,[e0 '() -> e0] ,[e1]) `(seq ,e0 ,e1)]
[(profile ,src) `(profile ,src)]
[(pariah) `(pariah)]
[,pr pr]
[else ($oops who "unexpected Expr ~s" ir)]))
(define-pass np-name-anonymous-lambda : L4.875 (ir) -> L5 ()
(CaseLambdaClause : CaseLambdaClause (ir) -> CaseLambdaClause ())
(Expr : Expr (ir) -> Expr ()
[(case-lambda ,info ,[cl] ...)
(let ([anon (make-tmp (or (let ([name (info-lambda-name info)])
(and name (string->symbol name)))
'anon))])
(uvar-info-lambda-set! anon info)
`(letrec ([,anon (case-lambda ,info ,cl ...)])
,anon))])
(nanopass-case (L4.875 CaseLambdaExpr) ir
[(case-lambda ,info ,[CaseLambdaClause : cl] ...) `(case-lambda ,info ,cl ...)]))
(define-pass np-convert-closures : L5 (x) -> L6 ()
(definitions
(define-record-type clinfo
(nongenerative)
(sealed #t)
(fields lid (mutable mask) (mutable fv*))
(protocol (lambda (n) (lambda (index) (n index 0 '())))))
(module (with-offsets)
(define set-offsets!
(lambda (x* index)
(do ([x* x* (cdr x*)] [index index (fx+ index 1)])
((null? x*) index)
(var-index-set! (car x*) index))))
(define-syntax with-offsets
(syntax-rules ()
[(_ index ?x* ?e1 ?e2 ...)
(identifier? #'index)
(let ([x* ?x*])
(let ([index (set-offsets! x* index)])
(let ([v (begin ?e1 ?e2 ...)])
(for-each (lambda (x) (var-index-set! x #f)) x*)
v)))])))
(define record-ref!
(lambda (x clinfo)
(let ([index (var-index x)])
(unless index (sorry! who "variable ~a lost its binding" x))
(when (fx< index (clinfo-lid clinfo))
(let ([mask (clinfo-mask clinfo)])
(unless (bitwise-bit-set? mask index)
(clinfo-mask-set! clinfo (bitwise-copy-bit mask index 1))
(clinfo-fv*-set! clinfo (cons x (clinfo-fv* clinfo))))))))))
(Expr : Expr (ir index clinfo) -> Expr ()
[,x (record-ref! x clinfo) x]
[(letrec ([,x* ,le*] ...) ,body)
(with-offsets index x*
(let loop ([le* le*] [rle* '()] [rfv** '()])
(if (null? le*)
`(closures ([,x* (,(reverse rfv**) ...) ,(reverse rle*)] ...)
,(Expr body index clinfo))
(let-values ([(le fv*) (CaseLambdaExpr (car le*) index clinfo)])
(loop (cdr le*) (cons le rle*) (cons fv* rfv**))))))]
[(let ([,x* ,[e*]] ...) ,body)
(with-offsets index x*
`(let ([,x* ,e*] ...) ,(Expr body index clinfo)))]
[(mvlet ,[e] ((,x** ...) ,interface* ,body*) ...)
`(mvlet ,e
((,x** ...)
,interface*
,(let f ([x** x**] [body* body*])
(if (null? x**)
'()
(cons
(with-offsets index (car x**)
(Expr (car body*) index clinfo))
(f (cdr x**) (cdr body*))))))
...)]
[(loop ,x (,x* ...) ,body)
(with-offsets index (cons x x*)
`(loop ,x (,x* ...) ,(Expr body index clinfo)))])
(CaseLambdaExpr : CaseLambdaExpr (ir index outer-clinfo) -> CaseLambdaExpr ()
[(case-lambda ,info ,cl* ...)
(let ([clinfo (make-clinfo index)])
(let ([cl* (map (lambda (cl) (CaseLambdaClause cl index clinfo)) cl*)])
(let ([fv* (clinfo-fv* clinfo)])
(for-each (lambda (x) (record-ref! x outer-clinfo)) fv*)
(values
`(case-lambda ,info ,cl* ...)
fv*))))])
(CaseLambdaClause : CaseLambdaClause (ir index parent-clinfo) -> CaseLambdaClause ()
[(clause (,x* ...) ,interface ,body)
(let ([clinfo (make-clinfo index)])
(with-offsets index x*
(let ([body (Expr body index clinfo)])
(let ([fv* (clinfo-fv* clinfo)])
(for-each (lambda (x) (record-ref! x parent-clinfo)) fv*)
`(clause (,x* ...) ,(if (null? fv*) #f (make-cpvar)) ,interface ,body)))))])
(let-values ([(le fv*) (CaseLambdaExpr x 0 (make-clinfo 0))])
(unless (null? fv*) (sorry! who "found unbound variables ~s" fv*))
le))
(define-pass np-optimize-direct-call : L6 (ir) -> L6 ()
(definitions
(define find-matching-clause
(lambda (len info kfixed kvariable kfail)
(if info
(let f ([interface* (info-lambda-interface* info)] [dcl* (info-lambda-dcl* info)])
(if (null? interface*)
(kfail)
(let ([interface (car interface*)])
(if (fx< interface 0)
(let ([nfixed (fxlognot interface)])
(if (fx>= len nfixed)
(kvariable nfixed (car dcl*))
(f (cdr interface*) (cdr dcl*))))
(if (fx= interface len)
(kfixed (car dcl*))
(f (cdr interface*) (cdr dcl*)))))))
(kfail)))))
(CaseLambdaExpr1 : CaseLambdaExpr (ir) -> * ()
[(case-lambda ,info ,cl* ...)
(info-lambda-well-known?-set! info #t)])
(CaseLambdaExpr2 : CaseLambdaExpr (ir) -> CaseLambdaExpr ())
(Expr : Expr (ir) -> Expr ()
[,x (let ([info (uvar-info-lambda x)])
(when info (info-lambda-well-known?-set! info #f))
x)]
[(closures ([,x* (,x** ...) ,le*] ...) ,body)
(for-each CaseLambdaExpr1 le*)
`(closures ([,x* (,x** ...) ,(map CaseLambdaExpr2 le*)] ...) ,(Expr body))]
[(loop ,x (,x* ...) ,body)
(uvar-location-set! x 'loop)
(let ([body (Expr body)])
(uvar-location-set! x #f)
`(loop ,x (,x* ...) ,body))]
[(call ,info ,mdcl ,x ,[e*] ...)
(guard (not (eq? (uvar-location x) 'loop)))
(if mdcl
(begin
; already a direct-call produced, e.g., by recognize-mrvs
(direct-call-label-referenced-set! mdcl #t)
`(call ,info ,mdcl ,x ,e* ...))
(find-matching-clause (length e*) (uvar-info-lambda x)
(lambda (dcl)
(direct-call-label-referenced-set! dcl #t)
`(call ,info ,dcl ,x ,e* ...))
(lambda (nfixed dcl)
(direct-call-label-referenced-set! dcl #t)
(let ([fixed-e* (list-head e* nfixed)] [rest-e* (list-tail e* nfixed)])
(let ([t* (map (lambda (x) (make-tmp 't)) fixed-e*)])
; evaluate fixed-e* first, before the rest list is created. rest-e* should
; be evaluated before as well assuming later passes handle calls correctly
`(let ([,t* ,fixed-e*] ...)
(call ,info ,dcl ,x ,t* ...
,(%primcall #f #f list ,rest-e* ...))))))
(lambda () `(call ,info #f ,(Expr x) ,e* ...))))])
(CaseLambdaExpr2 ir))
; this pass doesn't change the language, but it does add an extragrammatical
; restriction: each letrec is now strongly connected
(define-pass np-identify-scc : L6 (ir) -> L6 ()
(definitions
; returns a list of lists of strongly connected bindings sorted so that
; if a binding in some list binding1* binds a variable x that is in the
; free list of a binding in some other list binding2*, binding1* comes
; before binding2*.
(define-record-type binding
(fields le x x* (mutable link*) (mutable root) (mutable done))
(nongenerative)
(sealed #t)
(protocol
(lambda (new)
(lambda (le x x*)
(let ([b (new le x x* '() #f #f)])
(uvar-location-set! x b)
b)))))
(define (compute-sccs v*) ; Tarjan's algorithm
; adapted from cpletrec
(define scc* '())
(define (compute-sccs v)
(define index 0)
(define stack '())
(define (tarjan v)
(let ([v-index index])
(binding-root-set! v v-index)
(set! stack (cons v stack))
(set! index (fx+ index 1))
(for-each
(lambda (v^)
(unless (binding-done v^)
(unless (binding-root v^) (tarjan v^))
(binding-root-set! v (fxmin (binding-root v) (binding-root v^)))))
(binding-link* v))
(when (fx= (binding-root v) v-index)
(set! scc*
(cons
(let f ([ls stack])
(let ([v^ (car ls)])
(binding-done-set! v^ #t)
(cons v^ (if (eq? v^ v)
(begin (set! stack (cdr ls)) '())
(f (cdr ls))))))
scc*)))))
(tarjan v))
(for-each (lambda (v) (unless (binding-done v) (compute-sccs v))) v*)
(reverse scc*)))
(Expr : Expr (ir) -> Expr ()
[(closures ([,x* (,x** ...) ,[le*]] ...) ,[body])
; create bindings and set each uvar's location to the corresponding binding
(let ([b* (map make-binding le* x* x**)])
; establish links from each binding to the bindings of its free variables
(for-each
(lambda (b)
(binding-link*-set! b
(fold-left
(lambda (link* x)
(let ([loc (uvar-location x)])
(if (binding? loc)
(cons loc link*)
link*)))
'() (binding-x* b))))
b*)
; reset uvar locations
(for-each (lambda (b) (uvar-location-set! (binding-x b) #f)) b*)
; sort bindings into strongly connected components, then
; create one closure for each not-well-known binding,
; and one for all well-known bindings
(let f ([b** (compute-sccs b*)])
(if (null? b**)
body
(let ([b* (car b**)])
`(closures ([,(map binding-x b*) (,(map binding-x* b*) ...) ,(map binding-le b*)] ...)
,(f (cdr b**)))))))]))
(module (np-expand-closures np-expand/optimize-closures)
(define sort-bindings
; sort-bindings uses the otherwise unneeded info-lambda-seqno to put labels
; bindings in the same order whether we run np-expand/optimize-closures or
; just np-expand-closures, thus reducing code/icache layout differences and,
; when there are few other differences, eliminating spurious differences
; in run times. ultimately, we should try laying code objects out
; in some order that minimizes cache misses, whether at compile,
; load, or collection time.
(lambda (l* le*)
(define seqno
(lambda (p)
(let ([le (cdr p)])
(nanopass-case (L7 CaseLambdaExpr) le
[(case-lambda ,info ,cl* ...) (info-lambda-seqno info)]
[else 0]))))
(let ([ls (sort (lambda (x y) (< (seqno x) (seqno y))) (map cons l* le*))])
(values (map car ls) (map cdr ls)))))
(define-pass np-expand-closures : L6 (ir) -> L7 ()
(definitions
(define gl* '())
(define gle* '())
(define-record-type closure
(nongenerative)
(sealed #t)
(fields name label (mutable free*)))
(define-syntax with-uvar-location
(syntax-rules ()
[(_ ?uvar ?expr ?e)
(let ([uvar ?uvar])
(let ([old (uvar-location uvar)])
(uvar-location-set! uvar ?expr)
(let ([v ?e])
(uvar-location-set! uvar old)
v)))]))
(with-output-language (L7 Expr)
(define with-locations
(lambda (free* mcp body)
(if mcp
(let f ([free* free*] [i (constant closure-data-disp)])
(if (null? free*)
(Expr body)
(with-uvar-location (car free*) (%mref ,mcp ,i)
(f (cdr free*) (fx+ i (constant ptr-bytes))))))
(Expr body))))
(module (create-bindings create-inits)
(define (build-free-ref x) (or (uvar-location x) x))
(define create-bindings
(lambda (c* body)
(fold-right
(lambda (c body)
`(let ([,(closure-name c) ,(%constant-alloc type-closure
(fx* (fx+ (length (closure-free* c)) 1) (constant ptr-bytes)))])
,(%seq
(set! ,(%mref ,(closure-name c) ,(constant closure-code-disp))
(label-ref ,(closure-label c) ,(constant code-data-disp)))
,body)))
body
c*)))
(define create-inits
(lambda (c* body)
(fold-right
(lambda (c body)
(let f ([x* (closure-free* c)] [i (constant closure-data-disp)])
(if (null? x*)
body
(%seq
(set! ,(%mref ,(closure-name c) ,i) ,(build-free-ref (car x*)))
,(f (cdr x*) (fx+ i (constant ptr-bytes)))))))
body c*))))))
(CaseLambdaExpr : CaseLambdaExpr (ir c) -> CaseLambdaExpr ()
[(case-lambda ,info ,[cl*] ...)
(info-lambda-fv*-set! info (closure-free* c))
(info-lambda-closure-rep-set! info 'closure)
`(case-lambda ,info ,cl* ...)])
(CaseLambdaClause : CaseLambdaClause (ir c) -> CaseLambdaClause ()
[(clause (,x* ...) ,mcp ,interface ,body)
`(clause (,x* ...) ,mcp ,interface
,(with-locations (if c (closure-free* c) '()) mcp body))])
(Expr : Expr (ir) -> Expr ()
[(closures ([,x* (,x** ...) ,le*] ...) ,body)
(let* ([l* (map (lambda (x) (make-local-label (uvar-name x))) x*)]
[c* (map make-closure x* l* x**)])
(let ([le* (map CaseLambdaExpr le* c*)] [body (Expr body)])
(set! gl* (append l* gl*))
(set! gle* (append le* gle*))
(create-bindings c* (create-inits c* body))))]
[,x (or (uvar-location x) x)]
[(fcallable ,info)
(let ([label (make-local-label 'fcallable)])
(set! gl* (cons label gl*))
(set! gle* (cons (in-context CaseLambdaExpr `(fcallable ,info ,label)) gle*))
`(label-ref ,label 0))])
(nanopass-case (L6 CaseLambdaExpr) ir
[(case-lambda ,info ,[CaseLambdaClause : cl #f -> cl] ...)
(let ([l (make-local-label 'main)])
(let-values ([(gl* gle*) (sort-bindings gl* gle*)])
`(labels ([,gl* ,gle*] ... [,l (case-lambda ,info ,cl ...)]) ,l)))]))
(define-pass np-expand/optimize-closures : L6 (ir) -> L7 ()
(definitions
(module (add-original-closures! add-final-closures!
add-ref-counter add-create-and-alloc-counters
add-raw-counters with-raw-closure-ref-counter
with-was-closure-ref)
(include "types.ss")
(define add-create-and-alloc-counters
(lambda (c* e)
(if (track-dynamic-closure-counts)
(let f ([c* c*] [pair-count 0] [vector-count 0] [closure-count 0]
[vector-alloc-amount 0] [closure-alloc-amount 0]
[padded-vector-alloc-amount 0] [padded-closure-alloc-amount 0])
(if (null? c*)
(add-counter '#{pair-create-count bhowt6w0coxl0s2y-5} pair-count
(add-counter '#{vector-create-count bhowt6w0coxl0s2y-6} vector-count
(add-counter '#{closure-create-count bhowt6w0coxl0s2y-7} closure-count
(add-counter '#{vector-alloc-count bhowt6w0coxl0s2y-8} vector-alloc-amount
(add-counter '#{closure-alloc-count bhowt6w0coxl0s2y-9} closure-alloc-amount
(add-counter '#{padded-vector-alloc-count bhowt6w0coxl0s2y-11} padded-vector-alloc-amount
(add-counter '#{padded-closure-alloc-count bhowt6w0coxl0s2y-10} padded-closure-alloc-amount
e)))))))
(let ([c (car c*)])
(case (closure-type c)
[(pair) (f (cdr c*) (fx+ pair-count 1) vector-count closure-count
vector-alloc-amount closure-alloc-amount padded-vector-alloc-amount
padded-closure-alloc-amount)]
[(vector)
(let ([n (fx+ (length (closure-free* c)) 1)])
(f (cdr c*) pair-count (fx+ vector-count 1) closure-count
(fx+ vector-alloc-amount n) closure-alloc-amount
(fx+ padded-vector-alloc-amount (fxsll (fxsra (fx+ n 1) 1) 1))
padded-closure-alloc-amount))]
[(closure)
(let ([n (fx+ (length (closure-free* c)) 1)])
(f (cdr c*) pair-count vector-count (fx+ closure-count 1)
vector-alloc-amount (fx+ closure-alloc-amount n)
padded-vector-alloc-amount
(fx+ padded-closure-alloc-amount (fxsll (fxsra (fx+ n 1) 1) 1))))]
[else (f (cdr c*) pair-count vector-count closure-count
vector-alloc-amount closure-alloc-amount padded-vector-alloc-amount
padded-closure-alloc-amount)]))))
e)))
(define add-counter
(lambda (counter amount e)
(with-output-language (L7 Expr)
(%seq
,(%inline inc-profile-counter
,(%mref
(literal ,(make-info-literal #t 'object counter (constant symbol-value-disp)))
,(constant record-data-disp))
(quote ,amount))
,e))))
(define add-ref-counter
(lambda (e)
(if (track-dynamic-closure-counts)
(add-counter '#{ref-count bhowt6w0coxl0s2y-4} 1 e)
e)))
(define-syntax with-raw-closure-ref-counter
(syntax-rules ()
[(_ ?x ?e1 ?e2 ...)
(let ([expr (begin ?e1 ?e2 ...)])
(if (and (track-dynamic-closure-counts) (uvar-was-closure-ref? ?x))
(add-counter '#{raw-ref-count bhowt6w0coxl0s2y-1} 1 expr)
expr))]))
(define add-raw-counters
(lambda (free** e)
(if (track-dynamic-closure-counts)
(let f ([x** free**] [alloc 0] [raw 0])
(if (null? x**)
(add-counter '#{raw-create-count bhowt6w0coxl0s2y-2} (length free**)
(add-counter '#{raw-alloc-count bhowt6w0coxl0s2y-3} alloc
(add-counter '#{raw-ref-count bhowt6w0coxl0s2y-1} raw e)))
(let ([x* (car x**)])
(f (cdr x**) (fx+ alloc (length x*) 1)
(fold-left
(lambda (cnt x) (if (uvar-was-closure-ref? x) (fx+ cnt 1) cnt))
raw x*)))))
e)))
(define-syntax with-was-closure-ref
(syntax-rules ()
[(_ ?x* ?e1 ?e2 ...)
(let f ([x* ?x*])
(if (or (null? x*) (not (track-dynamic-closure-counts)))
(begin ?e1 ?e2 ...)
(let ([x (car x*)])
(let ([old-was-cr? (uvar-was-closure-ref? x)])
(uvar-was-closure-ref! x #t)
(let ([expr (f (cdr x*))])
(uvar-was-closure-ref! x old-was-cr?)
expr)))))]))
(define add-original-closures!
(lambda (free**)
(cond
[(track-static-closure-counts) =>
(lambda (ci)
(static-closure-info-raw-closure-count-set! ci
(fold-left (lambda (count free*)
(static-closure-info-raw-free-var-count-set! ci
(+ (static-closure-info-raw-free-var-count ci)
(length free*)))
(+ count 1))
(static-closure-info-raw-closure-count ci) free**)))])))
(define add-final-closures!
(lambda (c*)
(cond
[(track-static-closure-counts) =>
(lambda (ci)
(for-each
(lambda (c)
(let ([type (closure-type c)])
(if (closure-wk? c)
(case type
[(constant)
(static-closure-info-wk-empty-count-set! ci
(+ (static-closure-info-wk-empty-count ci) 1))]
[(singleton)
(static-closure-info-wk-single-count-set! ci
(+ (static-closure-info-wk-single-count ci) 1))]
[(pair)
(static-closure-info-wk-pair-count-set! ci
(+ (static-closure-info-wk-pair-count ci) 1))]
[(vector)
(static-closure-info-wk-vector-count-set! ci
(+ (static-closure-info-wk-vector-count ci) 1))
(static-closure-info-wk-vector-free-var-count-set! ci
(+ (static-closure-info-wk-vector-free-var-count ci)
(length (closure-free* c))))]
[(borrowed)
(static-closure-info-wk-borrowed-count-set! ci
(+ (static-closure-info-wk-borrowed-count ci) 1))]
[(closure)
(static-closure-info-nwk-closure-count-set! ci
(+ (static-closure-info-nwk-closure-count ci) 1))
(static-closure-info-nwk-closure-free-var-count-set! ci
(+ (static-closure-info-nwk-closure-free-var-count ci)
(length (closure-free* c))))]
[else (sorry! who "unexpected well-known closure type ~s" type)])
(case type
[(constant)
(static-closure-info-nwk-empty-count-set! ci
(+ (static-closure-info-nwk-empty-count ci) 1))]
[(closure)
(static-closure-info-nwk-closure-count-set! ci
(+ (static-closure-info-nwk-closure-count ci) 1))
(static-closure-info-nwk-closure-free-var-count-set! ci
(+ (static-closure-info-nwk-closure-free-var-count ci)
(length (closure-free* c))))]
[else (sorry! who "unexpected non-well-known closure type ~s" type)]))))
c*))]))))
(define gl* '())
(define gle* '())
(define-record-type binding
(fields l x x*)
(nongenerative)
(sealed #t)
(protocol
(lambda (new)
(lambda (l x x*)
(new l x x*)))))
(define binding-well-known?
(lambda (b)
(info-lambda-well-known?
(uvar-info-lambda
(binding-x b)))))
(define-record-type frob
(fields name (mutable expr) (mutable seen frob-seen? frob-seen!))
(nongenerative)
(sealed #t)
(protocol
(lambda (new)
(case-lambda
[(name expr) (new name expr #f)]
[(name expr seen) (new name expr seen)]))))
(define-record-type closure
(nongenerative)
(sealed #t)
(fields wk? name label b*
(mutable sibling*) (mutable free*) (mutable type)
(mutable seen closure-seen? closure-seen!)
(mutable borrowed-name))
(protocol
(lambda (new)
(lambda (wk? b*)
; must use name and label of first binding
(let ([b (car b*)])
(let ([c (new wk? (binding-x b) (binding-l b) b* '() '() #f #f #f)])
(for-each
(lambda (b) (uvar-location-set! (binding-x b) c))
b*)
c))))))
(module (make-bank deposit retain borrow)
; NB: borrowing is probably cubic at present
; might should represent bank as a prefix tree
(define sort-free
(lambda (free*)
(sort (lambda (x y) (fx< (var-index x) (var-index y))) free*)))
(define make-bank (lambda () '()))
(define deposit
; NB: if used when self-references are possible, remove (olosure-name c) from free*
(lambda (free* c bank)
(cons (cons (sort-free free*) c)
(cons (cons (sort-free (cons (closure-name c) free*)) c)
bank))))
(define retain
(lambda (name* bank)
(filter (lambda (a) (memq (closure-name (cdr a)) name*)) bank)))
(define borrow
; NB: if used when self-references are possible, remove (olosure-name c) from free*
(lambda (free* bank)
(let ([free* (sort-free free*)])
(cond
[(assoc free* bank) => cdr]
[else #f])))))
(module (with-offsets)
(define set-offsets!
(lambda (x* index)
(do ([x* x* (cdr x*)] [index index (fx+ index 1)])
((null? x*) index)
(var-index-set! (car x*) index))))
(define-syntax with-offsets
(syntax-rules ()
[(_ index ?x* ?e1 ?e2 ...)
(identifier? #'index)
(let ([x* ?x*])
(let ([index (set-offsets! x* index)])
(let ([v (begin ?e1 ?e2 ...)])
(for-each (lambda (x) (var-index-set! x #f)) x*)
v)))])))
(with-output-language (L7 Expr)
(module (create-bindings create-inits)
(define (build-free-ref x)
(let ([loc (uvar-location x)])
(when (eq? loc 'loop)
(sorry! who "found reference to loop variable outside call position" x))
(frob-expr loc)))
(define create-bindings
(lambda (c* body)
(fold-right
(lambda (c body)
(case (closure-type c)
; NB: the pair and vector cases can be done this way only if well-known
; NB: closures can be shared with each other and up to one non-well-known closure
[(pair)
`(let ([,(closure-name c) ,(%primcall #f #f cons ,(map build-free-ref (closure-free* c)) ...)])
,body)]
[(vector)
`(let ([,(closure-name c) ,(%primcall #f #f vector ,(map build-free-ref (closure-free* c)) ...)])
,body)]
[else
(safe-assert (eq? (closure-type c) 'closure))
`(let ([,(closure-name c) ,(%constant-alloc type-closure
(fx* (fx+ (length (closure-free* c)) 1) (constant ptr-bytes)))])
,(%seq
(set! ,(%mref ,(closure-name c) ,(constant closure-code-disp))
(label-ref ,(closure-label c) ,(constant code-data-disp)))
,body))]))
(add-create-and-alloc-counters c* body)
c*)))
(define create-inits
(lambda (c* body)
(fold-right
(lambda (c body)
(case (closure-type c)
[(closure)
(let f ([x* (closure-free* c)] [i (constant closure-data-disp)])
(if (null? x*)
body
(%seq
(set! ,(%mref ,(closure-name c) ,i) ,(build-free-ref (car x*)))
,(f (cdr x*) (fx+ i (constant ptr-bytes))))))]
[else body]))
body c*))))
(define-syntax with-frob-location
(syntax-rules ()
[(_ ?x ?expr ?e)
(let ([frob (uvar-location ?x)])
(let ([loc (frob-expr frob)])
(frob-expr-set! frob ?expr)
(let ([v ?e])
(frob-expr-set! frob loc)
v)))]))
(define with-locations
(lambda (type free* mcp body index bank)
(case type
[(singleton) (with-frob-location (car free*) mcp (Expr body index bank))]
[(pair)
(with-frob-location (car free*) (add-ref-counter (%mref ,mcp ,(constant pair-car-disp)))
(with-frob-location (cadr free*) (add-ref-counter (%mref ,mcp ,(constant pair-cdr-disp)))
(Expr body index bank)))]
[else
(safe-assert (memq type '(vector closure)))
(let f ([free* free*] [i (if (eq? type 'vector) (constant vector-data-disp) (constant closure-data-disp))])
(if (null? free*)
(Expr body index bank)
(with-frob-location (car free*) (add-ref-counter (%mref ,mcp ,i))
(f (cdr free*) (fx+ i (constant ptr-bytes))))))])))))
(CaseLambdaExpr : CaseLambdaExpr (ir index c bank) -> CaseLambdaExpr ()
[(case-lambda ,info ,cl* ...)
(info-lambda-fv*-set! info (closure-free* c))
(info-lambda-closure-rep-set! info (closure-type c))
`(case-lambda ,info
,(let ([bank (retain (closure-free* c) bank)])
(map (lambda (cl) (CaseLambdaClause cl index c bank)) cl*))
...)])
(CaseLambdaClause : CaseLambdaClause (ir index c bank) -> CaseLambdaClause ()
[(clause (,x* ...) ,mcp ,interface ,body)
(with-offsets index x*
(let ([type (if (and c mcp) (closure-type c) 'constant)])
(if (eq? type 'constant)
`(clause (,x* ...) #f ,interface ,(Expr body index bank))
`(clause (,x* ...) ,mcp ,interface
,(with-frob-location (closure-name c) mcp
(if (eq? type 'borrowed)
(with-frob-location (closure-borrowed-name c) mcp
(let ([free* (closure-free* c)])
(with-locations (if (fx= (length free*) 2) 'pair 'vector) free* mcp body index bank)))
(with-locations type (closure-free* c) mcp body index bank)))))))])
(Expr : Expr (ir index bank) -> Expr ()
[(closures ([,x* (,x** ...) ,le*] ...) ,body)
(with-offsets index x*
(safe-assert (andmap var-index x*)) ; should be bound now
(safe-assert (andmap (lambda (x*) (andmap var-index x*)) x**)) ; should either have already been bound, or are bound now
(add-original-closures! x**)
(let* ([x**-loc (map (lambda (x*) (map uvar-location x*)) x**)]
[l* (map (lambda (x) (make-local-label (uvar-name x))) x*)]
; create one closure for each not-well-known binding, and one for all well-known bindings
[c* (let-values ([(wk* !wk*) (partition binding-well-known? (map make-binding l* x* x**))])
(cond
[(null? wk*) (map (lambda (b) (make-closure #f (list b))) !wk*)]
[(null? !wk*) (list (make-closure #t wk*))]
[else
; putting one !wk* in with wk*. claim: if any of the closures is nonempty,
; all will be nonempty, so might as well allow wk* to share a !wk's closure.
; if all are empty, no harm done.
; TODO: there might be a more suitable !wk to pick than (car !wk*)
(cons
(make-closure #f (cons (car !wk*) wk*))
(map (lambda (b) (make-closure #f (list b))) (cdr !wk*)))]))]
[xc* (map uvar-location x*)])
; set up sibling* and initial free*
(for-each
(lambda (c)
(let fb ([b* (closure-b* c)] [free* '()] [sibling* '()])
(if (null? b*)
(begin
(closure-free*-set! c free*)
(closure-sibling*-set! c sibling*))
(let fx ([x* (binding-x* (car b*))] [free* free*] [sibling* sibling*])
(if (null? x*)
(fb (cdr b*) free* sibling*)
(let* ([x (car x*)] [loc (uvar-location x)])
(cond
[(not loc)
(let ([frob (make-frob x x #t)])
(uvar-location-set! x frob)
(fx (cdr x*) (cons x free*) sibling*)
(frob-seen! frob #f))]
[(frob? loc)
(if (or (frob-seen? loc) (not (frob-name loc)))
(fx (cdr x*) free* sibling*)
(begin
(frob-seen! loc #t)
(fx (cdr x*) (cons (frob-name loc) free*) sibling*)
(frob-seen! loc #f)))]
[(closure? loc)
(if (or (eq? loc c) (closure-seen? loc)) ; no reflexive links
(fx (cdr x*) free* sibling*)
(begin
(closure-seen! loc #t)
(fx (cdr x*) free* (cons (closure-name loc) sibling*))
(closure-seen! loc #f)))]
[else (sorry! who "unexpected uvar location ~s" loc)])))))))
c*)
; find closures w/free variables (non-constant closures) and propagate
(when (ormap (lambda (c) (not (null? (closure-free* c)))) c*)
(for-each
(lambda (c)
(closure-free*-set! c (append (closure-sibling* c) (closure-free* c))))
c*))
; determine each closure's representation & set uvar location frobs
(for-each
(lambda (c)
(let ([free* (closure-free* c)])
(let ([frob (cond
[(null? free*)
(closure-type-set! c 'constant)
(make-frob #f `(literal ,(make-info-literal #f 'closure (closure-label c) 0)))]
[(closure-wk? c)
(cond
[(fx= (length free*) 1)
(closure-type-set! c 'singleton)
(uvar-location (car free*))]
[(borrow free* bank) =>
(lambda (mc)
(closure-type-set! c 'borrowed)
(closure-borrowed-name-set! c (closure-name mc))
(closure-free*-set! c (closure-free* mc))
(uvar-location (closure-name mc)))]
[else
; NB: HACK
(set! bank (deposit free* c bank))
(closure-type-set! c (if (fx= (length free*) 2) 'pair 'vector))
(make-frob (closure-name c) (closure-name c))])]
[else
(closure-type-set! c 'closure)
(make-frob (closure-name c) (closure-name c))])])
(for-each
(lambda (b) (uvar-location-set! (binding-x b) frob))
(closure-b* c)))))
c*)
; NB: if we are not sharing, but we are borrowing, we need to ensure
; NB: all closure variables point to final frob, and not a closure record
; record static closure counts
(add-final-closures! c*)
; process subforms and rebuild
(fold-left (lambda (body le)
(nanopass-case (L6 CaseLambdaExpr) le
[(case-lambda ,info ,cl ...) body]))
(let ([le* (map (lambda (le xc x*) (with-was-closure-ref x* (CaseLambdaExpr le index xc bank)))
le* xc* x**)]
[body (Expr body index bank)])
(set! gl* (append l* gl*))
(set! gle* (append le* gle*))
(let ([c* (filter (lambda (c) (memq (closure-type c) '(pair closure vector))) c*)])
(let ([body (create-bindings c* (create-inits c* (add-raw-counters x** body)))])
; leave location clean for later passes
(for-each (lambda (x) (uvar-location-set! x #f)) x*)
(for-each (lambda (x* x*-loc) (for-each uvar-location-set! x* x*-loc)) x** x**-loc)
body)))
le*)))]
[,x (with-raw-closure-ref-counter x (cond [(uvar-location x) => frob-expr] [else x]))]
[(loop ,x (,x* ...) ,body)
(uvar-location-set! x 'loop)
(let ([body (with-offsets index x* (Expr body index bank))])
(uvar-location-set! x #f)
`(loop ,x (,x* ...) ,body))]
[(call ,info ,mdcl ,x ,[e*] ...)
(guard (eq? (uvar-location x) 'loop))
`(call ,info ,mdcl ,x ,e* ...)]
[(call ,info ,mdcl ,x ,[e*] ...)
(guard mdcl)
(with-raw-closure-ref-counter x
(cond
[(uvar-location x) =>
(lambda (frob)
(if (frob-name frob)
`(call ,info ,mdcl ,(frob-expr frob) ,e* ...)
`(call ,info ,mdcl #f ,e* ...)))]
[else `(call ,info ,mdcl ,x ,e* ...)]))]
[(fcallable ,info)
(let ([label (make-local-label 'fcallable)])
(set! gl* (cons label gl*))
(set! gle* (cons (in-context CaseLambdaExpr `(fcallable ,info ,label)) gle*))
`(label-ref ,label 0))]
[(let ([,x* ,[e*]] ...) ,body)
(with-offsets index x*
`(let ([,x* ,e*] ...) ,(Expr body index bank)))]
[(mvlet ,[e] ((,x** ...) ,interface* ,body*) ...)
(let f ([var** x**] [body* body*] [rbody* '()])
(if (null? var**)
`(mvlet ,e ((,x** ...) ,interface* ,(reverse rbody*)) ...)
(f (cdr var**) (cdr body*) (cons (with-offsets index (car var**) (Expr (car body*) index bank)) rbody*))))])
(nanopass-case (L6 CaseLambdaExpr) ir
[(case-lambda ,info ,[CaseLambdaClause : cl 0 #f (make-bank) -> cl] ...)
(let ([l (make-local-label 'main)])
(let-values ([(gl* gle*) (sort-bindings gl* gle*)])
`(labels ([,gl* ,gle*] ... [,l (case-lambda ,info ,cl ...)]) ,l)))])))
(define-pass np-simplify-if : L7 (ir) -> L7 ()
(definitions
(define-$type-check (L7 Expr))
(with-output-language (L7 Expr)
; (and (fixnum? x1) ... (fixnum xn) e ...) => (and (fixnum? (logor x1 ... xn)) e ...)
; restricting fixnum? arguments to vars to avoid unnecessary computation
(define process-fixnum?
(lambda (info1 pr1 e x*)
(define build-fixnum?
(lambda (x*)
`(call ,info1 #f ,pr1
,(if (fx= (length x*) 1)
(car x*)
(%primcall #f #f fxlogor ,x* ...)))))
(let f ([e e] [x* x*])
(nanopass-case (L7 Expr) e
[(if (call ,info1 ,mdcl ,pr1 ,x1) ,e2 (quote ,d))
(guard (eq? mdcl #f) (eq? (primref-name pr1) 'fixnum?) (eq? d #f))
(f e2 (cons x1 x*))]
[(call ,info1 ,mdcl ,pr1 ,x1)
(guard (eq? mdcl #f) (eq? (primref-name pr1) 'fixnum?))
(build-fixnum? (cons x1 x*))]
[else `(if ,(build-fixnum? x*) ,(Expr e) (quote #f))]))))
(define process-paired-predicate
(lambda (info1 pr1 pr2 x-arg)
(let ([pr1 (primref-name pr1)] [pr2 (primref-name pr2)])
(cond
[(and (eq? pr1 'integer?) (eq? pr2 'exact?))
`(if ,(%primcall #f #f fixnum? ,x-arg) (quote #t) ,(%primcall #f #f bignum? ,x-arg))]
[(and (eq? pr1 'port?) (eq? pr2 'binary-port?))
(%typed-object-check mask-binary-port type-binary-port ,x-arg)]
[(and (eq? pr1 'port?) (eq? pr2 'textual-port?))
(%typed-object-check mask-textual-port type-textual-port ,x-arg)]
[(and (eq? pr1 'input-port?) (eq? pr2 'binary-port?))
(%typed-object-check mask-binary-input-port type-binary-input-port ,x-arg)]
[(and (eq? pr1 'input-port?) (eq? pr2 'textual-port?))
(%typed-object-check mask-textual-input-port type-textual-input-port ,x-arg)]
[(and (eq? pr1 'output-port?) (eq? pr2 'binary-port?))
(%typed-object-check mask-binary-output-port type-binary-output-port ,x-arg)]
[(and (eq? pr1 'output-port?) (eq? pr2 'textual-port?))
(%typed-object-check mask-textual-output-port type-textual-output-port ,x-arg)]
[else #f]))))))
(Expr : Expr (ir) -> Expr ()
[(if (call ,info1 ,mdcl ,pr1 ,x1) ,e2 (quote ,d))
(guard (eq? d #f) (eq? mdcl #f))
(if (eq? (primref-name pr1) 'fixnum?)
(process-fixnum? info1 pr1 e2 (list x1))
(or (and (nanopass-case (L7 Expr) e2
[(if (call ,info5 ,mdcl5 ,pr2 ,x2) ,e2 (quote ,d))
(guard (eq? x2 x1) (eq? mdcl5 #f) (eq? d #f))
(let ([e-paired-pred (process-paired-predicate info1 pr1 pr2 x1)])
(and e-paired-pred `(if ,e-paired-pred ,(Expr e2) (quote #f))))]
[(call ,info4 ,mdcl4 ,pr2 ,x2)
(guard (eq? x2 x1) (eq? mdcl4 #f))
(process-paired-predicate info1 pr1 pr2 x1)]
[else #f]))
`(if (call ,info1 ,mdcl ,pr1 ,x1) ,(Expr e2) (quote ,d))))]))
(module (np-profile-unroll-loops)
(define-syntax mvmap
(lambda (x)
(syntax-case x ()
[(_ ?n ?proc ?ls1 ?ls2 ...)
(let ([n (datum ?n)])
(unless (and (fixnum? n) (fx>= n 0)) (syntax-error #'?n "invalid return-value count"))
(let ([foo* (make-list n)])
(with-syntax ([(ls2 ...) (generate-temporaries #'(?ls2 ...))]
[(out ...) (generate-temporaries foo*)]
[(out* ...) (generate-temporaries foo*)])
#'(let ([proc ?proc])
(let f ([ls1 ?ls1] [ls2 ?ls2] ...)
(if (null? ls1)
(let ([out '()] ...) (values out ...))
(let-values ([(out ...) (proc (car ls1) (car ls2) ...)]
[(out* ...) (f (cdr ls1) (cdr ls2) ...)])
(values (cons out out*) ...))))))))])))
(define-who loop-unroll-limit
($make-thread-parameter
0 ; NB: disabling loop unrolling for now
(lambda (x)
(cond
[(fixnum? x) x]
[else ($oops who "~s is not a fixnum" x)]))))
(define PATH-SIZE-LIMIT 100)
;; NB: this comment is no longer accurate
;; Code growth computation is a little restrictive since it's measured
;; per loop... but maybe since new-size is weighted when profiling is
;; enabled it's fine.
#;(define CODE-GROWTH-FACTOR (fx1+ (loop-unroll-limit)))
(define-syntax delay
(syntax-rules ()
[(_ x) (lambda () x)]))
(define (force x) (if (procedure? x) (x) x))
(define-who analyze-loops ;; -> (lambda () body) size new-weighted-size
(lambda (body path-size unroll-count)
(with-output-language (L7 Expr)
;; Not really a loop, just didn't want to pass around path-size and unroll-count when unnecessary
(let loop ([body body])
(if (not body)
(values #f 0 0)
(nanopass-case (L7 Expr) body
[(literal ,info) (values body 0 0)]
[(immediate ,imm) (values body 0 0)]
[(quote ,d) (values body 0 0)]
[(goto ,l) (values body 1 1)]
[(mref ,[loop : e1 -> e1-promise e1-size e1-new-size] ,[loop : e2 -> e2-promise e2-size e2-new-size] ,imm)
(values (delay `(mref ,(force e1-promise) ,(force e2-promise) ,imm))
(fx+ e1-size e2-size 1)
(fx+ e1-new-size e2-new-size 1))]
[,lvalue (values body 1 1)]
[(profile ,src) (values body 0 0)]
[(pariah) (values body 0 0)]
[(label-ref ,l ,offset) (values body 0 0)]
[,pr (values body 1 1)]
[(inline ,info ,prim ,[loop : e* -> e*-promise size* new-size*] ...)
(values (delay `(inline ,info ,prim ,(map force e*-promise) ...))
(apply fx+ size*)
(apply fx+ new-size*))]
[(values ,info ,[loop : e* -> e*-promise size* new-size*] ...)
(values (delay `(values ,info ,(map force e*-promise) ...))
(apply fx+ size*)
(apply fx+ new-size*))]
[(call ,info ,mdcl ,x ,[loop : e* -> e*-promise size* new-size*] ...)
(guard (uvar-location x))
;; NB: Magic formulas, using number assuming query-count \in [0,1000]
(let* ([src (info-call-src info)]
[query-count (if src (profile-query-weight src) #f)]
;; don't bother with unimportant loops (less than 1% count relative to max)
[query-count (if (or (not query-count) (< query-count .1)) 0 (exact (truncate (* query-count 1000))))]
;; allow path-size to increase up to 300
[adjusted-path-size-limit (fx+ PATH-SIZE-LIMIT (fx/ (or query-count 0) 5))]
;; allow unroll limit to increase up to 4
[adjusted-unroll-limit (fx+ (loop-unroll-limit) (fx/ (or query-count 0) 300))])
(if (or (fxzero? query-count)
(fxzero? (fx+ unroll-count adjusted-unroll-limit))
(fx> path-size adjusted-path-size-limit))
(begin
(values (delay `(call ,info ,mdcl ,x ,(map force e*-promise) ...))
(fx1+ (apply fx+ size*))
(fx1+ (apply fx+ new-size*))))
(let*-values ([(var*) (car (uvar-location x))]
[(loop-body-promise body-size new-size) (analyze-loops (cdr (uvar-location x)) (fx1+ path-size) (fx1- unroll-count))]
[(new-size) ((lambda (x) (if query-count (fx/ x query-count) x)) (fx+ (length e*-promise) new-size))]
[(acceptable-new-size) (fx* (fx1+ adjusted-unroll-limit) body-size)])
;; NB: trying code growth computation here, where it could be per call site.
(values
(if (<= new-size acceptable-new-size)
(delay (fold-left
(lambda (body var e-promise)
`(seq (set! ,var ,(force e-promise)) ,body))
(rename-loop-body (force loop-body-promise))
var* e*-promise))
body)
(fx1+ (apply fx+ size*))
;; pretend the new size is smaller for important loops
new-size))))]
[(call ,info ,mdcl ,pr ,e* ...)
(let-values ([(e*-promise size* new-size*) (mvmap 3 (lambda (e) (analyze-loops e (fx1+ path-size) unroll-count)) e*)])
(values (delay `(call ,info ,mdcl ,pr ,(map force e*-promise) ...))
(fx+ 2 (apply fx+ size*))
(fx+ 2 (apply fx+ new-size*))))]
[(call ,info ,mdcl ,e ,e* ...)
(let-values ([(e-promise e-size e-new-size) (loop e)]
[(e*-promise size* new-size*) (mvmap 3 (lambda (e) (analyze-loops e (fx1+ path-size) unroll-count)) e*)])
(values (delay `(call ,info ,mdcl ,(force e-promise) ,(map force e*-promise) ...))
(fx+ 5 e-size (apply fx+ size*))
(fx+ 5 e-new-size (apply fx+ new-size*))))]
[(foreign-call ,info ,[loop : e -> e-promise e-size e-new-size] ,[loop : e* -> e*-promise size* new-size*] ...)
(values (delay `(foreign-call ,info ,(force e-promise) ,(map force e*-promise) ...))
(fx+ 5 e-size (apply fx+ size*))
(fx+ 5 e-new-size (apply fx+ new-size*)))]
[(label ,l ,[loop : body -> e size new-size])
(values (delay `(label ,l ,(force e))) size new-size)]
[(mvlet ,[loop : e -> e-promise e-size e-new-size] ((,x** ...) ,interface* ,body*) ...)
(let-values ([(body*-promise body*-size body*-new-size) (mvmap 3 (lambda (e) (analyze-loops e (fx+ e-size path-size) unroll-count)) body*)])
(values (delay `(mvlet ,(force e-promise) ((,x** ...) ,interface* ,(map force body*-promise)) ...))
(fx+ e-size (apply fx+ body*-size))
(fx+ e-new-size (apply fx+ body*-new-size))))]
[(mvcall ,info ,e1 ,e2)
(let-values ([(e1-promise e1-size e1-new-size) (analyze-loops e1 (fx+ 5 e1) unroll-count)]
[(e2-promise e2-size e2-new-size) (analyze-loops e2 (fx+ 5 e2) unroll-count)])
(values (delay `(mvcall ,info ,(force e1-promise) ,(force e2-promise)))
(fx+ 5 e1-size e2-size)
(fx+ 5 e1-new-size e2-new-size)))]
[(let ([,x* ,[loop : e* -> e*-promise size* new-size*]] ...) ,body)
(let-values ([(body-promise body-size body-new-size) (analyze-loops body (fx+ path-size (apply fx+ size*)) unroll-count)])
(values (delay `(let ([,x* ,(map force e*-promise)] ...) ,(force body-promise)))
(fx+ 1 body-size (apply fx+ size*))
(fx+ 1 body-new-size (apply fx+ new-size*))))]
[(if ,[loop : e0 -> e0-promise e0-size e0-new-size] ,e1 ,e2)
(let-values ([(e1-promise e1-size e1-new-size) (analyze-loops e1 (fx+ path-size e0-size) unroll-count)]
[(e2-promise e2-size e2-new-size) (analyze-loops e2 (fx+ path-size e0-size) unroll-count)])
(values (delay `(if ,(force e0-promise) ,(force e1-promise) ,(force e2-promise)))
(fx+ e0-size e1-size e2-size)
(fx+ e0-new-size e1-new-size e2-new-size)))]
[(seq ,[loop : e0 -> e0-promise e0-size e0-new-size] ,e1)
(let-values ([(e1-promise e1-size e1-new-size) (analyze-loops e1 (fx+ path-size e0-size) unroll-count)])
(values (delay `(seq ,(force e0-promise) ,(force e1-promise)))
(fx+ e0-size e1-size)
(fx+ e0-new-size e1-new-size)))]
[(set! ,lvalue ,[loop : e -> e-promise e-size e-new-size])
(values (delay `(set! ,lvalue ,(force e-promise)))
(fx+ 1 e-size)
(fx+ 1 e-new-size))]
[(alloc ,info ,[loop : e -> e-promise e-size e-new-size])
(values (delay `(alloc ,info ,(force e-promise)))
(fx+ 1 e-size)
(fx+ 1 e-new-size))]
[(loop ,x (,x* ...) ,[loop : body -> body-promise body-size body-new-size])
;; NB: Handling of inner loops?
(values (delay `(loop ,x (,x* ...) ,(force body-promise)))
body-size
body-new-size)]
[else ($oops who "forgot a case: ~a" body)]))))))
(define-pass rename-loop-body : (L7 Expr) (ir) -> (L7 Expr) ()
(definitions
(define-syntax with-fresh
(syntax-rules ()
[(_ rename-ht x* body)
(let* ([x* x*]
[rename-ht (hashtable-copy rename-ht #t)]
[x* (let ([t* (map (lambda (x) (make-tmp (uvar-name x))) x*)])
(for-each (lambda (x t) (eq-hashtable-set! rename-ht x t)) x* t*)
t*)])
body)])))
(Lvalue : Lvalue (ir rename-ht) -> Lvalue ()
[,x (eq-hashtable-ref rename-ht x x)]
[(mref ,[e1] ,[e2] ,imm) `(mref ,e1 ,e2 ,imm)])
(Expr : Expr (ir rename-ht) -> Expr ()
[(loop ,x (,[Lvalue : x* rename-ht -> x*] ...) ,body)
;; NB: with-fresh is so well designed that it can't handle this case
(let*-values ([(x) (list x)]
[(x body) (with-fresh rename-ht x (values (car x) (Expr body rename-ht)))])
`(loop ,x (,x* ...) ,body))]
[(let ([,x* ,[e*]] ...) ,body)
(with-fresh rename-ht x*
`(let ([,x* ,e*] ...) ,(Expr body rename-ht)))]
[(mvlet ,[e] ((,x** ...) ,interface* ,body*) ...)
(let* ([x**/body* (map (lambda (x* body)
(with-fresh rename-ht x* (cons x* (Expr body rename-ht))))
x** body*)]
[x** (map car x**/body*)]
[body* (map cdr x**/body*)])
`(mvlet ,e ((,x** ...) ,interface* ,body*) ...))])
(Expr ir (make-eq-hashtable)))
(define-pass np-profile-unroll-loops : L7 (ir) -> L7 ()
(Expr : Expr (ir) -> Expr ()
[(loop ,x (,x* ...) ,body)
(uvar-location-set! x (cons x* body))
(let-values ([(e-promise size new-size) (analyze-loops body 0 (loop-unroll-limit))])
(uvar-location-set! x #f)
;; NB: Not fx
`(loop ,x (,x* ...) ,(force e-promise))
;; trying out code-growth computation higher up
#;(if (<= new-size (* size CODE-GROWTH-FACTOR))
(begin
#;(printf "Opt: ~a\n" x)
`(loop ,x (,x* ...) ,(force e-promise)))
(begin
#;(printf "New size: ~a, old size: ~a\n" new-size size)
ir)))]))
(set! $loop-unroll-limit loop-unroll-limit))
(define target-fixnum?
(if (and (= (constant most-negative-fixnum) (most-negative-fixnum))
(= (constant most-positive-fixnum) (most-positive-fixnum)))
fixnum?
(lambda (x)
(and (or (fixnum? x) (bignum? x))
(<= (constant most-negative-fixnum) x (constant most-positive-fixnum))))))
(define unfix
(lambda (imm)
(ash imm (fx- (constant fixnum-offset)))))
(define fix
(lambda (imm)
(ash imm (constant fixnum-offset))))
(define ptr->imm
(lambda (x)
(cond
[(eq? x #f) (constant sfalse)]
[(eq? x #t) (constant strue)]
[(eq? x (void)) (constant svoid)]
[(null? x) (constant snil)]
[(eof-object? x) (constant seof)]
[($unbound-object? x) (constant sunbound)]
[(bwp-object? x) (constant sbwp)]
[(target-fixnum? x) (fix x)]
[(char? x) (+ (* (constant char-factor) (char->integer x)) (constant type-char))]
[else #f])))
(define-syntax ref-reg
(lambda (x)
(syntax-case x ()
[(k reg)
(identifier? #'reg)
(if (real-register? (datum reg))
#'reg
(with-implicit (k %mref) #`(%mref ,%tc ,(tc-disp reg))))])))
; TODO: recognize a direct call when it is at the end of a sequence, closures, or let form
; TODO: push call into if? (would need to pull arguments into temporaries to ensure order of evaluation
; TODO: how does this interact with mvcall?
(module (np-expand-primitives)
(define-threaded new-l*)
(define-threaded new-le*)
(define ht2 (make-hashtable symbol-hash eq?))
(define ht3 (make-hashtable symbol-hash eq?))
(define handle-prim
(lambda (src sexpr level name e*)
(let ([handler (or (and (fx= level 3) (symbol-hashtable-ref ht3 name #f))
(symbol-hashtable-ref ht2 name #f))])
(and handler (handler src sexpr e*)))))
(define-syntax Symref
(lambda (x)
(syntax-case x ()
[(k ?sym)
(with-implicit (k quasiquote)
#'`(literal ,(make-info-literal #t 'object ?sym (constant symbol-value-disp))))])))
(define-pass np-expand-primitives : L7 (ir) -> L9 ()
(Program : Program (ir) -> Program ()
[(labels ([,l* ,le*] ...) ,l)
(fluid-let ([new-l* '()] [new-le* '()])
(let ([le* (map CaseLambdaExpr le*)])
`(labels ([,l* ,le*] ... [,new-l* ,new-le*] ...) ,l)))])
(CaseLambdaExpr : CaseLambdaExpr (ir) -> CaseLambdaExpr ())
(CaseLambdaClause : CaseLambdaClause (ir) -> CaseLambdaClause ())
(Expr : Expr (ir) -> Expr ()
[(quote ,d)
(cond
[(ptr->imm d) => (lambda (i) `(immediate ,i))]
[else `(literal ,(make-info-literal #f 'object d 0))])]
[,pr (Symref (primref-name pr))]
[(call ,info0 ,mdcl0
(call ,info1 ,mdcl1 ,pr (quote ,d))
,[e*] ...)
(guard (and (eq? (primref-name pr) '$top-level-value) (symbol? d)))
`(call ,info0 ,mdcl0 ,(Symref d) ,e* ...)]
[(call ,info ,mdcl ,pr ,e* ...)
(cond
[(handle-prim (info-call-src info) (info-call-sexpr info) (primref-level pr) (primref-name pr) e*) => Expr]
[else
(let ([e* (map Expr e*)])
; NB: expand calls through symbol top-level values similarly
(let ([info (if (any-set? (prim-mask abort-op) (primref-flags pr))
(make-info-call (info-call-src info) (info-call-sexpr info) (info-call-check? info) #t #t)
info)])
`(call ,info ,mdcl ,(Symref (primref-name pr)) ,e* ...)))])]))
(define-who unhandled-arity
(lambda (name args)
(sorry! who "unhandled argument count ~s for ~s" (length args) 'name)))
(with-output-language (L7 Expr)
(define-$type-check (L7 Expr))
(define-syntax define-inline
(let ()
(define ctht2 (make-hashtable symbol-hash eq?))
(define ctht3 (make-hashtable symbol-hash eq?))
(define check-and-record
(lambda (level name)
(let ([a (symbol-hashtable-cell (if (fx= level 2) ctht2 ctht3) (syntax->datum name) #f)])
(when (cdr a) (syntax-error name "duplicate inline"))
(set-cdr! a #t))))
(lambda (x)
(define compute-interface
(lambda (clause)
(syntax-case clause ()
[(x e1 e2 ...) (identifier? #'x) -1]
[((x ...) e1 e2 ...) (length #'(x ...))]
[((x ... . r) e1 e2 ...) (fxlognot (length #'(x ...)))])))
(define bitmaskify
(lambda (i*)
(fold-left (lambda (mask i)
(logor mask (if (fx< i 0) (ash -1 (fxlognot i)) (ash 1 i))))
0 i*)))
(syntax-case x ()
[(k level id clause ...)
(identifier? #'id)
(let ([level (datum level)] [name (datum id)])
(unless (memv level '(2 3))
(syntax-error x (format "invalid level ~s in inline definition" level)))
(let ([pr ($sgetprop name (if (eqv? level 2) '*prim2* '*prim3*) #f)])
(include "primref.ss")
(unless pr
(syntax-error x (format "unrecognized primitive name ~s in inline definition" name)))
(let ([arity (primref-arity pr)])
(when arity
(unless (= (bitmaskify arity) (bitmaskify (map compute-interface #'(clause ...))))
(syntax-error x (format "arity mismatch for ~s" name))))))
(check-and-record level #'id)
(with-implicit (k src sexpr moi)
#`(symbol-hashtable-set! #,(if (eqv? level 2) #'ht2 #'ht3) 'id
(rec moi
(lambda (src sexpr args)
(apply (case-lambda clause ... [rest #f]) args))))))]))))
(define no-need-to-bind?
(lambda (multiple-ref? e)
(nanopass-case (L7 Expr) e
[,x (if (uvar? x) (not (uvar-assigned? x)) (eq? x %zero))]
[(immediate ,imm) #t] ; might should produce binding if imm is large
[(quote ,d) (or (not multiple-ref?) (ptr->imm d))]
[,pr (not multiple-ref?)]
[(literal ,info) (and (not multiple-ref?) (not (info-literal-indirect? info)))]
[(profile ,src) #t]
[(pariah) #t]
[else #f])))
(define binder
(lambda (multiple-ref? type e)
(if (no-need-to-bind? multiple-ref? e)
(values e values)
(let ([t (make-tmp 't type)])
(values t
(lambda (body)
`(let ([,t ,e]) ,body)))))))
(define list-binder
(lambda (multiple-ref? type e*)
(if (null? e*)
(values '() values)
(let-values ([(e dobind) (binder multiple-ref? type (car e*))]
[(e* dobind*) (list-binder multiple-ref? type (cdr e*))])
(values (cons e e*)
(lambda (body)
(dobind (dobind* body))))))))
(define-syntax $bind
(lambda (x)
(syntax-case x ()
[(_ binder multiple-ref? type (b ...) e)
(let ([t0* (generate-temporaries #'(b ...))])
(let f ([b* #'(b ...)] [t* t0*] [x* '()])
(if (null? b*)
(with-syntax ([(x ...) (reverse x*)] [(t ...) t0*])
#`(let ([x t] ...) e))
(syntax-case (car b*) ()
[x (identifier? #'x)
#`(let-values ([(#,(car t*) dobind) (binder multiple-ref? 'type x)])
(dobind #,(f (cdr b*) (cdr t*) (cons #'x x*))))]
[(x e) (identifier? #'x)
#`(let-values ([(#,(car t*) dobind) (binder multiple-ref? 'type e)])
(dobind #,(f (cdr b*) (cdr t*) (cons #'x x*))))]))))])))
(define-syntax bind
(syntax-rules ()
[(_ multiple-ref? type (b ...) e)
(identifier? #'type)
($bind binder multiple-ref? type (b ...) e)]
[(_ multiple-ref? (b ...) e)
($bind binder multiple-ref? ptr (b ...) e)]))
(define-syntax list-bind
(syntax-rules ()
[(_ multiple-ref? type (b ...) e)
(identifier? #'type)
($bind list-binder multiple-ref? type (b ...) e)]
[(_ multiple-ref? (b ...) e)
($bind list-binder multiple-ref? ptr (b ...) e)]))
(define-syntax build-libcall
(lambda (x)
(syntax-case x ()
[(k pariah? src sexpr name e ...)
(let ([libspec ($sgetprop (datum name) '*libspec* #f)])
(define interface-okay?
(lambda (interface* cnt)
(ormap
(lambda (interface)
(if (fx< interface 0)
(fx>= cnt (lognot interface))
(fx= cnt interface)))
interface*)))
(unless libspec (syntax-error x "unrecognized library routine"))
(unless (eqv? (length #'(e ...)) (libspec-interface libspec))
(syntax-error x "invalid number of arguments"))
(let ([is-pariah? (datum pariah?)])
(unless (boolean? is-pariah?)
(syntax-error x "pariah indicator must be a boolean literal"))
(when (and (libspec-error? libspec) (not is-pariah?))
(syntax-error x "pariah indicator is inconsistent with libspec-error indicator"))
(with-implicit (k quasiquote)
(with-syntax ([body #`(call ,(make-info-call src sexpr #f pariah? #,(libspec-error? libspec)) #f
(literal ,(make-info-literal #f 'library '#,(datum->syntax #'* libspec) 0))
,e ...)])
(if is-pariah?
#'`(seq (pariah) body)
#'`body)))))])))
(define constant?
(case-lambda
[(x)
(nanopass-case (L7 Expr) x
[(quote ,d) #t]
; TODO: handle immediate?
[else #f])]
[(pred? x)
(nanopass-case (L7 Expr) x
[(quote ,d) (pred? d)]
; TODO: handle immediate?
[else #f])]))
(define constant-value
(lambda (x)
(nanopass-case (L7 Expr) x
[(quote ,d) d]
; TODO: handle immediate if constant? does
[else #f])))
(define maybe-add-label
(lambda (Llib body)
(if Llib
`(label ,Llib ,body)
body)))
(define build-and
(lambda (e1 e2)
`(if ,e1 ,e2 ,(%constant sfalse))))
(define build-simple-or
(lambda (e1 e2)
`(if ,e1 ,(%constant strue) ,e2)))
(define build-fix
(lambda (e)
(%inline sll ,e ,(%constant fixnum-offset))))
(define build-unfix
(lambda (e)
(nanopass-case (L7 Expr) e
[(quote ,d) (guard (target-fixnum? d)) `(immediate ,d)]
[else (%inline sra ,e ,(%constant fixnum-offset))])))
(define build-not
(lambda (e)
`(if ,e ,(%constant sfalse) ,(%constant strue))))
(define build-null?
(lambda (e)
(%type-check mask-nil snil ,e)))
(define build-eq?
(lambda (e1 e2)
(%inline eq? ,e1 ,e2)))
(define build-eqv?
(lambda (src sexpr e1 e2)
(build-libcall #f src sexpr eqv? e1 e2)))
(define make-build-eqv?
(lambda (src sexpr)
(lambda (e1 e2)
(build-eqv? src sexpr e1 e2))))
(define fixnum-constant?
(lambda (e)
(constant? target-fixnum? e)))
(define expr->index
(lambda (e alignment limit)
(nanopass-case (L7 Expr) e
[(quote ,d)
(and (target-fixnum? d)
(>= d 0)
(< d limit)
(fxzero? (logand d (fx- alignment 1)))
d)]
[else #f])))
(define build-fixnums?
(lambda (e*)
(let ([e* (remp fixnum-constant? e*)])
(if (null? e*)
`(quote #t)
(%type-check mask-fixnum type-fixnum
,(fold-left (lambda (e1 e2) (%inline logor ,e1 ,e2))
(car e*) (cdr e*)))))))
(define build-flonums?
(lambda (e*)
(let ([e* (remp (lambda (e) (constant? flonum? e)) e*)])
(if (null? e*)
`(quote #t)
(let f ([e* e*])
(let ([e (car e*)] [e* (cdr e*)])
(let ([check (%type-check mask-flonum type-flonum ,e)])
(if (null? e*)
check
(build-and check (f e*))))))))))
(define build-chars?
(lambda (e1 e2)
(define char-constant?
(lambda (e)
(constant? char? e)))
(if (char-constant? e1)
(if (char-constant? e2)
(%constant strue)
(%type-check mask-char type-char ,e2))
(if (char-constant? e2)
(%type-check mask-char type-char ,e1)
(build-and
(%type-check mask-char type-char ,e1)
(%type-check mask-char type-char ,e2))))))
(define build-list
(lambda (e*)
(if (null? e*)
(%constant snil)
(list-bind #f (e*)
(bind #t ([t (%constant-alloc type-pair (fx* (constant size-pair) (length e*)))])
(let loop ([e* e*] [i 0])
(let ([e (car e*)] [e* (cdr e*)])
`(seq
(set! ,(%mref ,t ,(fx+ i (constant pair-car-disp))) ,e)
,(if (null? e*)
`(seq
(set! ,(%mref ,t ,(fx+ i (constant pair-cdr-disp))) ,(%constant snil))
,t)
(let ([next-i (fx+ i (constant size-pair))])
`(seq
(set! ,(%mref ,t ,(fx+ i (constant pair-cdr-disp)))
,(%inline + ,t (immediate ,next-i)))
,(loop e* next-i))))))))))))
(define build-pair?
(lambda (e)
(%type-check mask-pair type-pair ,e)))
(define build-car
(lambda (e)
(%mref ,e ,(constant pair-car-disp))))
(define build-cdr
(lambda (e)
(%mref ,e ,(constant pair-cdr-disp))))
(define build-char->integer
(lambda (e)
(%inline srl ,e
(immediate ,(fx- (constant char-data-offset) (constant fixnum-offset))))))
(define build-integer->char
(lambda (e)
(%inline +
,(%inline sll ,e
(immediate ,(fx- (constant char-data-offset) (constant fixnum-offset))))
,(%constant type-char))))
(define build-dirty-store
(case-lambda
[(base offset e) (build-dirty-store base %zero offset e)]
[(base index offset e) (build-dirty-store base index offset e
(lambda (base index offset e) `(set! ,(%mref ,base ,index ,offset) ,e))
(lambda (s r) `(seq ,s ,r)))]
[(base index offset e build-assign build-seq)
(if (nanopass-case (L7 Expr) e
[(quote ,d) (ptr->imm d)]
[else #f])
(build-assign base index offset e)
(let ([a (if (eq? index %zero)
(%lea ,base offset)
(%lea ,base ,index offset))])
; NB: should work harder to determine cases where x can't be a fixnum
(if (nanopass-case (L7 Expr) e
[(quote ,d) #t]
[(literal ,info) #t]
[else #f])
(bind #f ([e e])
; eval a second so the address is not live across any calls
(bind #t ([a a])
(build-seq
(build-assign a %zero 0 e)
(%inline remember ,a))))
(bind #t ([e e])
; eval a second so the address is not live across any calls
(bind #t ([a a])
(build-seq
(build-assign a %zero 0 e)
`(if ,(%type-check mask-fixnum type-fixnum ,e)
,(%constant svoid)
,(%inline remember ,a))))))))]))
(define make-build-cas
(lambda (old-v)
(lambda (base index offset v)
`(seq
,(%inline cas ,base ,index (immediate ,offset) ,old-v ,v)
(inline ,(make-info-condition-code 'eq? #f #t) ,%condition-code)))))
(define build-cas-seq
(lambda (cas remember)
`(if ,cas
(seq ,remember ,(%constant strue))
,(%constant sfalse))))
(define build-$record
(lambda (tag args)
(bind #f (tag)
(list-bind #f (args)
(let ([n (fx+ (length args) 1)])
(bind #t ([t (%constant-alloc type-typed-object (fx* n (constant ptr-bytes)))])
`(seq
(set! ,(%mref ,t ,(constant record-type-disp)) ,tag)
,(let f ([args args] [offset (constant record-data-disp)])
(if (null? args)
t
`(seq
(set! ,(%mref ,t ,offset) ,(car args))
,(f (cdr args) (fx+ offset (constant ptr-bytes)))))))))))))
(define build-$real->flonum
(lambda (src sexpr x who)
(if (constant? flonum? x)
x
(bind #t (x)
(bind #f (who)
`(if ,(%type-check mask-flonum type-flonum ,x)
,x
,(build-libcall #t src sexpr real->flonum x who)))))))
(define build-$inexactnum-real-part
(lambda (e)
(%lea ,e (fx+ (constant inexactnum-real-disp)
(fx- (constant type-flonum) (constant typemod))))))
(define build-$inexactnum-imag-part
(lambda (e)
(%lea ,e (fx+ (constant inexactnum-imag-disp)
(fx- (constant type-flonum) (constant typemod))))))
(define make-build-fill
(lambda (elt-bytes data-disp)
(define ptr-bytes (constant ptr-bytes))
(define super-size
(lambda (e-fill)
(define-who super-size-imm
(lambda (imm)
`(immediate
,(constant-case ptr-bytes
[(4)
(case elt-bytes
[(1) (let ([imm (logand imm #xff)])
(let ([imm (logor (ash imm 8) imm)])
(logor (ash imm 16) imm)))]
[(2) (let ([imm (logand imm #xffff)])
(logor (ash imm 16) imm))]
[else (sorry! who "unexpected elt-bytes ~s" elt-bytes)])]
[(8)
(case elt-bytes
[(1) (let ([imm (logand imm #xff)])
(let ([imm (logor (ash imm 8) imm)])
(let ([imm (logor (ash imm 16) imm)])
(logor (ash imm 32) imm))))]
[(2) (let ([imm (logand imm #xffff)])
(let ([imm (logor (ash imm 16) imm)])
(logor (ash imm 32) imm)))]
[(4) (let ([imm (logand imm #xffffffff)])
(logor (ash imm 32) imm))]
[else (sorry! who "unexpected elt-bytes ~s" elt-bytes)])]))))
(define-who super-size-expr
(lambda (e-fill)
(define (double e-fill k)
(%inline logor
,(%inline sll ,e-fill (immediate ,k))
,e-fill))
(define (mask e-fill k)
(%inline logand ,e-fill (immediate ,k)))
(constant-case ptr-bytes
[(4)
(case elt-bytes
[(1) (bind #t ([e-fill (mask e-fill #xff)])
(bind #t ([e-fill (double e-fill 8)])
(double e-fill 16)))]
[(2) (bind #t ([e-fill (mask e-fill #xffff)])
(double e-fill 16))]
[else (sorry! who "unexpected elt-bytes ~s" elt-bytes)])]
[(8)
(case elt-bytes
[(1) (bind #t ([e-fill (mask e-fill #xff)])
(bind #t ([e-fill (double e-fill 8)])
(bind #t ([e-fill (double e-fill 16)])
(double e-fill 32))))]
[(2) (bind #t ([e-fill (mask e-fill #xffff)])
(bind #t ([e-fill (double e-fill 16)])
(double e-fill 32)))]
[(4) (bind #t ([e-fill (mask e-fill #xffffffff)])
(double e-fill 32))]
[else (sorry! who "unexpected elt-bytes ~s" elt-bytes)])])))
(if (fx= elt-bytes ptr-bytes)
e-fill
(nanopass-case (L7 Expr) e-fill
[(quote ,d)
(cond
[(ptr->imm d) => super-size-imm]
[else (super-size-expr e-fill)])]
[(immediate ,imm) (super-size-imm imm)]
[else (super-size-expr e-fill)]))))
(lambda (e-vec e-bytes e-fill)
; NB: caller must bind e-vec and e-fill
(safe-assert (no-need-to-bind? #t e-vec))
(safe-assert (no-need-to-bind? #f e-fill))
(nanopass-case (L7 Expr) e-bytes
[(immediate ,imm)
(guard (fixnum? imm) (fx<= 0 imm (fx* 4 ptr-bytes)))
(if (fx= imm 0)
e-vec
(bind #t ([e-fill (super-size e-fill)])
(let f ([n (if (fx>= elt-bytes ptr-bytes)
imm
(fxlogand (fx+ imm (fx- ptr-bytes 1)) (fx- ptr-bytes)))])
(let ([n (fx- n ptr-bytes)])
`(seq
(set! ,(%mref ,e-vec ,(fx+ data-disp n)) ,e-fill)
,(if (fx= n 0) e-vec (f n)))))))]
[else
(let ([Ltop (make-local-label 'Ltop)] [t (make-assigned-tmp 't 'uptr)])
(bind #t ([e-fill (super-size e-fill)])
`(let ([,t ,(if (fx>= elt-bytes ptr-bytes)
e-bytes
(nanopass-case (L7 Expr) e-bytes
[(immediate ,imm)
`(immediate ,(logand (+ imm (fx- ptr-bytes 1)) (fx- ptr-bytes)))]
[else
(%inline logand
,(%inline +
,e-bytes
(immediate ,(fx- ptr-bytes 1)))
(immediate ,(fx- ptr-bytes)))]))])
(label ,Ltop
(if ,(%inline eq? ,t (immediate 0))
,e-vec
,(%seq
(set! ,t ,(%inline - ,t (immediate ,ptr-bytes)))
(set! ,(%mref ,e-vec ,t ,data-disp) ,e-fill)
(goto ,Ltop)))))))]))))
;; NOTE: integer->ptr and unsigned->ptr DO NOT handle 64-bit integers on a 32-bit machine.
;; this is okay for $object-ref and $object-set!, which do not support moving 64-bit values
;; as single entities on a 32-bit machine, but care should be taken if these are used with
;; other primitives.
(define-who integer->ptr
(lambda (x width)
(if (fx>= (constant fixnum-bits) width)
(build-fix x)
(%seq
(set! ,%ac0 ,x)
(set! ,%xp ,(build-fix %ac0))
(set! ,%xp ,(build-unfix %xp))
(if ,(%inline eq? ,%ac0 ,%xp)
,(build-fix %ac0)
(seq
(set! ,%ac0
(inline
,(case width
[(32) (intrinsic-info-asmlib dofretint32 #f)]
[(64) (intrinsic-info-asmlib dofretint64 #f)]
[else ($oops who "can't handle width ~s" width)])
,%asmlibcall))
,%ac0))))))
(define-who unsigned->ptr
(lambda (x width)
(if (fx>= (constant fixnum-bits) width)
(build-fix x)
`(seq
(set! ,%ac0 ,x)
(if ,(%inline u< ,(%constant most-positive-fixnum) ,%ac0)
(seq
(set! ,%ac0
(inline
,(case width
[(32) (intrinsic-info-asmlib dofretuns32 #f)]
[(64) (intrinsic-info-asmlib dofretuns64 #f)]
[else ($oops who "can't handle width ~s" width)])
,%asmlibcall))
,%ac0)
,(build-fix %ac0))))))
(define-who i32xu32->ptr
(lambda (hi lo)
(safe-assert (eqv? (constant ptr-bits) 32))
(let ([Lbig (make-local-label 'Lbig)])
(bind #t (lo hi)
`(if ,(%inline eq? ,hi ,(%inline sra ,lo (immediate 31)))
,(bind #t ([fxlo (build-fix lo)])
`(if ,(%inline eq? ,(build-unfix fxlo) ,lo)
,fxlo
(goto ,Lbig)))
(label ,Lbig
,(%seq
(set! ,%ac0 ,lo)
(set! ,(ref-reg %ac1) ,hi)
(set! ,%ac0 (inline ,(intrinsic-info-asmlib dofretint64 #f) ,%asmlibcall))
,%ac0)))))))
(define-who u32xu32->ptr
(lambda (hi lo)
(safe-assert (eqv? (constant ptr-bits) 32))
(let ([Lbig (make-local-label 'Lbig)])
(bind #t (lo hi)
`(if ,(%inline eq? ,hi (immediate 0))
(if ,(%inline u< ,(%constant most-positive-fixnum) ,lo)
(goto ,Lbig)
,(build-fix lo))
(label ,Lbig
,(%seq
(set! ,%ac0 ,lo)
(set! ,(ref-reg %ac1) ,hi)
(set! ,%ac0 (inline ,(intrinsic-info-asmlib dofretuns64 #f) ,%asmlibcall))
,%ac0)))))))
(define-who ptr->integer
(lambda (value width)
(if (fx> (constant fixnum-bits) width)
(build-unfix value)
`(seq
(set! ,%ac0 ,value)
(if ,(%type-check mask-fixnum type-fixnum ,%ac0)
,(build-unfix %ac0)
(seq
(set! ,%ac0
(inline
,(cond
[(fx<= width 32) (intrinsic-info-asmlib dofargint32 #f)]
[(fx<= width 64) (intrinsic-info-asmlib dofargint64 #f)]
[else ($oops who "can't handle width ~s" width)])
,%asmlibcall))
,%ac0))))))
(define ptr-type (constant-case ptr-bits
[(32) 'unsigned-32]
[(64) 'unsigned-64]
[else ($oops 'ptr-type "unknown ptr-bit size ~s" (constant ptr-bits))]))
(define-who type->width
(lambda (x)
(case x
[(integer-8 unsigned-8 char) 8]
[(integer-16 unsigned-16) 16]
[(integer-24 unsigned-24) 24]
[(integer-32 unsigned-32 single-float) 32]
[(integer-40 unsigned-40) 40]
[(integer-48 unsigned-48) 48]
[(integer-56 unsigned-56) 56]
[(integer-64 unsigned-64 double-float) 64]
[(scheme-object fixnum) (constant ptr-bits)]
[(wchar) (constant wchar-bits)]
[else ($oops who "unknown type ~s" x)])))
(define offset-expr->index+offset
(lambda (offset)
(if (fixnum-constant? offset)
(values %zero (constant-value offset))
(values (build-unfix offset) 0))))
(define-who build-int-load
(lambda (swapped? type base index offset build-int)
(case type
[(integer-8 unsigned-8)
(build-int `(inline ,(make-info-load type #f) ,%load ,base ,index (immediate ,offset)))]
[(integer-16 integer-32 unsigned-16 unsigned-32)
(build-int `(inline ,(make-info-load type swapped?) ,%load ,base ,index (immediate ,offset)))]
[(integer-64 unsigned-64)
(constant-case ptr-bits
[(32)
(let-values ([(lo hi) (if (constant-case native-endianness [(little) swapped?] [(big) (not swapped?)])
(values (+ offset 4) offset)
(values offset (+ offset 4)))])
(bind #t (base index)
(build-int
`(inline ,(make-info-load 'integer-32 swapped?) ,%load ,base ,index (immediate ,hi))
`(inline ,(make-info-load 'unsigned-32 swapped?) ,%load ,base ,index (immediate ,lo)))))]
[(64)
(build-int `(inline ,(make-info-load type swapped?) ,%load ,base ,index (immediate ,offset)))])]
[(integer-24 unsigned-24)
(constant-case unaligned-integers
[(#t)
(let-values ([(lo hi) (if (constant-case native-endianness [(little) swapped?] [(big) (not swapped?)])
(values (+ offset 1) offset)
(values offset (+ offset 2)))])
(define hi-type (if (eq? type 'integer-24) 'integer-8 'unsigned-8))
(bind #t (base index)
(build-int
(%inline logor
,(%inline sll
(inline ,(make-info-load hi-type #f) ,%load ,base ,index (immediate ,hi))
(immediate 16))
(inline ,(make-info-load 'unsigned-16 swapped?) ,%load ,base ,index (immediate ,lo))))))])]
[(integer-40 unsigned-40)
(constant-case unaligned-integers
[(#t)
(let-values ([(lo hi) (if (constant-case native-endianness [(little) swapped?] [(big) (not swapped?)])
(values (+ offset 1) offset)
(values offset (+ offset 4)))])
(define hi-type (if (eq? type 'integer-40) 'integer-8 'unsigned-8))
(bind #t (base index)
(constant-case ptr-bits
[(32)
(build-int
`(inline ,(make-info-load hi-type #f) ,%load ,base ,index (immediate ,hi))
`(inline ,(make-info-load 'unsigned-32 swapped?) ,%load ,base ,index (immediate ,lo)))]
[(64)
(build-int
(%inline logor
,(%inline sll
(inline ,(make-info-load hi-type #f) ,%load ,base ,index (immediate ,hi))
(immediate 32))
(inline ,(make-info-load 'unsigned-32 swapped?) ,%load ,base ,index (immediate ,lo))))])))])]
[(integer-48 unsigned-48)
(constant-case unaligned-integers
[(#t)
(let-values ([(lo hi) (if (constant-case native-endianness [(little) swapped?] [(big) (not swapped?)])
(values (+ offset 2) offset)
(values offset (+ offset 4)))])
(define hi-type (if (eq? type 'integer-48) 'integer-16 'unsigned-16))
(bind #t (base index)
(constant-case ptr-bits
[(32)
(build-int
`(inline ,(make-info-load hi-type swapped?) ,%load ,base ,index (immediate ,hi))
`(inline ,(make-info-load 'unsigned-32 swapped?) ,%load ,base ,index (immediate ,lo)))]
[(64)
(build-int
(%inline logor
,(%inline sll
(inline ,(make-info-load hi-type swapped?) ,%load ,base ,index (immediate ,hi))
(immediate 32))
(inline ,(make-info-load 'unsigned-32 swapped?) ,%load ,base ,index (immediate ,lo))))])))])]
[(integer-56 unsigned-56)
(constant-case unaligned-integers
[(#t)
(let-values ([(lo mi hi) (if (constant-case native-endianness [(little) swapped?] [(big) (not swapped?)])
(values (+ offset 3) (+ offset 1) offset)
(values offset (+ offset 4) (+ offset 6)))])
(define hi-type (if (eq? type 'integer-56) 'integer-8 'unsigned-8))
(bind #t (base index)
(constant-case ptr-bits
[(32)
(build-int
(%inline logor
,(%inline sll
(inline ,(make-info-load hi-type #f) ,%load ,base ,index (immediate ,hi))
(immediate 16))
(inline ,(make-info-load 'unsigned-16 swapped?) ,%load ,base ,index (immediate ,mi)))
`(inline ,(make-info-load 'unsigned-32 swapped?) ,%load ,base ,index (immediate ,lo)))]
[(64)
(build-int
(%inline logor
,(%inline sll
,(%inline logor
,(%inline sll
(inline ,(make-info-load hi-type #f) ,%load ,base ,index (immediate ,hi))
(immediate 16))
(inline ,(make-info-load 'unsigned-16 swapped?) ,%load ,base ,index (immediate ,mi)))
(immediate 32))
(inline ,(make-info-load 'unsigned-32 swapped?) ,%load ,base ,index (immediate ,lo))))])))])]
[else (sorry! who "unsupported type ~s" type)])))
(define-who build-object-ref
(case-lambda
[(swapped? type base offset-expr)
(let-values ([(index offset) (offset-expr->index+offset offset-expr)])
(build-object-ref swapped? type base index offset))]
[(swapped? type base index offset)
(case type
[(scheme-object) `(inline ,(make-info-load ptr-type swapped?) ,%load ,base ,index (immediate ,offset))]
[(double-float)
(if swapped?
(constant-case ptr-bits
[(32)
(bind #t (base index)
(bind #t ([t (%constant-alloc type-flonum (constant size-flonum))])
(%seq
(set! ,(%mref ,t ,(constant flonum-data-disp))
(inline ,(make-info-load 'unsigned-32 #t) ,%load ,base ,index
(immediate ,(+ offset 4))))
(set! ,(%mref ,t ,(+ (constant flonum-data-disp) 4))
(inline ,(make-info-load 'unsigned-32 #t) ,%load ,base ,index
(immediate ,offset)))
,t)))]
[(64)
(bind #f (base index)
(bind #t ([t (%constant-alloc type-flonum (constant size-flonum))])
`(seq
(set! ,(%mref ,t ,(constant flonum-data-disp))
(inline ,(make-info-load 'unsigned-64 #t) ,%load ,base ,index
(immediate ,offset)))
,t)))])
(bind #f (base index)
(bind #t ([t (%constant-alloc type-flonum (constant size-flonum))])
(%seq
(inline ,(make-info-loadfl %flreg1) ,%load-double
,base ,index (immediate ,offset))
(inline ,(make-info-loadfl %flreg1) ,%store-double
,t ,%zero ,(%constant flonum-data-disp))
,t))))]
[(single-float)
(if swapped?
(bind #f (base index)
(bind #t ([t (%constant-alloc type-flonum (constant size-flonum))])
(%seq
(set! ,(%mref ,t ,(constant flonum-data-disp))
(inline ,(make-info-load 'unsigned-32 #t) ,%load ,base ,index
(immediate ,offset)))
(inline ,(make-info-loadfl %flreg1) ,%load-single->double
,t ,%zero ,(%constant flonum-data-disp))
(inline ,(make-info-loadfl %flreg1) ,%store-double
,t ,%zero ,(%constant flonum-data-disp))
,t)))
(bind #f (base index)
(bind #t ([t (%constant-alloc type-flonum (constant size-flonum))])
(%seq
(inline ,(make-info-loadfl %flreg1) ,%load-single->double
,base ,index (immediate ,offset))
(inline ,(make-info-loadfl %flreg1) ,%store-double
,t ,%zero ,(%constant flonum-data-disp))
,t))))]
[(integer-8 integer-16 integer-24 integer-32 integer-40 integer-48 integer-56 integer-64)
(build-int-load swapped? type base index offset
(if (and (eqv? (constant ptr-bits) 32) (memq type '(integer-40 integer-48 integer-56 integer-64)))
i32xu32->ptr
(lambda (x) (integer->ptr x (type->width type)))))]
[(unsigned-8 unsigned-16 unsigned-24 unsigned-32 unsigned-40 unsigned-48 unsigned-56 unsigned-64)
(build-int-load swapped? type base index offset
(if (and (eqv? (constant ptr-bits) 32) (memq type '(unsigned-40 unsigned-48 unsigned-56 unsigned-64)))
u32xu32->ptr
(lambda (x) (unsigned->ptr x (type->width type)))))]
[(fixnum) (build-fix `(inline ,(make-info-load ptr-type swapped?) ,%load ,base ,index (immediate ,offset)))]
[else (sorry! who "unsupported type ~s" type)])]))
(define-who build-int-store
(lambda (swapped? type base index offset value)
(case type
[(integer-8 unsigned-8)
`(inline ,(make-info-load type #f) ,%store ,base ,index (immediate ,offset) ,value)]
[(integer-16 integer-32 integer-64 unsigned-16 unsigned-32 unsigned-64)
`(inline ,(make-info-load type swapped?) ,%store ,base ,index (immediate ,offset) ,value)]
[(integer-24 unsigned-24)
(constant-case unaligned-integers
[(#t)
(let-values ([(lo hi) (if (constant-case native-endianness [(little) swapped?] [(big) (not swapped?)])
(values (+ offset 1) offset)
(values offset (+ offset 2)))])
(bind #t (base index value)
(%seq
(inline ,(make-info-load 'unsigned-16 swapped?) ,%store ,base ,index (immediate ,lo) ,value)
(inline ,(make-info-load 'unsigned-8 #f) ,%store ,base ,index (immediate ,hi)
,(%inline srl ,value (immediate 16))))))])]
[(integer-40 unsigned-40)
(constant-case unaligned-integers
[(#t)
(let-values ([(lo hi) (if (constant-case native-endianness [(little) swapped?] [(big) (not swapped?)])
(values (+ offset 1) offset)
(values offset (+ offset 4)))])
(bind #t (base index value)
(%seq
(inline ,(make-info-load 'unsigned-32 swapped?) ,%store ,base ,index (immediate ,lo) ,value)
(inline ,(make-info-load 'unsigned-8 #f) ,%store ,base ,index (immediate ,hi)
,(%inline srl ,value (immediate 32))))))])]
[(integer-48 unsigned-48)
(constant-case unaligned-integers
[(#t)
(let-values ([(lo hi) (if (constant-case native-endianness [(little) swapped?] [(big) (not swapped?)])
(values (+ offset 2) offset)
(values offset (+ offset 4)))])
(bind #t (base index value)
(%seq
(inline ,(make-info-load 'unsigned-32 swapped?) ,%store ,base ,index (immediate ,lo) ,value)
(inline ,(make-info-load 'unsigned-16 swapped?) ,%store ,base ,index (immediate ,hi)
,(%inline srl ,value (immediate 32))))))])]
[(integer-56 unsigned-56)
(constant-case unaligned-integers
[(#t)
(let-values ([(lo mi hi) (if (constant-case native-endianness [(little) swapped?] [(big) (not swapped?)])
(values (+ offset 3) (+ offset 1) offset)
(values offset (+ offset 4) (+ offset 6)))])
(bind #t (base index value)
(%seq
(inline ,(make-info-load 'unsigned-32 swapped?) ,%store ,base ,index (immediate ,lo) ,value)
(inline ,(make-info-load 'unsigned-16 swapped?) ,%store ,base ,index (immediate ,mi)
,(%inline srl ,value (immediate 32)))
(inline ,(make-info-load 'unsigned-8 #f) ,%store ,base ,index (immediate ,hi)
,(%inline srl ,value (immediate 48))))))])]
[else (sorry! who "unsupported type ~s" type)])))
(define-who build-object-set!
(case-lambda
[(type base offset-expr value)
(let-values ([(index offset) (offset-expr->index+offset offset-expr)])
(build-object-set! type base index offset value))]
[(type base index offset value)
(case type
[(scheme-object) (build-dirty-store base index offset value)]
[(double-float)
(bind #f (base index)
(%seq
(inline ,(make-info-loadfl %flreg1) ,%load-double
,value ,%zero ,(%constant flonum-data-disp))
(inline ,(make-info-loadfl %flreg1) ,%store-double
,base ,index (immediate ,offset))))]
[(single-float)
(bind #f (base index)
(%seq
(inline ,(make-info-loadfl %flreg1) ,%load-double->single
,value ,%zero ,(%constant flonum-data-disp))
(inline ,(make-info-loadfl %flreg1) ,%store-single
,base ,index (immediate ,offset))))]
; 40-bit+ only on 64-bit machines
[(integer-8 integer-16 integer-24 integer-32 integer-40 integer-48 integer-56 integer-64
unsigned-8 unsigned-16 unsigned-24 unsigned-32 unsigned-40 unsigned-48 unsigned-56 unsigned-64)
(build-int-store #f type base index offset (ptr->integer value (type->width type)))]
[(fixnum)
`(inline ,(make-info-load ptr-type #f) ,%store
,base ,index (immediate ,offset) ,(build-unfix value))]
[else (sorry! who "unrecognized type ~s" type)])]))
(define-who build-swap-object-set!
(case-lambda
[(type base offset-expr value)
(let-values ([(index offset) (offset-expr->index+offset offset-expr)])
(build-swap-object-set! type base index offset value))]
[(type base index offset value)
(case type
; only on 64-bit machines
[(double-float)
`(inline ,(make-info-load 'unsigned-64 #t) ,%store
,base ,index (immediate ,offset)
,(%mref ,value ,(constant flonum-data-disp)))]
; 40-bit+ only on 64-bit machines
[(integer-16 integer-24 integer-32 integer-40 integer-48 integer-56 integer-64
unsigned-16 unsigned-24 unsigned-32 unsigned-40 unsigned-48 unsigned-56 unsigned-64)
(build-int-store #t type base index offset (ptr->integer value (type->width type)))]
[(fixnum)
`(inline ,(make-info-load ptr-type #t) ,%store ,base ,index (immediate ,offset)
,(build-unfix value))]
[else (sorry! who "unrecognized type ~s" type)])]))
(define extract-unsigned-bitfield
(lambda (raw? start end arg)
(let* ([left (fx- (if raw? (constant ptr-bits) (constant fixnum-bits)) end)]
[right (if raw? (fx- (fx+ left start) (constant fixnum-offset)) (fx+ left start))]
[body (%inline srl
,(if (fx= left 0)
arg
(%inline sll ,arg (immediate ,left)))
(immediate ,right))])
(if (fx= start 0)
body
(%inline logand ,body (immediate ,(- (constant fixnum-factor))))))))
(define extract-signed-bitfield
(lambda (raw? start end arg)
(let* ([left (fx- (if raw? (constant ptr-bits) (constant fixnum-bits)) end)]
[right (if raw? (fx- (fx+ left start) (constant fixnum-offset)) (fx+ left start))])
(let ([body (if (fx= left 0) arg (%inline sll ,arg (immediate ,left)))])
(let ([body (if (fx= right 0) body (%inline sra ,body (immediate ,right)))])
(if (fx= start 0)
body
(%inline logand ,body (immediate ,(- (constant fixnum-factor))))))))))
(define insert-bitfield
(lambda (raw? start end bf-width arg val)
(if raw?
(cond
[(fx= start 0)
(%inline logor
,(%inline sll
,(%inline srl ,arg (immediate ,end))
(immediate ,end))
,(%inline srl
,(%inline sll ,val (immediate ,(fx- (constant fixnum-bits) end)))
(immediate ,(fx- (constant ptr-bits) end))))]
[(fx= end bf-width)
(%inline logor
,(%inline srl
,(%inline sll ,arg
(immediate ,(fx- (constant ptr-bits) start)))
(immediate ,(fx- (constant ptr-bits) start)))
,(cond
[(fx< start (constant fixnum-offset))
(%inline srl ,val
(immediate ,(fx- (constant fixnum-offset) start)))]
[(fx> start (constant fixnum-offset))
(%inline sll ,val
(immediate ,(fx- start (constant fixnum-offset))))]
[else val]))]
[else
(%inline logor
,(%inline logand ,arg
(immediate ,(lognot (ash (- (expt 2 (fx- end start)) 1) start))))
,(%inline srl
,(if (fx= (fx- end start) (constant fixnum-bits))
val
(%inline sll ,val
(immediate ,(fx- (constant fixnum-bits) (fx- end start)))))
(immediate ,(fx- (constant ptr-bits) end))))])
(cond
[(fx= start 0)
(%inline logor
,(%inline sll
,(%inline srl ,arg (immediate ,(fx+ end (constant fixnum-offset))))
(immediate ,(fx+ end (constant fixnum-offset))))
,(%inline srl
,(%inline sll ,val (immediate ,(fx- (constant fixnum-bits) end)))
(immediate ,(fx- (constant fixnum-bits) end))))]
#;[(fx= end (constant fixnum-bits)) ---] ; end < fixnum-bits
[else
(%inline logor
,(%inline logand ,arg
(immediate ,(lognot (ash (- (expt 2 (fx- end start)) 1)
(fx+ start (constant fixnum-offset))))))
,(%inline srl
,(%inline sll ,val
(immediate ,(fx- (constant fixnum-bits) (fx- end start))))
(immediate ,(fx- (constant fixnum-bits) end))))]))))
(define translate
(lambda (e current-shift target-shift)
(let ([delta (fx- current-shift target-shift)])
(if (fx= delta 0)
e
(if (fx< delta 0)
(%inline sll ,e (immediate ,(fx- delta)))
(%inline srl ,e (immediate ,delta)))))))
(define extract-length
(lambda (t/l length-offset)
(%inline logand
,(translate t/l length-offset (constant fixnum-offset))
(immediate ,(- (constant fixnum-factor))))))
(define build-type/length
(lambda (e type current-shift target-shift)
(let ([e (translate e current-shift target-shift)])
(if (eqv? type 0)
e
(%inline logor ,e (immediate ,type))))))
(define-syntax build-ref-check
(syntax-rules ()
[(_ type-disp maximum-length length-offset type mask immutable-flag)
(lambda (e-v e-i maybe-e-new)
; NB: caller must bind e-v, e-i, and maybe-e-new
(safe-assert (no-need-to-bind? #t e-v))
(safe-assert (no-need-to-bind? #t e-i))
(safe-assert (or (not maybe-e-new) (no-need-to-bind? #t maybe-e-new)))
(build-and
(%type-check mask-typed-object type-typed-object ,e-v)
(bind #t ([t (%mref ,e-v ,(constant type-disp))])
(cond
[(expr->index e-i 1 (constant maximum-length)) =>
(lambda (index)
(let ([e (%inline u<
(immediate ,(logor (ash index (constant length-offset)) (constant type) (constant immutable-flag)))
,t)])
(if (and (eqv? (constant type) (constant type-fixnum))
(eqv? (constant mask) (constant mask-fixnum)))
(build-and e (build-fixnums? (if maybe-e-new (list t maybe-e-new) (list t))))
(build-and
(%type-check mask type ,t)
(if maybe-e-new (build-and e (build-fixnums? (list maybe-e-new))) e)))))]
[else
(let ([e (%inline u< ,e-i ,(extract-length t (constant length-offset)))])
(if (and (eqv? (constant type) (constant type-fixnum))
(eqv? (constant mask) (constant mask-fixnum)))
(build-and e (build-fixnums? (if maybe-e-new (list e-i t maybe-e-new) (list e-i t))))
(build-and
(%type-check mask type ,t)
(build-and
(build-fixnums? (if maybe-e-new (list e-i maybe-e-new) (list e-i)))
e))))]))))]))
(define-syntax build-set-immutable!
(syntax-rules ()
[(_ type-disp immutable-flag)
(lambda (e-v)
(bind #t (e-v)
`(set! ,(%mref ,e-v ,(constant type-disp))
,(%inline logor
,(%mref ,e-v ,(constant type-disp))
(immediate ,(constant immutable-flag))))))]))
(define inline-args-limit 10)
(define reduce-equality
(lambda (src sexpr moi e1 e2 e*)
(and (fx<= (length e*) (fx- inline-args-limit 2))
(bind #t (e1)
(bind #f (e2)
(list-bind #f (e*)
(let compare ([src src] [e2 e2] [e* e*])
(if (null? e*)
(moi src sexpr (list e1 e2))
`(if ,(moi src sexpr (list e1 e2))
,(compare #f (car e*) (cdr e*))
(quote #f))))))))))
(define reduce-inequality
(lambda (src sexpr moi e1 e2 e*)
(and (fx<= (length e*) (fx- inline-args-limit 2))
(let f ([e2 e2] [e* e*] [re* '()])
(if (null? e*)
(bind #f ([e2 e2])
(let compare ([src src] [e* (cons e1 (reverse (cons e2 re*)))])
(let ([more-args (cddr e*)])
(if (null? more-args)
(moi src sexpr e*)
`(if ,(moi src sexpr (list (car e*) (cadr e*)))
,(compare #f (cdr e*))
(quote #f))))))
(bind #t ([e2 e2]) (f (car e*) (cdr e*) (cons e2 re*))))))))
(define reduce ; left associative as required for, e.g., fx-
(lambda (src sexpr moi e e*)
(and (fx<= (length e*) (fx- inline-args-limit 1))
(bind #f (e)
(list-bind #f ([e* e*])
(let reduce ([src src] [e e] [e* e*])
(if (null? e*)
e
(reduce #f (moi src sexpr (list e (car e*))) (cdr e*)))))))))
(module (relop-length RELOP< RELOP<= RELOP= RELOP>= RELOP>)
(define RELOP< -2)
(define RELOP<= -1)
(define RELOP= 0)
(define RELOP>= 1)
(define RELOP> 2)
(define (mirror op) (fx- op))
(define go
(lambda (op e n)
(let f ([n n] [e e])
(if (fx= n 0)
(cond
[(or (eqv? op RELOP=) (eqv? op RELOP<=)) (build-null? e)]
[(eqv? op RELOP<) `(seq ,e (quote #f))]
[(eqv? op RELOP>) (build-not (build-null? e))]
[(eqv? op RELOP>=) `(seq ,e (quote #t))]
[else (sorry! 'relop-length "unexpected op ~s" op)])
(cond
[(or (eqv? op RELOP=) (eqv? op RELOP>))
(bind #t (e)
(build-and
(build-not (build-null? e))
(f (fx- n 1) (build-cdr e))))]
[(eqv? op RELOP<)
(if (fx= n 1)
(build-null? e)
(bind #t (e)
(build-simple-or
(build-null? e)
(f (fx- n 1) (build-cdr e)))))]
[(eqv? op RELOP<=)
(bind #t (e)
(build-simple-or
(build-null? e)
(f (fx- n 1) (build-cdr e))))]
[(eqv? op RELOP>=)
(if (fx= n 1)
(build-not (build-null? e))
(bind #t (e)
(build-and
(build-not (build-null? e))
(f (fx- n 1) (build-cdr e)))))]
[else (sorry! 'relop-length "unexpected op ~s" op)])))))
(define relop-length1
(lambda (op e n)
(nanopass-case (L7 Expr) e
[(call ,info ,mdcl ,pr ,e)
(guard (and (eq? (primref-name pr) 'length) (all-set? (prim-mask unsafe) (primref-flags pr))))
(go op e n)]
[else #f])))
(define relop-length2
(lambda (op e1 e2)
(nanopass-case (L7 Expr) e2
[(quote ,d) (and (fixnum? d) (fx<= 0 d 4) (relop-length1 op e1 d))]
[else #f])))
(define relop-length
(case-lambda
[(op e) (relop-length1 op e 0)]
[(op e1 e2) (or (relop-length2 op e1 e2) (relop-length2 (mirror op) e2 e1))])))
(define make-ftype-pointer-equal?
(lambda (e1 e2)
(bind #f (e1 e2)
(%inline eq?
,(%mref ,e1 ,(constant record-data-disp))
,(%mref ,e2 ,(constant record-data-disp))))))
(define make-ftype-pointer-null?
(lambda (e)
(%inline eq?
,(%mref ,e ,(constant record-data-disp))
(immediate 0))))
(define eqvop-null-fptr
(lambda (e1 e2)
(nanopass-case (L7 Expr) e1
[(call ,info ,mdcl ,pr ,e1)
(and
(eq? (primref-name pr) 'ftype-pointer-address)
(all-set? (prim-mask unsafe) (primref-flags pr))
(nanopass-case (L7 Expr) e2
[(quote ,d)
(and (eqv? d 0) (make-ftype-pointer-null? e1))]
[(call ,info ,mdcl ,pr ,e2)
(and (eq? (primref-name pr) 'ftype-pointer-address)
(all-set? (prim-mask unsafe) (primref-flags pr))
(make-ftype-pointer-equal? e1 e2))]
[else #f]))]
[(quote ,d)
(and (eqv? d 0)
(nanopass-case (L7 Expr) e2
[(call ,info ,mdcl ,pr ,e2)
(and (eq? (primref-name pr) 'ftype-pointer-address)
(all-set? (prim-mask unsafe) (primref-flags pr))
(make-ftype-pointer-null? e2))]
[else #f]))]
[else #f])))
(define-inline 2 values
[(e) e]
[e* `(values ,(make-info-call src sexpr #f #f #f) ,e* ...)])
(define-inline 2 eq?
[(e1 e2)
(or (eqvop-null-fptr e1 e2)
(relop-length RELOP= e1 e2)
(%inline eq? ,e1 ,e2))])
(define-inline 2 $keep-live
[(e) (%seq ,(%inline keep-live ,e) ,(%constant svoid))])
(let ()
(define (zgo src sexpr e e1 e2 r6rs?)
(build-simple-or
(%inline eq? ,e (immediate 0))
`(if ,(build-fixnums? (list e))
,(%constant sfalse)
,(if r6rs?
(build-libcall #t src sexpr fx=? e1 e2)
(build-libcall #t src sexpr fx= e1 e2)))))
(define (go src sexpr e1 e2 r6rs?)
(or (relop-length RELOP= e1 e2)
(cond
[(constant? (lambda (x) (eqv? x 0)) e1)
(bind #t (e2) (zgo src sexpr e2 e1 e2 r6rs?))]
[(constant? (lambda (x) (eqv? x 0)) e2)
(bind #t (e1) (zgo src sexpr e1 e1 e2 r6rs?))]
[else (bind #t (e1 e2)
`(if ,(build-fixnums? (list e1 e2))
,(%inline eq? ,e1 ,e2)
,(if r6rs?
(build-libcall #t src sexpr fx=? e1 e2)
(build-libcall #t src sexpr fx= e1 e2))))])))
(define-inline 2 fx=
[(e1 e2) (go src sexpr e1 e2 #f)]
[(e1 . e*) #f])
(define-inline 2 fx=?
[(e1 e2) (go src sexpr e1 e2 #t)]
[(e1 e2 . e*) #f]))
(let () ; level 2 fx<, fx<?, etc.
(define-syntax fx-pred
(syntax-rules ()
[(_ op r6rs:op length-op inline-op)
(let ()
(define (go src sexpr e1 e2 r6rs?)
(or (relop-length length-op e1 e2)
(bind #t (e1 e2)
`(if ,(build-fixnums? (list e1 e2))
,(%inline inline-op ,e1 ,e2)
,(if r6rs?
(build-libcall #t src sexpr r6rs:op e1 e2)
(build-libcall #t src sexpr op e1 e2))))))
(define-inline 2 op
[(e1 e2) (go src sexpr e1 e2 #f)]
; TODO: 3-operand case requires 3-operand library routine
#;[(e1 e2 e3) (go3 src sexpr e1 e2 e3 #f)]
[(e1 . e*) #f])
(define-inline 2 r6rs:op
[(e1 e2) (go src sexpr e1 e2 #t)]
; TODO: 3-operand case requires 3-operand library routine
#; [(e1 e2 e3) (go3 src sexpr e1 e2 e3 #t)]
[(e1 e2 . e*) #f]))]))
(fx-pred fx< fx<? RELOP< <)
(fx-pred fx<= fx<=? RELOP<= <=)
(fx-pred fx>= fx>=? RELOP>= >=)
(fx-pred fx> fx>? RELOP> >))
(let () ; level 3 fx=, fx=?, etc.
(define-syntax fx-pred
(syntax-rules ()
[(_ op r6rs:op length-op inline-op)
(let ()
(define (go e1 e2)
(or (relop-length length-op e1 e2)
(%inline inline-op ,e1 ,e2)))
(define reducer
(if (eq? 'inline-op 'eq?)
reduce-equality
reduce-inequality))
(define-inline 3 op
[(e) `(seq ,e ,(%constant strue))]
[(e1 e2) (go e1 e2)]
[(e1 e2 . e*) (reducer src sexpr moi e1 e2 e*)])
(define-inline 3 r6rs:op
[(e1 e2) (go e1 e2)]
[(e1 e2 . e*) (reducer src sexpr moi e1 e2 e*)]))]))
(fx-pred fx< fx<? RELOP< <)
(fx-pred fx<= fx<=? RELOP<= <=)
(fx-pred fx= fx=? RELOP= eq?)
(fx-pred fx>= fx>=? RELOP>= >=)
(fx-pred fx> fx>? RELOP> >))
(let () ; level 3 fxlogand, ...
(define-syntax fxlogop
(syntax-rules ()
[(_ op inline-op base)
(define-inline 3 op
[() `(immediate ,(fix base))]
[(e) e]
[(e1 e2) (%inline inline-op ,e1 ,e2)]
[(e1 . e*) (reduce src sexpr moi e1 e*)])]))
(fxlogop fxlogand logand -1)
(fxlogop fxand logand -1)
(fxlogop fxlogor logor 0)
(fxlogop fxlogior logor 0)
(fxlogop fxior logor 0)
(fxlogop fxlogxor logxor 0)
(fxlogop fxxor logxor 0))
(let ()
(define log-partition
(lambda (p base e*)
(let loop ([e* e*] [n base] [nc* '()])
(if (null? e*)
(if (and (fixnum? n) (fx= n base) (not (null? nc*)))
(values (car nc*) (cdr nc*) nc*)
(values `(immediate ,(fix n)) nc* nc*))
(let ([e (car e*)])
(if (fixnum-constant? e)
(let ([m (constant-value e)])
(loop (cdr e*) (if n (p n m) m) nc*))
(loop (cdr e*) n (cons e nc*))))))))
(let () ; level 2 fxlogor, fxlogior, fxor
(define-syntax fxlogorop
(syntax-rules ()
[(_ op)
(let ()
(define (go src sexpr e*)
(and (fx<= (length e*) inline-args-limit)
(list-bind #t (e*)
(let-values ([(e e* nc*) (log-partition logor 0 e*)])
(bind #t ([t (fold-left (lambda (e1 e2) (%inline logor ,e1 ,e2)) e e*)])
`(if ,(%type-check mask-fixnum type-fixnum ,t)
,t
,(case (length nc*)
[(1) (build-libcall #t src sexpr op (car nc*) `(immediate ,(fix 0)))]
[(2) (build-libcall #t src sexpr op (car nc*) (cadr nc*))]
; TODO: need fxargerr library routine w/who arg & rest interface
[else `(call ,(make-info-call src sexpr #f #t #t) #f ,(Symref 'op) ,nc* (... ...))]))))))) ; NB: should be error call---but is it?
(define-inline 2 op
[() `(immediate ,(fix 0))]
[e* (go src sexpr e*)]))]))
(fxlogorop fxlogor)
(fxlogorop fxlogior)
(fxlogorop fxior))
(let () ; level 2 fxlogand, ...
(define-syntax fxlogop
(syntax-rules ()
[(_ op inline-op base)
(define-inline 2 op
[() `(immediate ,(fix base))]
[e* (and (fx<= (length e*) (fx- inline-args-limit 1))
(list-bind #t (e*)
;; NB: using inline-op here because it works when target's
;; NB: fixnum range is larger than the host's fixnum range
;; NB: during cross compile
(let-values ([(e e* nc*) (log-partition inline-op base e*)])
`(if ,(build-fixnums? nc*)
,(fold-left (lambda (e1 e2) (%inline inline-op ,e1 ,e2)) e e*)
; TODO: need fxargerr library routine w/who arg & rest interface
,(case (length nc*)
[(1) (build-libcall #t src sexpr op (car nc*) `(immediate ,(fix 0)))]
[(2) (build-libcall #t src sexpr op (car nc*) (cadr nc*))]
; TODO: need fxargerr library routine w/who arg & rest interface
[else `(call ,(make-info-call src sexpr #f #t #t) #f ,(Symref 'op) ,nc* (... ...))])))))])])) ; NB: should be error call---but is it?
(fxlogop fxlogand logand -1)
(fxlogop fxand logand -1)
(fxlogop fxlogxor logxor 0)
(fxlogop fxxor logxor 0)))
(define-inline 3 fxlogtest
[(e1 e2) (%inline logtest ,e1 ,e2)])
(define-inline 2 fxlogtest
[(e1 e2)
(bind #t (e1 e2)
`(if ,(build-fixnums? (list e1 e2))
,(%inline logtest ,e1 ,e2)
,(build-libcall #t src sexpr fxlogtest e1 e2)))])
(let ()
(define xorbits (lognot (constant mask-fixnum)))
(define-syntax fxlognotop
(syntax-rules ()
[(_ name)
(begin
(define-inline 3 name
[(e) (%inline logxor ,e (immediate ,xorbits))])
(define-inline 2 name
[(e) (bind #t (e)
`(if ,(%type-check mask-fixnum type-fixnum ,e)
,(%inline logxor ,e (immediate ,xorbits))
,(build-libcall #t src sexpr name e)))]))]))
(fxlognotop fxlognot)
(fxlognotop fxnot))
(define-inline 3 $fxu<
[(e1 e2) (or (relop-length RELOP< e1 e2)
(%inline u< ,e1 ,e2))])
(define-inline 3 fx+
[() `(immediate 0)]
[(e) e]
[(e1 e2) (%inline + ,e1 ,e2)]
[(e1 . e*) (reduce src sexpr moi e1 e*)])
(define-inline 3 r6rs:fx+ ; limited to two arguments
[(e1 e2) (%inline + ,e1 ,e2)])
(define-inline 3 fx1+
[(e) (%inline + ,e (immediate ,(fix 1)))])
(define-inline 2 $fx+?
[(e1 e2)
(let ([Lfalse (make-local-label 'Lfalse)])
(bind #t (e1 e2)
`(if ,(build-fixnums? (list e1 e2))
,(bind #f ([t (%inline +/ovfl ,e1 ,e2)])
`(if (inline ,(make-info-condition-code 'overflow #f #t) ,%condition-code)
(label ,Lfalse ,(%constant sfalse))
,t))
(goto ,Lfalse))))])
(let ()
(define (go src sexpr e1 e2)
(let ([Llib (make-local-label 'Llib)])
(bind #t (e1 e2)
`(if ,(build-fixnums? (list e1 e2))
,(bind #f ([t (%inline +/ovfl ,e1 ,e2)])
`(if (inline ,(make-info-condition-code 'overflow #f #t) ,%condition-code)
(label ,Llib ,(build-libcall #t src sexpr fx+ e1 e2))
,t))
(goto ,Llib)))))
(define-inline 2 fx+
[() `(immediate 0)]
[(e)
(bind #t (e)
`(if ,(%type-check mask-fixnum type-fixnum ,e)
,e
,(build-libcall #t #f sexpr fx+ e `(immediate ,(fix 0)))))]
[(e1 e2) (go src sexpr e1 e2)]
; TODO: 3-operand case requires 3-operand library routine
#;[(e1 e2 e3)
(let ([Llib (make-local-label 'Llib)])
(bind #t (e1 e2 e3)
`(if ,(build-fixnums? (list e1 e2 e3))
,(bind #t ([t (%inline +/ovfl ,e1 ,e2)])
`(if (inline ,(make-info-condition-code 'overflow #f #t) ,%condition-code)
(label ,Llib ,(build-libcall #t src sexpr fx+ e1 e2 e3))
,(bind #t ([t (%inline +/ovfl ,t ,e3)])
`(if (inline ,(make-info-condition-code 'overflow #f #t) ,%condition-code)
(goto ,Llib)
,t))))
(goto ,Llib))))]
[(e1 . e*) #f])
(define-inline 2 r6rs:fx+ ; limited to two arguments
[(e1 e2) (go src sexpr e1 e2)]))
(define-inline 3 fx-
[(e) (%inline - (immediate 0) ,e)]
[(e1 e2) (%inline - ,e1 ,e2)]
[(e1 . e*) (reduce src sexpr moi e1 e*)])
(define-inline 3 r6rs:fx- ; limited to one or two arguments
[(e) (%inline - (immediate 0) ,e)]
[(e1 e2) (%inline - ,e1 ,e2)])
(define-inline 3 fx1-
[(e) (%inline - ,e (immediate ,(fix 1)))])
(define-inline 2 $fx-?
[(e1 e2)
(let ([Lfalse (make-local-label 'Lfalse)])
(bind #t (e1 e2)
`(if ,(build-fixnums? (list e1 e2))
,(bind #f ([t (%inline -/ovfl ,e1 ,e2)])
`(if (inline ,(make-info-condition-code 'overflow #f #t) ,%condition-code)
(label ,Lfalse ,(%constant sfalse))
,t))
(goto ,Lfalse))))])
(let ()
(define (go src sexpr e1 e2)
(let ([Llib (make-local-label 'Llib)])
(bind #t (e1 e2)
`(if ,(build-fixnums? (list e1 e2))
,(bind #t ([t (%inline -/ovfl ,e1 ,e2)])
`(if (inline ,(make-info-condition-code 'overflow #f #t) ,%condition-code)
(label ,Llib ,(build-libcall #t src sexpr fx- e1 e2))
,t))
(goto ,Llib)))))
(define-inline 2 fx-
[(e) (go src sexpr `(immediate ,(fix 0)) e)]
[(e1 e2) (go src sexpr e1 e2)]
; TODO: 3-operand case requires 3-operand library routine
#;[(e1 e2 e3)
(let ([Llib (make-local-label 'Llib)])
(bind #t (e1 e2 e3)
`(if ,(build-fixnums? (list e1 e2 e3))
,(bind #t ([t (%inline -/ovfl ,e1 ,e2)])
`(if (inline ,(make-info-condition-code 'overflow #f #t) ,%condition-code)
(label ,Llib ,(build-libcall #t src sexpr fx- e1 e2 e3))
,(bind #t ([t (%inline -/ovfl ,t ,e3)])
`(if (inline ,(make-info-condition-code 'overflow #f #t) ,%condition-code)
(goto ,Llib)
,t))))
(goto ,Llib))))]
[(e1 . e*) #f])
(define-inline 2 r6rs:fx- ; limited to one or two arguments
[(e) (go src sexpr `(immediate ,(fix 0)) e)]
[(e1 e2) (go src sexpr e1 e2)]))
(define-inline 2 fx1-
[(e) (let ([Llib (make-local-label 'Llib)])
(bind #t (e)
`(if ,(build-fixnums? (list e))
,(bind #t ([t (%inline -/ovfl ,e (immediate ,(fix 1)))])
`(if (inline ,(make-info-condition-code 'overflow #f #t) ,%condition-code)
(label ,Llib ,(build-libcall #t src sexpr fx1- e))
,t))
(goto ,Llib))))])
(define-inline 2 fx1+
[(e) (let ([Llib (make-local-label 'Llib)])
(bind #t (e)
`(if ,(build-fixnums? (list e))
,(bind #f ([t (%inline +/ovfl ,e (immediate ,(fix 1)))])
`(if (inline ,(make-info-condition-code 'overflow #f #t) ,%condition-code)
(label ,Llib ,(build-libcall #t src sexpr fx1+ e))
,t))
(goto ,Llib))))])
(let ()
(define fixnum-powers-of-two
(let f ([m 2] [e 1])
(if (<= m (constant most-positive-fixnum))
(cons (cons m e) (f (* m 2) (fx+ e 1)))
'())))
(define-inline 3 fxdiv
[(e1 e2)
(nanopass-case (L7 Expr) e2
[(quote ,d)
(let ([a (assv d fixnum-powers-of-two)])
(and a
(%inline logand
,(%inline sra ,e1 (immediate ,(cdr a)))
(immediate ,(- (constant fixnum-factor))))))]
[else #f])])
(define-inline 3 fxmod
[(e1 e2)
(nanopass-case (L7 Expr) e2
[(quote ,d)
(let ([a (assv d fixnum-powers-of-two)])
(and a (%inline logand ,e1 (immediate ,(fix (- d 1))))))]
[else #f])])
(let ()
(define (build-fx* e1 e2 ovfl?)
(define (fx*-constant e n)
(if ovfl?
(%inline */ovfl ,e (immediate ,n))
(cond
[(eqv? n 1) e]
[(eqv? n -1) (%inline - (immediate 0) ,e)]
[(eqv? n 2) (%inline sll ,e (immediate 1))]
[(eqv? n 3)
(bind #t (e)
(%inline +
,(%inline + ,e ,e)
,e))]
[(eqv? n 10)
(bind #t (e)
(%inline +
,(%inline +
,(%inline sll ,e (immediate 3))
,e)
,e))]
[(assv n fixnum-powers-of-two) =>
(lambda (a) (%inline sll ,e (immediate ,(cdr a))))]
[else (%inline * ,e (immediate ,n))])))
(nanopass-case (L7 Expr) e2
[(quote ,d) (guard (target-fixnum? d)) (fx*-constant e1 d)]
[else
(nanopass-case (L7 Expr) e1
[(quote ,d) (guard (target-fixnum? d)) (fx*-constant e2 d)]
[else
(let ([t (make-tmp 't 'uptr)])
`(let ([,t ,(build-unfix e2)])
,(if ovfl?
(%inline */ovfl ,e1 ,t)
(%inline * ,e1 ,t))))])]))
(define-inline 3 fx*
[() `(immediate ,(fix 1))]
[(e) e]
[(e1 e2) (build-fx* e1 e2 #f)]
[(e1 . e*) (reduce src sexpr moi e1 e*)])
(define-inline 3 r6rs:fx* ; limited to two arguments
[(e1 e2) (build-fx* e1 e2 #f)])
(let ()
(define (go src sexpr e1 e2)
(let ([Llib (make-local-label 'Llib)])
(bind #t (e1 e2)
`(if ,(build-fixnums? (list e1 e2))
,(bind #t ([t (build-fx* e1 e2 #t)])
`(if (inline ,(make-info-condition-code 'multiply-overflow #f #t) ,%condition-code)
(label ,Llib ,(build-libcall #t src sexpr fx* e1 e2))
,t))
(goto ,Llib)))))
(define-inline 2 fx*
[() `(immediate ,(fix 1))]
[(e)
(bind #t (e)
`(if ,(%type-check mask-fixnum type-fixnum ,e)
,e
,(build-libcall #t src sexpr fx* e `(immediate ,(fix 0)))))]
[(e1 e2) (go src sexpr e1 e2)]
; TODO: 3-operand case requires 3-operand library routine
#;[(e1 e2 e3)
(let ([Llib (make-local-label 'Llib)])
(bind #t (e1 e2 e3)
`(if ,(build-fixnums? (list e1 e2 e3))
,(bind #t ([t (build-fx* e1 e2 #t)])
`(if (inline ,(make-info-condition-code 'overflow #f #t) ,%condition-code)
(label ,Llib ,(build-libcall #t src sexpr fx* e1 e2 e3))
,(bind #t ([t (build-fx* t e3 #t)])
`(if (inline ,(make-info-condition-code 'overflow #f #t) ,%condition-code)
(goto ,Llib)
,t))))
(goto ,Llib))))]
[(e1 . e*) #f])
(define-inline 2 r6rs:fx* ; limited to two arguments
[(e1 e2) (go src sexpr e1 e2)]))
(let ()
(define build-fx/p2
(lambda (e1 p2)
(bind #t (e1)
(build-fix
(%inline sra
,(%inline + ,e1
,(%inline srl
,(if (fx= p2 1)
e1
(%inline sra ,e1 (immediate ,(fx- p2 1))))
(immediate ,(fx- (constant fixnum-bits) p2))))
(immediate ,(fx+ p2 (constant fixnum-offset))))))))
(define build-fx/
(lambda (src sexpr e1 e2)
(or (nanopass-case (L7 Expr) e2
[(quote ,d)
(let ([a (assv d fixnum-powers-of-two)])
(and a (build-fx/p2 e1 (cdr a))))]
[else #f])
(if (constant integer-divide-instruction)
(build-fix (%inline / ,e1 ,e2))
`(call ,(make-info-call src sexpr #f #f #f) #f
,(lookup-primref 3 '$fx/)
,e1 ,e2)))))
(define-inline 3 fx/
[(e) (build-fx/ src sexpr `(quote 1) e)]
[(e1 e2) (build-fx/ src sexpr e1 e2)]
[(e1 . e*) (reduce src sexpr moi e1 e*)])
(define-inline 3 fxquotient
[(e) (build-fx/ src sexpr `(quote 1) e)]
[(e1 e2) (build-fx/ src sexpr e1 e2)]
[(e1 . e*) (reduce src sexpr moi e1 e*)])
(define-inline 3 fxremainder
[(e1 e2)
(bind #t (e1 e2)
(%inline - ,e1
,(build-fx*
(build-fx/ src sexpr e1 e2)
e2 #f)))]))))
(let ()
(define do-fxsll
(lambda (e1 e2)
(nanopass-case (L7 Expr) e2
[(quote ,d)
(%inline sll ,e1 (immediate ,d))]
[else
; TODO: bind-uptr might be handy here and also a make-unfix
(let ([t (make-tmp 't 'uptr)])
`(let ([,t ,(build-unfix e2)])
,(%inline sll ,e1 ,t)))])))
(define-inline 3 fxsll
[(e1 e2) (do-fxsll e1 e2)])
(define-inline 3 fxarithmetic-shift-left
[(e1 e2) (do-fxsll e1 e2)]))
(define-inline 3 fxsrl
[(e1 e2)
(%inline logand
,(nanopass-case (L7 Expr) e2
[(quote ,d)
(%inline srl ,e1 (immediate ,d))]
[else
(let ([t (make-tmp 't 'uptr)])
`(let ([,t ,(build-unfix e2)])
,(%inline srl ,e1 ,t)))])
(immediate ,(fx- (constant fixnum-factor))))])
(let ()
(define do-fxsra
(lambda (e1 e2)
(%inline logand
,(nanopass-case (L7 Expr) e2
[(quote ,d)
(%inline sra ,e1 (immediate ,d))]
[else
(let ([t (make-tmp 't 'uptr)])
`(let ([,t ,(build-unfix e2)])
,(%inline sra ,e1 ,t)))])
(immediate ,(fx- (constant fixnum-factor))))))
(define-inline 3 fxsra
[(e1 e2) (do-fxsra e1 e2)])
(define-inline 3 fxarithmetic-shift-right
[(e1 e2) (do-fxsra e1 e2)]))
(let ()
(define-syntax %safe-shift
(syntax-rules ()
[(_ src sexpr op libcall e1 e2 ?size)
(let ([size ?size])
(if (constant? (lambda (x) (and (fixnum? x) (fx<= 0 x (fx- size 1)))) e2)
(bind #t (e1 e2)
`(if ,(build-fixnums? (list e1))
,(%inline logand
,(%inline op ,e1 (immediate ,(constant-value e2)))
(immediate ,(- (constant fixnum-factor))))
,(build-libcall #t src sexpr libcall e1 e2)))
(bind #t (e1 e2)
`(if ,(build-and
(build-fixnums? (list e1 e2))
(%inline u< ,e2 (immediate ,(fix size))))
,(%inline logand
,(%inline op ,e1 ,(build-unfix e2))
(immediate ,(- (constant fixnum-factor))))
,(build-libcall #t src sexpr libcall e1 e2)))))]))
(define-inline 2 fxsrl
[(e1 e2) (%safe-shift src sexpr srl fxsrl e1 e2 (+ (constant fixnum-bits) 1))])
(define-inline 2 fxsra
[(e1 e2) (%safe-shift src sexpr sra fxsra e1 e2 (+ (constant fixnum-bits) 1))])
(define-inline 2 fxarithmetic-shift-right
[(e1 e2) (%safe-shift src sexpr sra fxarithmetic-shift-right e1 e2 (constant fixnum-bits))]))
(define-inline 3 fxarithmetic-shift
[(e1 e2)
(or (nanopass-case (L7 Expr) e2
[(quote ,d)
(and (fixnum? d)
(if ($fxu< d (constant fixnum-bits))
(%inline sll ,e1 (immediate ,d))
(and (fx< (fx- (constant fixnum-bits)) d 0)
(%inline logand
,(%inline sra ,e1 (immediate ,(fx- d)))
(immediate ,(- (constant fixnum-factor)))))))]
[else #f])
(build-libcall #f src sexpr fxarithmetic-shift e1 e2))])
(define-inline 2 fxarithmetic-shift
[(e1 e2)
(or (nanopass-case (L7 Expr) e2
[(quote ,d)
(guard (fixnum? d) (fx< (fx- (constant fixnum-bits)) d 0))
(bind #t (e1)
`(if ,(build-fixnums? (list e1))
,(%inline logand
,(%inline sra ,e1 (immediate ,(fx- d)))
(immediate ,(- (constant fixnum-factor))))
,(build-libcall #t src sexpr fxarithmetic-shift e1 e2)))]
[else #f])
(build-libcall #f src sexpr fxarithmetic-shift e1 e2))])
(let ()
(define dofxlogbit0
(lambda (e1 e2)
(if (constant? (lambda (x) (and (fixnum? x) ($fxu< x (fx- (constant fixnum-bits) 1)))) e2)
(%inline logand ,e1
(immediate ,(fix (lognot (ash 1 (constant-value e2))))))
(%inline logand ,e1
,(%inline lognot
,(%inline sll (immediate ,(fix 1))
,(build-unfix e2)))))))
(define dofxlogbit1
(lambda (e1 e2)
(if (constant? (lambda (x) (and (fixnum? x) ($fxu< x (fx- (constant fixnum-bits) 1)))) e2)
(%inline logor ,e1
(immediate ,(fix (ash 1 (constant-value e2)))))
(%inline logor ,e1
,(%inline sll (immediate ,(fix 1))
,(build-unfix e2))))))
(define-inline 3 fxlogbit0
[(e1 e2) (dofxlogbit0 e2 e1)])
(define-inline 3 fxlogbit1
[(e1 e2) (dofxlogbit1 e2 e1)])
(define-inline 3 fxcopy-bit
[(e1 e2 e3)
(and (fixnum-constant? e3)
(case (constant-value e3)
[(0) (dofxlogbit0 e1 e2)]
[(1) (dofxlogbit1 e1 e2)]
[else #f]))]))
(let ()
(define dofxlogbit0
(lambda (e1 e2 libcall)
(if (constant? (lambda (x) (and (fixnum? x) ($fxu< x (fx- (constant fixnum-bits) 1)))) e2)
(bind #t (e1)
`(if ,(build-fixnums? (list e1))
,(%inline logand ,e1
(immediate ,(fix (lognot (ash 1 (constant-value e2))))))
,(libcall e1 e2)))
(bind #t (e1 e2)
`(if ,(build-and
(build-fixnums? (list e1 e2))
(%inline u< ,e2 (immediate ,(fix (fx- (constant fixnum-bits) 1)))))
,(%inline logand ,e1
,(%inline lognot
,(%inline sll (immediate ,(fix 1))
,(build-unfix e2))))
,(libcall e1 e2))))))
(define dofxlogbit1
(lambda (e1 e2 libcall)
(if (constant? (lambda (x) (and (fixnum? x) ($fxu< x (fx- (constant fixnum-bits) 1)))) e2)
(bind #t (e1)
`(if ,(build-fixnums? (list e1))
,(%inline logor ,e1
(immediate ,(fix (ash 1 (constant-value e2)))))
,(libcall e1 e2)))
(bind #t (e1 e2)
`(if ,(build-and
(build-fixnums? (list e1 e2))
(%inline u< ,e2 (immediate ,(fix (fx- (constant fixnum-bits) 1)))))
,(%inline logor ,e1
,(%inline sll (immediate ,(fix 1))
,(build-unfix e2)))
,(libcall e1 e2))))))
(define-inline 2 fxlogbit0
[(e1 e2) (dofxlogbit0 e2 e1
(lambda (e2 e1)
(build-libcall #t src sexpr fxlogbit0 e1 e2)))])
(define-inline 2 fxlogbit1
[(e1 e2) (dofxlogbit1 e2 e1
(lambda (e2 e1)
(build-libcall #t src sexpr fxlogbit1 e1 e2)))])
(define-inline 2 fxcopy-bit
[(e1 e2 e3)
(and (fixnum-constant? e3)
(case (constant-value e3)
[(0) (dofxlogbit0 e1 e2
(lambda (e1 e2)
(build-libcall #t src sexpr fxcopy-bit e1 e2)))]
[(1) (dofxlogbit1 e1 e2
(lambda (e1 e2)
(build-libcall #t src sexpr fxcopy-bit e1 e2)))]
[else #f]))]))
(define-inline 3 fxzero?
[(e) (or (relop-length RELOP= e) (%inline eq? ,e (immediate 0)))])
(define-inline 3 fxpositive?
[(e) (or (relop-length RELOP> e) (%inline > ,e (immediate 0)))])
(define-inline 3 fxnonnegative?
[(e) (or (relop-length RELOP>= e) (%inline >= ,e (immediate 0)))])
(define-inline 3 fxnegative?
[(e) (or (relop-length RELOP< e) (%inline < ,e (immediate 0)))])
(define-inline 3 fxnonpositive?
[(e) (or (relop-length RELOP<= e) (%inline <= ,e (immediate 0)))])
(define-inline 3 fxeven?
[(e) (%inline eq?
,(%inline logand ,e (immediate ,(fix 1)))
(immediate ,(fix 0)))])
(define-inline 3 fxodd?
[(e) (%inline eq?
,(%inline logand ,e (immediate ,(fix 1)))
(immediate ,(fix 1)))])
(define-inline 2 fxzero?
[(e) (or (relop-length RELOP= e)
(bind #t (e)
(build-simple-or
(%inline eq? ,e (immediate 0))
`(if ,(build-fixnums? (list e))
,(%constant sfalse)
,(build-libcall #t src sexpr fxzero? e)))))])
(define-inline 2 fxpositive?
[(e) (or (relop-length RELOP> e)
(bind #t (e)
`(if ,(build-fixnums? (list e))
,(%inline > ,e (immediate 0))
,(build-libcall #t src sexpr fxpositive? e))))])
(define-inline 2 fxnonnegative?
[(e) (or (relop-length RELOP>= e)
(bind #t (e)
`(if ,(build-fixnums? (list e))
,(%inline >= ,e (immediate 0))
,(build-libcall #t src sexpr fxnonnegative? e))))])
(define-inline 2 fxnegative?
[(e) (or (relop-length RELOP< e)
(bind #t (e)
`(if ,(build-fixnums? (list e))
,(%inline < ,e (immediate 0))
,(build-libcall #t src sexpr fxnegative? e))))])
(define-inline 2 fxnonpositive?
[(e) (or (relop-length RELOP<= e)
(bind #t (e)
`(if ,(build-fixnums? (list e))
,(%inline <= ,e (immediate 0))
,(build-libcall #t src sexpr fxnonpositive? e))))])
(define-inline 2 fxeven?
[(e) (bind #t (e)
`(if ,(build-fixnums? (list e))
,(%inline eq?
,(%inline logand ,e (immediate ,(fix 1)))
(immediate ,(fix 0)))
,(build-libcall #t src sexpr fxeven? e)))])
(define-inline 2 fxodd?
[(e) (bind #t (e)
`(if ,(build-fixnums? (list e))
,(%inline eq?
,(%inline logand ,e (immediate ,(fix 1)))
(immediate ,(fix 1)))
,(build-libcall #t src sexpr fxodd? e)))])
(let ()
(define dofxlogbit?
(lambda (e1 e2)
(cond
[(constant? (lambda (x) (and (fixnum? x) (fx<= 0 x (fx- (constant fixnum-bits) 2)))) e1)
(%inline logtest ,e2 (immediate ,(fix (ash 1 (constant-value e1)))))]
[(constant? (lambda (x) (and (target-fixnum? x) (> x (fx- (constant fixnum-bits) 2)))) e1)
(%inline < ,e2 (immediate ,(fix 0)))]
[(fixnum-constant? e2)
(bind #t (e1)
`(if ,(%inline < (immediate ,(fix (fx- (constant fixnum-bits) 2))) ,e1)
,(if (< (constant-value e2) 0) (%constant strue) (%constant sfalse))
,(%inline logtest
,(%inline sra ,e2 ,(build-unfix e1))
(immediate ,(fix 1)))))]
[else
(bind #t (e1 e2)
`(if ,(%inline < (immediate ,(fix (fx- (constant fixnum-bits) 2))) ,e1)
,(%inline < ,e2 (immediate ,(fix 0)))
,(%inline logtest
,(%inline sra ,e2 ,(build-unfix e1))
(immediate ,(fix 1)))))])))
(define-inline 3 fxbit-set?
[(e1 e2) (dofxlogbit? e2 e1)])
(define-inline 3 fxlogbit?
[(e1 e2) (dofxlogbit? e1 e2)]))
(let ()
(define dofxlogbit?
(lambda (e1 e2 libcall)
(cond
[(constant? (lambda (x) (and (fixnum? x) (fx<= 0 x (fx- (constant fixnum-bits) 2)))) e1)
(bind #t (e2)
`(if ,(build-fixnums? (list e2))
,(%inline logtest ,e2
(immediate ,(fix (ash 1 (constant-value e1)))))
,(libcall e1 e2)))]
[(constant? (lambda (x) (and (target-fixnum? x) (> x (fx- (constant fixnum-bits) 2)))) e1)
(bind #t (e2)
`(if ,(build-fixnums? (list e2))
,(%inline < ,e2 (immediate ,(fix 0)))
,(libcall e1 e2)))]
[else
(bind #t (e1 e2)
`(if ,(build-and
(build-fixnums? (list e1 e2))
(%inline u< ,e1 (immediate ,(fix (constant fixnum-bits)))))
,(%inline logtest
,(%inline sra ,e2 ,(build-unfix e1))
(immediate ,(fix 1)))
,(libcall e1 e2)))])))
(define-inline 2 fxbit-set?
[(e1 e2) (dofxlogbit? e2 e1
(lambda (e2 e1)
(build-libcall #t src sexpr fxbit-set? e1 e2)))])
(define-inline 2 fxlogbit?
[(e1 e2) (dofxlogbit? e1 e2
(lambda (e1 e2)
(build-libcall #t src sexpr fxlogbit? e1 e2)))]))
; can avoid if in fxabs with:
; t = sra(x, k) ; where k is ptr-bits - 1
; ; t is now -1 if x's sign bit set, otherwise 0
; x = xor(x, t) ; logical not if x negative, otherwise leave x alone
; x = x - t ; add 1 to complete two's complement negation if
; ; x was negative, otherwise leave x alone
; tests on i3le indicate that the if is actually faster, even in a loop
; where input alternates between positive and negative to defeat branch
; prediction.
(define-inline 3 fxabs
[(e) (bind #t (e)
`(if ,(%inline < ,e (immediate ,(fix 0)))
,(%inline - (immediate ,(fix 0)) ,e)
,e))])
;(define-inline 3 min ; needs library min
; ; must take care to be inexactness-preserving
; [(e0) e0]
; [(e0 e1)
; (bind #t (e0 e1)
; `(if ,(build-fixnums? (list e0 e1))
; (if ,(%inline < ,e0 ,e1) ,e0 ,e1)
; ,(build-libcall #t src sexpr min e0 e1)))]
; [(e0 . e*) (reduce src sexpr moi e1 e*)])
;
;(define-inline 3 max ; needs library max
; ; must take care to be inexactness-preserving
; [(e0) e0]
; [(e0 e1)
; (bind #t (e0 e1)
; `(if ,(build-fixnums? (list e0 e1))
; (if ,(%inline < ,e0 ,e1) ,e0 ,e1)
; ,(build-libcall #t src sexpr max e0 e1)))]
; [(e1 . e*) (reduce src sexpr moi e1 e*)])
(define-inline 3 fxmin
[(e) e]
[(e1 e2) (bind #t (e1 e2)
`(if ,(%inline < ,e1 ,e2)
,e1
,e2))]
[(e1 . e*) (reduce src sexpr moi e1 e*)])
(define-inline 3 fxmax
[(e) e]
[(e1 e2) (bind #t (e1 e2)
`(if ,(%inline < ,e2 ,e1)
,e1
,e2))]
[(e1 . e*) (reduce src sexpr moi e1 e*)])
(define-inline 3 fxif
[(e1 e2 e3)
(bind #t (e1)
(%inline logor
,(%inline logand ,e2 ,e1)
,(%inline logand ,e3
,(%inline lognot ,e1))))])
(define-inline 3 fxbit-field
[(e1 e2 e3)
(and (constant? fixnum? e2) (constant? fixnum? e3)
(let ([start (constant-value e2)] [end (constant-value e3)])
(if (fx= end start)
(%seq ,e1 (immediate ,(fix 0)))
(and (and (fx>= start 0) (fx> end start) (fx< end (constant fixnum-bits)))
(extract-unsigned-bitfield #f start end e1)))))])
(define-inline 3 fxcopy-bit-field
[(e1 e2 e3 e4)
(and (constant? fixnum? e2) (constant? fixnum? e3)
(let ([start (constant-value e2)] [end (constant-value e3)])
(if (fx= end start)
e1
(and (and (fx>= start 0) (fx> end start) (fx< end (constant fixnum-bits)))
(insert-bitfield #f start end (constant fixnum-bits) e1 e4)))))])
;; could be done with one mutable variable instead of two, but this seems to generate
;; the same code as the existing compiler
(define-inline 3 fxlength
[(e)
(let ([t (make-assigned-tmp 't 'uptr)] [result (make-assigned-tmp 'result)])
`(let ([,t ,(build-unfix e)])
(seq
(if ,(%inline < ,t (immediate 0))
(set! ,t ,(%inline lognot ,t))
,(%constant svoid))
(let ([,result (immediate ,(fix 0))])
,((lambda (body)
(constant-case fixnum-bits
[(30) body]
[(61)
`(seq
(if ,(%inline < ,t (immediate #x100000000))
,(%constant svoid)
(seq
(set! ,t ,(%inline srl ,t (immediate 32)))
(set! ,result
,(%inline + ,result (immediate ,(fix 32))))))
,body)]))
(%seq
(if ,(%inline < ,t (immediate #x10000))
,(%constant svoid)
(seq
(set! ,t ,(%inline srl ,t (immediate 16)))
(set! ,result ,(%inline + ,result (immediate ,(fix 16))))))
(if ,(%inline < ,t (immediate #x100))
,(%constant svoid)
(seq
(set! ,t ,(%inline srl ,t (immediate 8)))
(set! ,result ,(%inline + ,result (immediate ,(fix 8))))))
,(%inline + ,result
(inline ,(make-info-load 'unsigned-8 #f) ,%load
,(%tc-ref fxlength-bv) ,t
,(%constant bytevector-data-disp)))))))))])
(define-inline 3 fxfirst-bit-set
[(e)
(let ([t (make-assigned-tmp 't 'uptr)] [result (make-assigned-tmp 'result)])
(bind #t (e)
`(if ,(%inline eq? ,e (immediate ,(fix 0)))
(immediate ,(fix -1))
(let ([,t ,(build-unfix e)] [,result (immediate ,(fix 0))])
,((lambda (body)
(constant-case fixnum-bits
[(30) body]
[(61)
`(seq
(if ,(%inline logtest ,t (immediate #xffffffff))
,(%constant svoid)
(seq
(set! ,t ,(%inline srl ,t (immediate 32)))
(set! ,result ,(%inline + ,result (immediate ,(fix 32))))))
,body)]))
(%seq
(if ,(%inline logtest ,t (immediate #xffff))
,(%constant svoid)
(seq
(set! ,t ,(%inline srl ,t (immediate 16)))
(set! ,result ,(%inline + ,result (immediate ,(fix 16))))))
(if ,(%inline logtest ,t (immediate #xff))
,(%constant svoid)
(seq
(set! ,t ,(%inline srl ,t (immediate 8)))
(set! ,result ,(%inline + ,result (immediate ,(fix 8))))))
,(%inline + ,result
(inline ,(make-info-load 'unsigned-8 #f) ,%load
,(%tc-ref fxfirst-bit-set-bv)
,(%inline logand ,t (immediate #xff))
,(%constant bytevector-data-disp)))))))))])
(let ()
(define-syntax type-pred
(syntax-rules ()
[(_ name? mask type)
(define-inline 2 name?
[(e) (%type-check mask type ,e)])]))
(define-syntax typed-object-pred
(syntax-rules ()
[(_ name? mask type)
(define-inline 2 name?
[(e)
(bind #t (e)
(%typed-object-check mask type ,e))])]))
(type-pred boolean? mask-boolean type-boolean)
(type-pred bwp-object? mask-bwp sbwp)
(type-pred char? mask-char type-char)
(type-pred eof-object? mask-eof seof)
(type-pred fixnum? mask-fixnum type-fixnum)
(type-pred flonum? mask-flonum type-flonum)
(type-pred null? mask-nil snil)
(type-pred pair? mask-pair type-pair)
(type-pred procedure? mask-closure type-closure)
(type-pred symbol? mask-symbol type-symbol)
(type-pred $unbound-object? mask-unbound sunbound)
(typed-object-pred bignum? mask-bignum type-bignum)
(typed-object-pred box? mask-box type-box)
(typed-object-pred mutable-box? mask-mutable-box type-mutable-box)
(typed-object-pred immutable-box? mask-mutable-box type-immutable-box)
(typed-object-pred bytevector? mask-bytevector type-bytevector)
(typed-object-pred mutable-bytevector? mask-mutable-bytevector type-mutable-bytevector)
(typed-object-pred immutable-bytevector? mask-mutable-bytevector type-immutable-bytevector)
(typed-object-pred $code? mask-code type-code)
(typed-object-pred $exactnum? mask-exactnum type-exactnum)
(typed-object-pred fxvector? mask-fxvector type-fxvector)
(typed-object-pred mutable-fxvector? mask-mutable-fxvector type-mutable-fxvector)
(typed-object-pred immutable-fxvector? mask-mutable-fxvector type-immutable-fxvector)
(typed-object-pred $inexactnum? mask-inexactnum type-inexactnum)
(typed-object-pred $rtd-counts? mask-rtd-counts type-rtd-counts)
(typed-object-pred input-port? mask-input-port type-input-port)
(typed-object-pred output-port? mask-output-port type-output-port)
(typed-object-pred port? mask-port type-port)
(typed-object-pred ratnum? mask-ratnum type-ratnum)
(typed-object-pred $record? mask-record type-record)
(typed-object-pred string? mask-string type-string)
(typed-object-pred mutable-string? mask-mutable-string type-mutable-string)
(typed-object-pred immutable-string? mask-mutable-string type-immutable-string)
(typed-object-pred $system-code? mask-system-code type-system-code)
(typed-object-pred $tlc? mask-tlc type-tlc)
(typed-object-pred vector? mask-vector type-vector)
(typed-object-pred mutable-vector? mask-mutable-vector type-mutable-vector)
(typed-object-pred immutable-vector? mask-mutable-vector type-immutable-vector)
(typed-object-pred thread? mask-thread type-thread))
(define-inline 3 $bigpositive?
[(e) (%type-check mask-signed-bignum type-positive-bignum
,(%mref ,e ,(constant bignum-type-disp)))])
(define-inline 3 csv7:record-field-accessible?
[(e1 e2) (%seq ,e1 ,e2 ,(%constant strue))])
(define-inline 2 cflonum?
[(e) (bind #t (e)
`(if ,(%type-check mask-flonum type-flonum ,e)
,(%constant strue)
,(%typed-object-check mask-inexactnum type-inexactnum ,e)))])
(define-inline 2 $immediate?
[(e) (bind #t (e)
`(if ,(%type-check mask-fixnum type-fixnum ,e)
,(%constant strue)
,(%type-check mask-immediate type-immediate ,e)))])
(define-inline 3 $inexactnum-real-part
[(e) (build-$inexactnum-real-part e)])
(define-inline 3 $inexactnum-imag-part
[(e) (build-$inexactnum-imag-part e)])
(define-inline 3 cfl-real-part
[(e) (bind #t (e)
`(if ,(%type-check mask-flonum type-flonum ,e)
,e
,(build-$inexactnum-real-part e)))])
(define-inline 3 cfl-imag-part
[(e) (bind #t (e)
`(if ,(%type-check mask-flonum type-flonum ,e)
(quote 0.0)
,(build-$inexactnum-imag-part e)))])
(define-inline 3 $closure-ref
[(e-v e-i)
(nanopass-case (L7 Expr) e-i
[(quote ,d)
(guard (target-fixnum? d))
(%mref ,e-v ,(+ (fix d) (constant closure-data-disp)))]
[else (%mref ,e-v ,e-i ,(constant closure-data-disp))])])
(define-inline 3 $closure-code
[(e) (%inline -
,(%mref ,e ,(constant closure-code-disp))
,(%constant code-data-disp))])
(define-inline 3 $code-free-count
[(e) (build-fix (%mref ,e ,(constant code-closure-length-disp)))])
(define-inline 2 $unbound-object
[() `(quote ,($unbound-object))])
(define-inline 2 void
[() `(quote ,(void))])
(define-inline 2 eof-object
[() `(quote #!eof)])
(define-inline 2 cons
[(e1 e2)
(bind #f (e1 e2)
(bind #t ([t (%constant-alloc type-pair (constant size-pair))])
(%seq
(set! ,(%mref ,t ,(constant pair-car-disp)) ,e1)
(set! ,(%mref ,t ,(constant pair-cdr-disp)) ,e2)
,t)))])
(define-inline 2 box
[(e)
(bind #f (e)
(bind #t ([t (%constant-alloc type-typed-object (constant size-box))])
(%seq
(set! ,(%mref ,t ,(constant box-type-disp)) ,(%constant type-box))
(set! ,(%mref ,t ,(constant box-ref-disp)) ,e)
,t)))])
(define-inline 2 box-immutable
[(e)
(bind #f (e)
(bind #t ([t (%constant-alloc type-typed-object (constant size-box))])
(%seq
(set! ,(%mref ,t ,(constant box-type-disp)) ,(%constant type-immutable-box))
(set! ,(%mref ,t ,(constant box-ref-disp)) ,e)
,t)))])
(define-inline 3 $make-tlc
[(e-ht e-keyval e-next)
(bind #f (e-ht e-keyval e-next)
(bind #t ([t (%constant-alloc type-typed-object (constant size-tlc))])
(%seq
(set! ,(%mref ,t ,(constant tlc-type-disp)) ,(%constant type-tlc))
(set! ,(%mref ,t ,(constant tlc-ht-disp)) ,e-ht)
(set! ,(%mref ,t ,(constant tlc-keyval-disp)) ,e-keyval)
(set! ,(%mref ,t ,(constant tlc-next-disp)) ,e-next)
,t)))])
(define-inline 2 list
[e* (build-list e*)])
(let ()
(define (go e e*)
(bind #f (e)
(list-bind #f (e*)
(bind #t ([t (%constant-alloc type-pair (fx* (constant size-pair) (length e*)))])
(let loop ([e e] [e* e*] [i 0])
(let ([e2 (car e*)] [e* (cdr e*)])
`(seq
(set! ,(%mref ,t ,(fx+ i (constant pair-car-disp))) ,e)
,(if (null? e*)
`(seq
(set! ,(%mref ,t ,(fx+ i (constant pair-cdr-disp))) ,e2)
,t)
(let ([next-i (fx+ i (constant size-pair))])
`(seq
(set! ,(%mref ,t ,(fx+ i (constant pair-cdr-disp)))
,(%inline + ,t (immediate ,next-i)))
,(loop e2 e* next-i)))))))))))
(define-inline 2 list*
[(e) e]
[(e . e*) (go e e*)])
(define-inline 2 cons*
[(e) e]
[(e . e*) (go e e*)]))
(define-inline 2 vector
[() `(quote #())]
[e*
(let ([n (length e*)])
(list-bind #f (e*)
(bind #t ([t (%constant-alloc type-typed-object
(fx+ (constant header-size-vector) (fx* n (constant ptr-bytes))))])
(let loop ([e* e*] [i 0])
(if (null? e*)
`(seq
(set! ,(%mref ,t ,(constant vector-type-disp))
(immediate ,(+ (fx* n (constant vector-length-factor))
(constant type-vector))))
,t)
`(seq
(set! ,(%mref ,t ,(fx+ i (constant vector-data-disp))) ,(car e*))
,(loop (cdr e*) (fx+ i (constant ptr-bytes)))))))))])
(let ()
(define (go e*)
(let ([n (length e*)])
(list-bind #f (e*)
(bind #t ([t (%constant-alloc type-typed-object
(fx+ (constant header-size-fxvector) (fx* n (constant ptr-bytes))))])
(let loop ([e* e*] [i 0])
(if (null? e*)
`(seq
(set! ,(%mref ,t ,(constant fxvector-type-disp))
(immediate ,(+ (fx* n (constant fxvector-length-factor))
(constant type-fxvector))))
,t)
`(seq
(set! ,(%mref ,t ,(fx+ i (constant fxvector-data-disp))) ,(car e*))
,(loop (cdr e*) (fx+ i (constant ptr-bytes))))))))))
(define-inline 2 fxvector
[() `(quote #vfx())]
[e* (and (andmap (lambda (x) (constant? target-fixnum? x)) e*) (go e*))])
(define-inline 3 fxvector
[() `(quote #vfx())]
[e* (go e*)]))
(let ()
(define (go e*)
(let ([n (length e*)])
(list-bind #f (e*)
(bind #t ([t (%constant-alloc type-typed-object
(fx+ (constant header-size-string) (fx* n (constant string-char-bytes))))])
(let loop ([e* e*] [i 0])
(if (null? e*)
`(seq
(set! ,(%mref ,t ,(constant string-type-disp))
(immediate ,(+ (fx* n (constant string-length-factor))
(constant type-string))))
,t)
`(seq
(inline ,(make-info-load (string-char-type) #f) ,%store ,t ,%zero
(immediate ,(fx+ i (constant string-data-disp)))
,(car e*))
,(loop (cdr e*) (fx+ i (constant string-char-bytes))))))))))
(define-inline 2 string
[() `(quote "")]
[e* (and (andmap (lambda (x) (constant? char? x)) e*) (go e*))])
(define-inline 3 string
[() `(quote "")]
[e* (go e*)]))
(let () ; level 2 car, cdr, caar, etc.
(define-syntax def-c..r*
(lambda (x)
(define (go ad*)
(let ([id (datum->syntax #'* (string->symbol (format "c~{~a~}r" ad*)))])
#`(define-inline 2 #,id
[(e) (let ([Lerr (make-local-label 'Lerr)])
#,(let f ([ad* ad*])
(let ([builder (if (char=? (car ad*) #\a) #'build-car #'build-cdr)]
[ad* (cdr ad*)])
(if (null? ad*)
#`(bind #t (e)
`(if ,(build-pair? e)
,(#,builder e)
(label ,Lerr ,(build-libcall #t src sexpr #,id e))))
#`(bind #t ([t #,(f ad*)])
`(if ,(build-pair? t)
,(#,builder t)
(goto ,Lerr)))))))])))
(let f ([n 4] [ad* '()])
(let ([f (lambda (ad*)
(let ([defn (go ad*)])
(if (fx= n 1)
defn
#`(begin #,defn #,(f (fx- n 1) ad*)))))])
#`(begin
#,(f (cons #\a ad*))
#,(f (cons #\d ad*)))))))
def-c..r*)
(let () ; level 3 car, cdr, caar, etc.
(define-syntax def-c..r*
(lambda (x)
(define (go ad*)
(let ([id (datum->syntax #'* (string->symbol (format "c~{~a~}r" ad*)))])
#`(define-inline 3 #,id
[(e) #,(let f ([ad* ad*])
(let ([builder (if (char=? (car ad*) #\a) #'build-car #'build-cdr)]
[ad* (cdr ad*)])
(if (null? ad*)
#`(#,builder e)
#`(#,builder #,(f ad*)))))])))
(let f ([n 4] [ad* '()])
(let ([f (lambda (ad*)
(let ([defn (go ad*)])
(if (fx= n 1)
defn
#`(begin #,defn #,(f (fx- n 1) ad*)))))])
#`(begin
#,(f (cons #\a ad*))
#,(f (cons #\d ad*)))))))
def-c..r*)
(let () ; level 3 simple accessors, e.g., unbox, vector-length
(define-syntax inline-accessor
(syntax-rules ()
[(_ prim disp)
(define-inline 3 prim
[(e) (%mref ,e ,(constant disp))])]))
(inline-accessor unbox box-ref-disp)
(inline-accessor $symbol-name symbol-name-disp)
(inline-accessor $symbol-property-list symbol-plist-disp)
(inline-accessor $system-property-list symbol-splist-disp)
(inline-accessor $symbol-hash symbol-hash-disp)
(inline-accessor $ratio-numerator ratnum-numerator-disp)
(inline-accessor $ratio-denominator ratnum-denominator-disp)
(inline-accessor $exactnum-real-part exactnum-real-disp)
(inline-accessor $exactnum-imag-part exactnum-imag-disp)
(inline-accessor binary-port-input-buffer port-ibuffer-disp)
(inline-accessor textual-port-input-buffer port-ibuffer-disp)
(inline-accessor binary-port-output-buffer port-obuffer-disp)
(inline-accessor textual-port-output-buffer port-obuffer-disp)
(inline-accessor $code-name code-name-disp)
(inline-accessor $code-arity-mask code-arity-mask-disp)
(inline-accessor $code-info code-info-disp)
(inline-accessor $code-pinfo* code-pinfo*-disp)
(inline-accessor $continuation-link continuation-link-disp)
(inline-accessor $continuation-winders continuation-winders-disp)
(inline-accessor csv7:record-type-descriptor record-type-disp)
(inline-accessor $record-type-descriptor record-type-disp)
(inline-accessor record-rtd record-type-disp)
(inline-accessor $port-handler port-handler-disp)
(inline-accessor $port-info port-info-disp)
(inline-accessor port-name port-name-disp)
(inline-accessor $thread-tc thread-tc-disp)
)
(define-inline 2 unbox
[(e)
(bind #t (e)
`(if ,(%typed-object-check mask-box type-box ,e)
,(%mref ,e ,(constant box-ref-disp))
,(build-libcall #t src sexpr unbox e)))])
(let ()
(define-syntax def-len
(syntax-rules ()
[(_ prim type-disp length-offset)
(define-inline 3 prim
[(e) (extract-length (%mref ,e ,(constant type-disp)) (constant length-offset))])]))
(def-len vector-length vector-type-disp vector-length-offset)
(def-len fxvector-length fxvector-type-disp fxvector-length-offset)
(def-len string-length string-type-disp string-length-offset)
(def-len bytevector-length bytevector-type-disp bytevector-length-offset)
(def-len $bignum-length bignum-type-disp bignum-length-offset))
(let ()
(define-syntax def-len
(syntax-rules ()
[(_ prim mask type type-disp length-offset)
(define-inline 2 prim
[(e) (let ([Lerr (make-local-label 'Lerr)])
(bind #t (e)
`(if ,(%type-check mask-typed-object type-typed-object ,e)
,(bind #t ([t/l (%mref ,e ,(constant type-disp))])
`(if ,(%type-check mask type ,t/l)
,(extract-length t/l (constant length-offset))
(goto ,Lerr)))
(label ,Lerr ,(build-libcall #t #f sexpr prim e)))))])]))
(def-len vector-length mask-vector type-vector vector-type-disp vector-length-offset)
(def-len fxvector-length mask-fxvector type-fxvector fxvector-type-disp fxvector-length-offset)
(def-len string-length mask-string type-string string-type-disp string-length-offset)
(def-len bytevector-length mask-bytevector type-bytevector bytevector-type-disp bytevector-length-offset))
; TODO: consider adding integer-valued?, rational?, rational-valued?,
; real?, and real-valued?
(define-inline 2 integer?
[(e) (bind #t (e)
(build-simple-or
(%type-check mask-fixnum type-fixnum ,e)
(build-simple-or
(%typed-object-check mask-bignum type-bignum ,e)
(build-and
(%type-check mask-flonum type-flonum ,e)
`(call ,(make-info-call src sexpr #f #f #f) #f ,(lookup-primref 3 'flinteger?) ,e)))))])
(let ()
(define build-number?
(lambda (e)
(bind #t (e)
(build-simple-or
(%type-check mask-fixnum type-fixnum ,e)
(build-simple-or
(%type-check mask-flonum type-flonum ,e)
(build-and
(%type-check mask-typed-object type-typed-object ,e)
(%type-check mask-other-number type-other-number
,(%mref ,e ,(constant bignum-type-disp)))))))))
(define-inline 2 number?
[(e) (build-number? e)])
(define-inline 2 complex?
[(e) (build-number? e)]))
(define-inline 3 set-car!
[(e1 e2) (build-dirty-store e1 (constant pair-car-disp) e2)])
(define-inline 3 set-cdr!
[(e1 e2) (build-dirty-store e1 (constant pair-cdr-disp) e2)])
(define-inline 3 set-box!
[(e1 e2) (build-dirty-store e1 (constant box-ref-disp) e2)])
(define-inline 3 box-cas!
[(e1 e2 e3)
(bind #t (e2)
(build-dirty-store e1 %zero (constant box-ref-disp) e3 (make-build-cas e2) build-cas-seq))])
(define-inline 3 $set-symbol-name!
[(e1 e2) (build-dirty-store e1 (constant symbol-name-disp) e2)])
(define-inline 3 $set-symbol-property-list!
[(e1 e2) (build-dirty-store e1 (constant symbol-plist-disp) e2)])
(define-inline 3 $set-system-property-list!
[(e1 e2) (build-dirty-store e1 (constant symbol-splist-disp) e2)])
(define-inline 3 $set-port-info!
[(e1 e2) (build-dirty-store e1 (constant port-info-disp) e2)])
(define-inline 3 set-port-name!
[(e1 e2) (build-dirty-store e1 (constant port-name-disp) e2)])
(define-inline 2 set-box!
[(e-box e-new)
(bind #t (e-box e-new)
`(if ,(%typed-object-check mask-mutable-box type-mutable-box ,e-box)
,(build-dirty-store e-box (constant box-ref-disp) e-new)
,(build-libcall #t src sexpr set-box! e-box e-new)))])
(define-inline 2 box-cas!
[(e-box e-old e-new)
(bind #t (e-box e-old e-new)
`(if ,(%typed-object-check mask-mutable-box type-mutable-box ,e-box)
,(build-dirty-store e-box %zero (constant box-ref-disp) e-new (make-build-cas e-old) build-cas-seq)
,(build-libcall #t src sexpr box-cas! e-box e-old e-new)))])
(define-inline 2 set-car!
[(e-pair e-new)
(bind #t (e-pair e-new)
`(if ,(%type-check mask-pair type-pair ,e-pair)
,(build-dirty-store e-pair (constant pair-car-disp) e-new)
,(build-libcall #t src sexpr set-car! e-pair e-new)))])
(define-inline 2 set-cdr!
[(e-pair e-new)
(bind #t (e-pair e-new)
`(if ,(%type-check mask-pair type-pair ,e-pair)
,(build-dirty-store e-pair (constant pair-cdr-disp) e-new)
,(build-libcall #t src sexpr set-cdr! e-pair e-new)))])
(define-inline 3 $set-symbol-hash!
; no need for dirty store---e2 should be a fixnum
[(e1 e2) `(set! ,(%mref ,e1 ,(constant symbol-hash-disp)) ,e2)])
(let ()
(define-syntax define-tlc-parameter
(syntax-rules ()
[(_ name disp)
(define-inline 3 name
[(e-x) (%mref ,e-x ,(constant disp))])]
[(_ name name! disp)
(begin
(define-tlc-parameter name disp)
(define-inline 3 name!
[(e-x e-new) (build-dirty-store e-x (constant disp) e-new)]))]))
(define-tlc-parameter $tlc-keyval tlc-keyval-disp)
(define-tlc-parameter $tlc-ht tlc-ht-disp)
(define-tlc-parameter $tlc-next $set-tlc-next! tlc-next-disp))
(define-inline 2 $top-level-value
[(e) (nanopass-case (L7 Expr) e
[(quote ,d)
(guard (symbol? d))
(if (any-set? (prim-mask (or primitive system)) ($sgetprop d '*flags* 0))
(Symref d)
(bind #t (e)
(bind #t ([t (%mref ,e ,(constant symbol-value-disp))])
`(if ,(%type-check mask-unbound sunbound ,t)
,(build-libcall #t #f sexpr $top-level-value e)
,t))))]
[else
(bind #t (e)
(let ([Lfail (make-local-label 'tlv-fail)])
`(if ,(%type-check mask-symbol type-symbol ,e)
,(bind #t ([t (%mref ,e ,(constant symbol-value-disp))])
`(if ,(%type-check mask-unbound sunbound ,t)
(goto ,Lfail)
,t))
(label ,Lfail ,(build-libcall #t #f sexpr $top-level-value e)))))])])
(define-inline 3 $top-level-value
[(e) (nanopass-case (L7 Expr) e
[(quote ,d) (guard (symbol? d)) (Symref d)]
[else (%mref ,e ,(constant symbol-value-disp))])])
(let ()
(define (go e-sym e-value)
(bind #t (e-sym)
`(seq
,(build-dirty-store e-sym (constant symbol-value-disp) e-value)
(set! ,(%mref ,e-sym ,(constant symbol-pvalue-disp))
(literal
,(make-info-literal #f 'library
(lookup-libspec nonprocedure-code)
(constant code-data-disp)))))))
(define-inline 3 $set-top-level-value!
[(e-sym e-value) (go e-sym e-value)])
(define-inline 2 $set-top-level-value!
[(e-sym e-value) (and (constant? symbol? e-sym) (go e-sym e-value))]))
(define-inline 3 $top-level-bound?
[(e-sym)
(build-not
(%type-check mask-unbound sunbound
,(nanopass-case (L7 Expr) e-sym
[(quote ,d) (guard (symbol? d)) (Symref d)]
[else (%mref ,e-sym ,(constant symbol-value-disp))])))])
(let ()
(define parse-format
(lambda (who src cntl-arg args)
(nanopass-case (L7 Expr) cntl-arg
[(quote ,d)
(guard (c [(and (assertion-violation? c)
(format-condition? c)
(message-condition? c)
(irritants-condition? c))
($source-warning 'compile
src #t
"~? in call to ~s"
(condition-message c)
(condition-irritants c)
who)
#f])
(#%$parse-format-string who d (length args)))]
[else #f])))
(define fmt->expr
($make-fmt->expr
(lambda (d) `(quote ,d))
(lambda (e1 e2) `(seq ,e1 ,e2))
(lambda (src sexpr prim arg*)
`(call ,(make-info-call src sexpr #f #f #f) #f
,(lookup-primref 3 prim)
,arg* ...))))
(define build-format
(lambda (who src sexpr op-arg cntl-arg arg*)
(let ([x (parse-format who src cntl-arg arg*)])
(and x
(cond
[(and (fx= (length x) 1)
(string? (car x))
(nanopass-case (L7 Expr) op-arg
[(quote ,d) (eq? d #f)]
[else #f]))
(%primcall src sexpr string-copy (quote ,(car x)))]
[(and (nanopass-case (L7 Expr) op-arg
[(quote ,d) (not (eq? d #f))]
[else #t])
(let-values ([(op-arg dobind) (binder #t 'ptr op-arg)]
[(arg* dobind*) (list-binder #t 'ptr arg*)])
(let ([e (fmt->expr src sexpr x op-arg arg*)])
(and e (dobind (dobind* e))))))]
[else
(%primcall src sexpr $dofmt (quote ,who) ,op-arg ,cntl-arg
(quote ,x)
,(build-list arg*))])))))
(define-inline 2 errorf
[(e-who e-str . e*)
(parse-format 'errorf src e-str e*)
`(seq (pariah) (call ,(make-info-call src sexpr #f #t #t) #f ,(Symref 'errorf) ,e-who ,e-str ,e* ...))])
(define-inline 2 assertion-violationf
[(e-who e-str . e*)
(parse-format 'assertion-violationf src e-str e*)
`(seq (pariah) (call ,(make-info-call src sexpr #f #t #t) #f ,(Symref 'assertion-violationf) ,e-who ,e-str ,e* ...))])
(define-inline 2 $oops
[(e-who e-str . e*)
(parse-format '$oops src e-str e*)
`(seq (pariah) (call ,(make-info-call src sexpr #f #t #t) #f ,(Symref '$oops) ,e-who ,e-str ,e* ...))])
(define-inline 2 $impoops
[(e-who e-str . e*)
(parse-format '$impoops src e-str e*)
`(seq (pariah) (call ,(make-info-call src sexpr #f #t #t) #f ,(Symref '$impoops) ,e-who ,e-str ,e* ...))])
(define-inline 2 warningf
[(e-who e-str . e*)
(parse-format 'warningf src e-str e*)
`(seq (pariah) (call ,(make-info-call src sexpr #f #t #f) #f ,(Symref 'warningf) ,e-who ,e-str ,e* ...))])
(define-inline 2 $source-violation
[(e-who e-src e-start? e-str . e*)
(parse-format '$source-violation src e-str e*)
`(seq (pariah) (call ,(make-info-call src sexpr #f #t #t) #f ,(Symref '$source-violation)
,e-who ,e-src ,e-start? ,e-str ,e* ...))])
(define-inline 2 $source-warning
[(e-who e-src e-start? e-str . e*)
(parse-format '$source-warning src e-str e*)
`(seq (pariah) (call ,(make-info-call src sexpr #f #t #f) #f ,(Symref '$source-warning)
,e-who ,e-src ,e-start? ,e-str ,e* ...))])
(define-inline 2 fprintf
[(e-op e-str . e*)
(parse-format 'fprintf src e-str e*)
#f])
(define-inline 3 fprintf
[(e-op e-str . e*) (build-format 'fprintf src sexpr e-op e-str e*)])
(define-inline 2 printf
[(e-str . e*)
(build-format 'printf src sexpr (%tc-ref current-output) e-str e*)])
(define-inline 2 format
[(e . e*)
(nanopass-case (L7 Expr) e
[(quote ,d)
(if (string? d)
(build-format 'format src sexpr `(quote #f) e e*)
(and (not (null? e*))
(cond
[(eq? d #f) (build-format 'format src sexpr e (car e*) (cdr e*))]
[(eq? d #t) (build-format 'format src sexpr
(%tc-ref current-output)
(car e*) (cdr e*))]
[else #f])))]
[else #f])]))
(let ()
(define hand-coded-closure?
(lambda (name)
(not (memq name '(nuate nonprocedure-code error-invoke invoke)))))
(define-inline 2 $hand-coded
[(name)
(nanopass-case (L7 Expr) name
[(quote ,d)
(guard (symbol? d))
(let ([l (make-local-label 'hcl)])
(set! new-l* (cons l new-l*))
(set! new-le* (cons (with-output-language (L9 CaseLambdaExpr) `(hand-coded ,d)) new-le*))
(if (hand-coded-closure? d)
`(literal ,(make-info-literal #f 'closure l 0))
`(label-ref ,l 0)))]
[(seq (profile ,src) ,[e]) `(seq (profile ,src) ,e)]
[else ($oops '$hand-coded "~s is not a quoted symbol" name)])]))
(define-inline 2 $tc
[() %tc])
(define-inline 3 $tc-field
[(e-fld e-tc)
(nanopass-case (L7 Expr) e-fld
[(quote ,d)
(let ()
(define-syntax a
(lambda (x)
#`(case d
#,@(fold-left
(lambda (ls field)
(apply
(lambda (name type disp len)
(if (eq? type 'ptr)
(cons
(with-syntax ([name (datum->syntax #'* name)])
#'[(name) (%tc-ref ,e-tc name)])
ls)
ls))
field))
'() (getprop 'tc '*fields* '()))
[else #f])))
a)]
[else #f])]
[(e-fld e-tc e-val)
(nanopass-case (L7 Expr) e-fld
[(quote ,d)
(let ()
(define-syntax a
(lambda (x)
#`(case d
#,@(fold-left
(lambda (ls field)
(apply
(lambda (name type disp len)
(if (eq? type 'ptr)
(cons
(with-syntax ([name (datum->syntax #'* name)])
#'[(name) `(set! ,(%tc-ref ,e-tc name) ,e-val)])
ls)
ls))
field))
'() (getprop 'tc '*fields* '()))
[else #f])))
a)]
[else #f])])
(let ()
(define-syntax define-tc-parameter
(syntax-rules ()
[(_ name tc-name)
(begin
(define-inline 2 name
[() (%tc-ref tc-name)]
[(x) #f])
(define-inline 3 name
[() (%tc-ref tc-name)]
[(x) `(set! ,(%tc-ref tc-name) ,x)]))]))
(define-tc-parameter current-input-port current-input)
(define-tc-parameter current-output-port current-output)
(define-tc-parameter current-error-port current-error)
(define-tc-parameter generate-inspector-information generate-inspector-information)
(define-tc-parameter generate-procedure-source-information generate-procedure-source-information)
(define-tc-parameter generate-profile-forms generate-profile-forms)
(define-tc-parameter $compile-profile compile-profile)
(define-tc-parameter optimize-level optimize-level)
(define-tc-parameter subset-mode subset-mode)
(define-tc-parameter $suppress-primitive-inlining suppress-primitive-inlining)
(define-tc-parameter $block-counter block-counter)
(define-tc-parameter $sfd sfd)
(define-tc-parameter $current-mso current-mso)
(define-tc-parameter $target-machine target-machine)
(define-tc-parameter $current-stack-link stack-link)
(define-tc-parameter $current-winders winders)
(define-tc-parameter default-record-equal-procedure default-record-equal-procedure)
(define-tc-parameter default-record-hash-procedure default-record-hash-procedure)
)
(define-inline 3 $install-guardian
[(e-obj e-rep e-tconc)
(bind #f (e-obj e-rep e-tconc)
(bind #t ([t (%constant-alloc typemod (constant size-guardian-entry))])
(%seq
(set! ,(%mref ,t ,(constant guardian-entry-obj-disp)) ,e-obj)
(set! ,(%mref ,t ,(constant guardian-entry-rep-disp)) ,e-rep)
(set! ,(%mref ,t ,(constant guardian-entry-tconc-disp)) ,e-tconc)
(set! ,(%mref ,t ,(constant guardian-entry-next-disp)) ,(%tc-ref guardian-entries))
(set! ,(%tc-ref guardian-entries) ,t))))])
(define-inline 3 $install-ftype-guardian
[(e-obj e-tconc)
(bind #f (e-obj e-tconc)
(bind #t ([t (%constant-alloc typemod (constant size-guardian-entry))])
(%seq
(set! ,(%mref ,t ,(constant guardian-entry-obj-disp)) ,e-obj)
(set! ,(%mref ,t ,(constant guardian-entry-rep-disp)) (immediate ,(constant ftype-guardian-rep)))
(set! ,(%mref ,t ,(constant guardian-entry-tconc-disp)) ,e-tconc)
(set! ,(%mref ,t ,(constant guardian-entry-next-disp)) ,(%tc-ref guardian-entries))
(set! ,(%tc-ref guardian-entries) ,t))))])
(define-inline 2 guardian?
[(e)
(bind #t (e)
(build-and
(%type-check mask-closure type-closure ,e)
(%type-check mask-guardian-code type-guardian-code
,(%mref
,(%inline -
,(%mref ,e ,(constant closure-code-disp))
,(%constant code-data-disp))
,(constant code-type-disp)))))])
(define-inline 2 virtual-register-count
[() `(quote ,(constant virtual-register-count))])
(let ()
(define constant-ref
(lambda (e-idx)
(nanopass-case (L7 Expr) e-idx
[(quote ,d)
(guard (and (fixnum? d) ($fxu< d (constant virtual-register-count))))
(%mref ,%tc ,(fx+ (constant tc-virtual-registers-disp) (fx* d (constant ptr-bytes))))]
[else #f])))
(define constant-set
(lambda (e-idx e-val)
(let ([ref (constant-ref e-idx)])
(and ref `(set! ,ref ,e-val)))))
(define index-check
(lambda (e-idx libcall e)
`(if (if ,(%type-check mask-fixnum type-fixnum ,e-idx)
,(%inline u< ,e-idx (immediate ,(fix (constant virtual-register-count))))
,(%constant sfalse))
,e
,libcall)))
(meta-assert (= (constant log2-ptr-bytes) (constant fixnum-offset)))
(define-inline 3 virtual-register
[(e-idx)
(or (constant-ref e-idx)
(%mref ,%tc ,e-idx ,(constant tc-virtual-registers-disp)))])
(define-inline 2 virtual-register
[(e-idx)
(or (constant-ref e-idx)
(bind #t (e-idx)
(index-check e-idx
(build-libcall #t src sexpr virtual-register e-idx)
(%mref ,%tc ,e-idx ,(constant tc-virtual-registers-disp)))))])
(define-inline 3 set-virtual-register!
[(e-idx e-val)
(or (constant-set e-idx e-val)
`(set! ,(%mref ,%tc ,e-idx ,(constant tc-virtual-registers-disp)) ,e-val))])
(define-inline 2 set-virtual-register!
[(e-idx e-val)
(or (constant-set e-idx e-val)
(bind #t (e-idx)
(bind #f (e-val)
(index-check e-idx
(build-libcall #t src sexpr set-virtual-register! e-idx)
`(set! ,(%mref ,%tc ,e-idx ,(constant tc-virtual-registers-disp)) ,e-val)))))]))
(define-inline 2 $thread-list
[() `(literal ,(make-info-literal #t 'entry (lookup-c-entry thread-list) 0))])
(when-feature pthreads
(define-inline 2 $raw-tc-mutex
[() `(literal ,(make-info-literal #f 'entry (lookup-c-entry raw-tc-mutex) 0))])
(define-inline 2 $raw-collect-cond
[() `(literal ,(make-info-literal #f 'entry (lookup-c-entry raw-collect-cond) 0))]))
(define-inline 2 not
[(e) `(if ,e ,(%constant sfalse) ,(%constant strue))])
(define-inline 2 most-negative-fixnum
[() `(quote ,(constant most-negative-fixnum))])
(define-inline 2 most-positive-fixnum
[() `(quote ,(constant most-positive-fixnum))])
(define-inline 2 least-fixnum
[() `(quote ,(constant most-negative-fixnum))])
(define-inline 2 greatest-fixnum
[() `(quote ,(constant most-positive-fixnum))])
(define-inline 2 fixnum-width
[() `(quote ,(constant fixnum-bits))])
(define-inline 2 native-endianness
[() `(quote ,(constant native-endianness))])
(define-inline 2 directory-separator
[() `(quote ,(if-feature windows #\\ #\/))])
(let () ; level 2 char=?, r6rs:char=?, etc.
(define-syntax char-pred
(syntax-rules ()
[(_ op r6rs:op inline-op)
(let ()
(define (go2 src sexpr e1 e2)
(bind #t (e1 e2)
`(if ,(build-chars? e1 e2)
,(%inline inline-op ,e1 ,e2)
,(build-libcall #t src sexpr op e1 e2))))
(define (go3 src sexpr e1 e2 e3)
(and (constant? char? e1)
(constant? char? e3)
(bind #t (e2)
`(if ,(%type-check mask-char type-char ,e2)
,(build-and
(%inline inline-op ,e1 ,e2)
(%inline inline-op ,e2 ,e3))
; could also pass e2 and e3:
,(build-libcall #t src sexpr op e1 e2)))))
(define-inline 2 op
[(e1 e2) (go2 src sexpr e1 e2)]
[(e1 e2 e3) (go3 src sexpr e1 e2 e3)]
[(e1 . e*) #f])
(define-inline 2 r6rs:op
[(e1 e2) (go2 src sexpr e1 e2)]
[(e1 e2 e3) (go3 src sexpr e1 e2 e3)]
[(e1 e2 . e*) #f]))]))
(char-pred char<? r6rs:char<? <)
(char-pred char<=? r6rs:char<=? <=)
(char-pred char=? r6rs:char=? eq?)
(char-pred char>=? r6rs:char>=? >=)
(char-pred char>? r6rs:char>? >))
(let () ; level 3 char=?, r6rs:char=?, etc.
(define-syntax char-pred
(syntax-rules ()
[(_ op r6rs:op inline-op)
(let ()
(define (go2 e1 e2)
(%inline inline-op ,e1 ,e2))
(define (go3 e1 e2 e3)
(bind #t (e2)
(bind #f (e3)
(build-and
(go2 e1 e2)
(go2 e2 e3)))))
(define-inline 3 op
[(e) `(seq ,e ,(%constant strue))]
[(e1 e2) (go2 e1 e2)]
[(e1 e2 e3) (go3 e1 e2 e3)]
[(e1 . e*) #f])
(define-inline 3 r6rs:op
[(e1 e2) (go2 e1 e2)]
[(e1 e2 e3) (go3 e1 e2 e3)]
[(e1 e2 . e*) #f]))]))
(char-pred char<? r6rs:char<? <)
(char-pred char<=? r6rs:char<=? <=)
(char-pred char=? r6rs:char=? eq?)
(char-pred char>=? r6rs:char>=? >=)
(char-pred char>? r6rs:char>? >))
(define-inline 3 map
[(e-proc e-ls)
(or (nanopass-case (L7 Expr) e-proc
[,pr
(and (all-set? (prim-mask unsafe) (primref-flags pr))
(let ([name (primref-name pr)])
(or (and (eq? name 'car) (build-libcall #f src sexpr map-car e-ls))
(and (eq? name 'cdr) (build-libcall #f src sexpr map-cdr e-ls)))))]
[else #f])
(build-libcall #f src sexpr map1 e-proc e-ls))]
[(e-proc e-ls1 e-ls2)
(or (nanopass-case (L7 Expr) e-proc
[,pr
(and (eq? (primref-name pr) 'cons)
(build-libcall #f src sexpr map-cons e-ls1 e-ls2))]
[else #f])
(build-libcall #f src sexpr map2 e-proc e-ls1 e-ls2))]
[(e-proc e-ls . e-ls*) #f])
(define-inline 3 andmap
[(e-proc e-ls) (build-libcall #f src sexpr andmap1 e-proc e-ls)]
[(e-proc e-ls . e-ls*) #f])
(define-inline 3 for-all
[(e-proc e-ls) (build-libcall #f src sexpr andmap1 e-proc e-ls)]
[(e-proc e-ls . e-ls*) #f])
(define-inline 3 ormap
[(e-proc e-ls) (build-libcall #f src sexpr ormap1 e-proc e-ls)]
[(e-proc e-ls . e-ls*) #f])
(define-inline 3 exists
[(e-proc e-ls) (build-libcall #f src sexpr ormap1 e-proc e-ls)]
[(e-proc e-ls . e-ls*) #f])
(define-inline 3 fold-left
[(e-proc e-base e-ls) (build-libcall #f src sexpr fold-left1 e-proc e-base e-ls)]
[(e-proc e-base e-ls1 e-ls2) (build-libcall #f src sexpr fold-left2 e-proc e-base e-ls1 e-ls2)]
[(e-proc e-base e-ls . e-ls*) #f])
(define-inline 3 fold-right
[(e-proc e-base e-ls) (build-libcall #f src sexpr fold-right1 e-proc e-base e-ls)]
[(e-proc e-base e-ls1 e-ls2) (build-libcall #f src sexpr fold-right2 e-proc e-base e-ls1 e-ls2)]
[(e-proc e-base e-ls . e-ls*) #f])
(define-inline 3 for-each
[(e-proc e-ls) (build-libcall #f src sexpr for-each1 e-proc e-ls)]
[(e-proc e-ls1 e-ls2) (build-libcall #f src sexpr for-each2 e-proc e-ls1 e-ls2)]
[(e-proc e-ls . e-ls*) #f])
(define-inline 3 vector-map
[(e-proc e-ls) (build-libcall #f src sexpr vector-map1 e-proc e-ls)]
[(e-proc e-ls1 e-ls2) (build-libcall #f src sexpr vector-map2 e-proc e-ls1 e-ls2)]
[(e-proc e-ls . e-ls*) #f])
(define-inline 3 vector-for-each
[(e-proc e-ls) (build-libcall #f src sexpr vector-for-each1 e-proc e-ls)]
[(e-proc e-ls1 e-ls2) (build-libcall #f src sexpr vector-for-each2 e-proc e-ls1 e-ls2)]
[(e-proc e-ls . e-ls*) #f])
(define-inline 3 string-for-each
[(e-proc e-ls) (build-libcall #f src sexpr string-for-each1 e-proc e-ls)]
[(e-proc e-ls1 e-ls2) (build-libcall #f src sexpr string-for-each2 e-proc e-ls1 e-ls2)]
[(e-proc e-ls . e-ls*) #f])
(define-inline 3 reverse
[(e) (build-libcall #f src sexpr reverse e)])
(let ()
(define inline-getprop
(lambda (plist-offset e-sym e-key e-dflt)
(let ([t-ls (make-assigned-tmp 't-ls)] [t-cdr (make-tmp 't-cdr)] [Ltop (make-local-label 'Ltop)])
(bind #t (e-key e-dflt)
; indirect symbol after evaluating e-key and e-dflt
`(let ([,t-ls ,(%mref ,e-sym ,plist-offset)])
(label ,Ltop
(if ,(%inline eq? ,t-ls ,(%constant snil))
,e-dflt
(let ([,t-cdr ,(%mref ,t-ls ,(constant pair-cdr-disp))])
(if ,(%inline eq? ,(%mref ,t-ls ,(constant pair-car-disp)) ,e-key)
,(%mref ,t-cdr ,(constant pair-car-disp))
(seq
(set! ,t-ls ,(%mref ,t-cdr ,(constant pair-cdr-disp)))
(goto ,Ltop)))))))))))
(define-inline 3 getprop
[(e-sym e-key) (inline-getprop (constant symbol-plist-disp) e-sym e-key (%constant sfalse))]
[(e-sym e-key e-dflt) (inline-getprop (constant symbol-plist-disp) e-sym e-key e-dflt)])
(define-inline 3 $sgetprop
[(e-sym e-key e-dflt) (inline-getprop (constant symbol-splist-disp) e-sym e-key e-dflt)]))
(define-inline 3 assq
[(e-key e-ls)
(let ([t-ls (make-assigned-tmp 't-ls)] [Ltop (make-local-label 'Ltop)])
(bind #t (e-key)
`(let ([,t-ls ,e-ls])
(label ,Ltop
(if ,(%inline eq? ,t-ls ,(%constant snil))
,(%constant sfalse)
,(bind #t ([t-a (%mref ,t-ls ,(constant pair-car-disp))])
`(if ,(%inline eq? ,(%mref ,t-a ,(constant pair-car-disp)) ,e-key)
,t-a
(seq
(set! ,t-ls ,(%mref ,t-ls ,(constant pair-cdr-disp)))
(goto ,Ltop)))))))))])
(define-inline 3 length
[(e-ls)
(let ([t-ls (make-assigned-tmp 't-ls)]
[t-n (make-assigned-tmp 't-n)]
[Ltop (make-local-label 'Ltop)])
(bind #t (e-ls)
`(if ,(%inline eq? ,e-ls ,(%constant snil))
(immediate ,(fix 0))
(let ([,t-ls ,e-ls] [,t-n (immediate ,(fix 0))])
(label ,Ltop
,(%seq
(set! ,t-ls ,(%mref ,t-ls ,(constant pair-cdr-disp)))
(set! ,t-n ,(%inline + ,t-n (immediate ,(fix 1))))
(if ,(%inline eq? ,t-ls ,(%constant snil))
,t-n
(goto ,Ltop))))))))])
(define-inline 3 append
; TODO: hand-coded library routine that allocates the new pairs in a block
[() (%constant snil)]
[(e-ls) e-ls]
[(e-ls1 e-ls2) (build-libcall #f src sexpr append e-ls1 e-ls2)]
[(e-ls1 e-ls2 e-ls3)
(build-libcall #f src sexpr append e-ls1
(build-libcall #f #f sexpr append e-ls2 e-ls3))]
[(e-ls . e-ls*) #f])
(define-inline 3 apply
[(e0 e1) (build-libcall #f src sexpr apply0 e0 e1)]
[(e0 e1 e2) (build-libcall #f src sexpr apply1 e0 e1 e2)]
[(e0 e1 e2 e3) (build-libcall #f src sexpr apply2 e0 e1 e2 e3)]
[(e0 e1 e2 e3 e4) (build-libcall #f src sexpr apply3 e0 e1 e2 e3 e4)]
[(e0 e1 . e*) #f])
(define-inline 2 fxsll
[(e0 e1) (build-libcall #f src sexpr fxsll e0 e1)])
(define-inline 2 fxarithmetic-shift-left
[(e0 e1) (build-libcall #f src sexpr fxarithmetic-shift-left e0 e1)])
(define-inline 3 display-string
[(e-s) (build-libcall #f src sexpr display-string e-s (%tc-ref current-output))]
[(e-s e-op) (build-libcall #f src sexpr display-string e-s e-op)])
(define-inline 3 call-with-current-continuation
[(e) (build-libcall #f src sexpr callcc e)])
(define-inline 3 call/cc
[(e) (build-libcall #f src sexpr callcc e)])
(define-inline 3 call/1cc
[(e) (build-libcall #f src sexpr call1cc e)])
(define-inline 2 $event
[() (build-libcall #f src sexpr event)])
(define-inline 3 eq-hashtable-ref
[(e1 e2 e3) (build-libcall #f src sexpr eq-hashtable-ref e1 e2 e3)])
(define-inline 3 eq-hashtable-contains?
[(e1 e2) (build-libcall #f src sexpr eq-hashtable-contains? e1 e2)])
(define-inline 3 eq-hashtable-set!
[(e1 e2 e3) (build-libcall #f src sexpr eq-hashtable-set! e1 e2 e3)])
(define-inline 3 eq-hashtable-update!
[(e1 e2 e3 e4) (build-libcall #f src sexpr eq-hashtable-update! e1 e2 e3 e4)])
(define-inline 3 eq-hashtable-cell
[(e1 e2 e3) (build-libcall #f src sexpr eq-hashtable-cell e1 e2 e3)])
(define-inline 3 eq-hashtable-delete!
[(e1 e2) (build-libcall #f src sexpr eq-hashtable-delete! e1 e2)])
(define-inline 3 symbol-hashtable-ref
[(e1 e2 e3) (build-libcall #f src sexpr symbol-hashtable-ref e1 e2 e3)])
(define-inline 3 symbol-hashtable-contains?
[(e1 e2) (build-libcall #f src sexpr symbol-hashtable-contains? e1 e2)])
(define-inline 3 symbol-hashtable-set!
[(e1 e2 e3) (build-libcall #f src sexpr symbol-hashtable-set! e1 e2 e3)])
(define-inline 3 symbol-hashtable-update!
[(e1 e2 e3 e4) (build-libcall #f src sexpr symbol-hashtable-update! e1 e2 e3 e4)])
(define-inline 3 symbol-hashtable-cell
[(e1 e2 e3) (build-libcall #f src sexpr symbol-hashtable-cell e1 e2 e3)])
(define-inline 3 symbol-hashtable-delete!
[(e1 e2) (build-libcall #f src sexpr symbol-hashtable-delete! e1 e2)])
(define-inline 2 bytevector-s8-set!
[(e1 e2 e3) (build-libcall #f src sexpr bytevector-s8-set! e1 e2 e3)])
(define-inline 2 bytevector-u8-set!
[(e1 e2 e3) (build-libcall #f src sexpr bytevector-u8-set! e1 e2 e3)])
(define-inline 3 bytevector=?
[(e1 e2) (build-libcall #f src sexpr bytevector=? e1 e2)])
(let ()
(define eqok-help?
(lambda (obj)
(or (symbol? obj)
(char? obj)
(target-fixnum? obj)
(null? obj)
(boolean? obj)
(eqv? obj "")
(eqv? obj ($tc-field 'null-immutable-string ($tc)))
(eqv? obj '#())
(eqv? obj ($tc-field 'null-immutable-vector ($tc)))
(eqv? obj '#vu8())
(eqv? obj ($tc-field 'null-immutable-bytevector ($tc)))
(eqv? obj '#vfx())
(eqv? obj ($tc-field 'null-immutable-fxvector ($tc)))
(eq? obj (void))
(eof-object? obj)
(bwp-object? obj)
(eq? obj '#6=#6#)
($unbound-object? obj))))
(define eqvok-help? number?)
(define e*ok?
(lambda (e*ok-help?)
(lambda (e)
(nanopass-case (L7 Expr) e
[(quote ,d) (e*ok-help? d)]
[else #f]))))
(define eqok? (e*ok? eqok-help?))
(define eqvok? (e*ok? eqvok-help?))
(define-inline 2 eqv?
[(e1 e2) (or (eqvop-null-fptr e1 e2)
(relop-length RELOP= e1 e2)
(if (or (eqok? e1) (eqok? e2))
(build-eq? e1 e2)
(build-eqv? src sexpr e1 e2)))])
(let ()
(define xform-equal?
(lambda (src sexpr e1 e2)
(nanopass-case (L7 Expr) e1
[(quote ,d1)
(let xform ([d1 d1] [e2 e2] [n 3] [k (lambda (e n) e)])
(if (eqok-help? d1)
(k (build-eq? `(quote ,d1) e2) n)
(if (eqvok-help? d1)
(k (build-eqv? src sexpr `(quote ,d1) e2) n)
(and (fx> n 0)
(pair? d1)
(let-values ([(e2 dobind) (binder #t 'ptr e2)])
(xform (car d1) (build-car e2) (fx- n 1)
(lambda (a n)
(xform (cdr d1) (build-cdr e2) n
(lambda (d n)
(k (dobind
(build-and
(build-pair? e2)
(build-and a d)))
n))))))))))]
[else #f])))
(define-inline 2 equal?
[(e1 e2) (or (eqvop-null-fptr e1 e2)
(relop-length RELOP= e1 e2)
(xform-equal? src sexpr e1 e2)
(xform-equal? src sexpr e2 e1))]))
(let ()
(define mem*ok?
(lambda (e*ok-help?)
(lambda (x)
(nanopass-case (L7 Expr) x
[(quote ,d)
(and (list? d)
(let f ([d d])
(or (null? d)
(and (e*ok-help? (car d))
(f (cdr d))))))]
[else #f]))))
(define memqok? (mem*ok? eqok-help?))
(define memvok? (mem*ok? eqvok-help?))
(define mem*->e*?s
(lambda (build-e*? limit)
(lambda (e-key e-ls)
(nanopass-case (L7 Expr) e-ls
[(quote ,d)
(and (let f ([d d] [n 0])
(or (null? d)
(and (pair? d)
(fx< n limit)
(f (cdr d) (fx1+ n)))))
(bind #t (e-key)
(let f ([ls d])
(if (null? ls)
`(quote #f)
`(if ,(build-e*? e-key `(quote ,(car ls)))
(quote ,ls)
,(f (cdr ls)))))))]
[else #f]))))
(define memq->eq?s (mem*->e*?s build-eq? 8))
(define (memv->eqv?s src sexpr) (mem*->e*?s (make-build-eqv? src sexpr) 4))
(define do-memq
(lambda (src sexpr e-key e-ls)
(or (memq->eq?s e-key e-ls)
(let ([t-ls (make-assigned-tmp 't-ls)] [Ltop (make-local-label 'Ltop)])
(bind #t (e-key)
`(let ([,t-ls ,e-ls])
(label ,Ltop
(if ,(%inline eq? ,t-ls ,(%constant snil))
,(%constant sfalse)
(if ,(%inline eq? ,(%mref ,t-ls ,(constant pair-car-disp)) ,e-key)
,t-ls
(seq
(set! ,t-ls ,(%mref ,t-ls ,(constant pair-cdr-disp)))
(goto ,Ltop)))))))))))
(define do-memv
(lambda (src sexpr e-key e-ls)
(or ((memv->eqv?s src sexpr) e-key e-ls)
(build-libcall #f src sexpr memv e-key e-ls))))
(define-inline 3 memq
[(e-key e-ls) (do-memq src sexpr e-key e-ls)])
(define-inline 3 memv
[(e-key e-ls)
(if (or (eqok? e-key) (memqok? e-ls))
(do-memq src sexpr e-key e-ls)
(do-memv src sexpr e-key e-ls))])
(define-inline 3 member
[(e-key e-ls)
(if (or (eqok? e-key) (memqok? e-ls))
(do-memq src sexpr e-key e-ls)
(and (or (eqvok? e-key) (memvok? e-ls))
(do-memv src sexpr e-key e-ls)))])
(define-inline 2 memq
[(e-key e-ls) (memq->eq?s e-key e-ls)])
(define-inline 2 memv
[(e-key e-ls) (or (and (memqok? e-ls) (memq->eq?s e-key e-ls))
((memv->eqv?s src sexpr) e-key e-ls))])
(define-inline 2 member
[(e-key e-ls) (or (and (memqok? e-ls) (memq->eq?s e-key e-ls))
(and (memvok? e-ls) ((memv->eqv?s src sexpr) e-key e-ls)))])))
; NB: for all of the I/O routines, consider putting optimize-level 2 code out-of-line
; w/o going all the way to the port handler, i.e., always defer to library routine but
; have library routine do the checks and run the optimize-level 3 version...this could
; save a lot of code
; NB: verify that the inline checks don't always fail, i.e., don't always send us to the
; library routine
(let ()
(define (go src sexpr e-p check? update? do-libcall)
(let ([Llib (and check? (make-local-label 'Llib))])
(define maybe-add-port-check
(lambda (e-p body)
(if Llib
`(if (if ,(%type-check mask-typed-object type-typed-object ,e-p)
,(%type-check mask-binary-input-port type-binary-input-port
,(%mref ,e-p ,(constant typed-object-type-disp)))
,(%constant sfalse))
,body
(goto ,Llib))
body)))
(define maybe-add-update
(lambda (t0 e-icount body)
(if update?
`(seq
(set! ,e-icount ,(%inline + ,t0 (immediate 1)))
,body)
body)))
(bind #t (e-p)
(let ([e-icount (%mref ,e-p ,(constant port-icount-disp))])
(maybe-add-port-check e-p
(bind #t ([t0 e-icount])
`(if ,(%inline eq? ,t0 (immediate 0))
,(maybe-add-label Llib (do-libcall src sexpr e-p))
,(maybe-add-update t0 e-icount
; TODO: this doesn't completely fall away when used in effect context
(build-fix
`(inline ,(make-info-load 'unsigned-8 #f) ,%load
,t0
,(%mref ,e-p ,(constant port-ilast-disp))
(immediate 0)))))))))))
(define (unsafe-lookahead-u8-libcall src sexpr e-p) (build-libcall #t src sexpr unsafe-lookahead-u8 e-p))
(define (safe-lookahead-u8-libcall src sexpr e-p) (build-libcall #t src sexpr safe-lookahead-u8 e-p))
(define (unsafe-get-u8-libcall src sexpr e-p) (build-libcall #t src sexpr unsafe-get-u8 e-p))
(define (safe-get-u8-libcall src sexpr e-p) (build-libcall #t src sexpr safe-get-u8 e-p))
(define-inline 3 lookahead-u8
[(e-p) (go src sexpr e-p #f #f unsafe-lookahead-u8-libcall)])
(define-inline 2 lookahead-u8
[(e-p) (go src sexpr e-p #t #f safe-lookahead-u8-libcall)])
(define-inline 3 get-u8
[(e-p) (go src sexpr e-p #f #t unsafe-get-u8-libcall)])
(define-inline 2 get-u8
[(e-p) (go src sexpr e-p #t #t safe-get-u8-libcall)]))
(let ()
(define (go src sexpr e-p check? update? do-libcall)
(let ([Llib (and check? (make-local-label 'Llib))])
(define maybe-add-port-check
(lambda (e-p body)
(if Llib
`(if (if ,(%type-check mask-typed-object type-typed-object ,e-p)
,(%type-check mask-textual-input-port type-textual-input-port
,(%mref ,e-p ,(constant typed-object-type-disp)))
,(%constant sfalse))
,body
(goto ,Llib))
body)))
(define maybe-add-update
(lambda (t0 e-icount body)
(if update?
`(seq
(set! ,e-icount ,(%inline + ,t0 ,(%constant string-char-bytes)))
,body)
body)))
(bind #t (e-p)
(let ([e-icount (%mref ,e-p ,(constant port-icount-disp))])
(maybe-add-port-check e-p
(bind #t ([t0 e-icount])
`(if ,(%inline eq? ,t0 (immediate 0))
,(maybe-add-label Llib (do-libcall src sexpr e-p))
,(maybe-add-update t0 e-icount
; TODO: this doesn't completely fall away when used in effect context
`(inline ,(make-info-load (string-char-type) #f) ,%load
,t0
,(%mref ,e-p ,(constant port-ilast-disp))
(immediate 0))))))))))
(define (unsafe-lookahead-char-libcall src sexpr e-p) (build-libcall #t src sexpr unsafe-lookahead-char e-p))
(define (safe-lookahead-char-libcall src sexpr e-p) (build-libcall #t src sexpr safe-lookahead-char e-p))
(define (unsafe-peek-char-libcall src sexpr e-p) (build-libcall #t src sexpr unsafe-peek-char e-p))
(define (safe-peek-char-libcall src sexpr e-p) (build-libcall #t src sexpr safe-peek-char e-p))
(define (unsafe-get-char-libcall src sexpr e-p) (build-libcall #t src sexpr unsafe-get-char e-p))
(define (safe-get-char-libcall src sexpr e-p) (build-libcall #t src sexpr safe-get-char e-p))
(define (unsafe-read-char-libcall src sexpr e-p) (build-libcall #t src sexpr unsafe-read-char e-p))
(define (safe-read-char-libcall src sexpr e-p) (build-libcall #t src sexpr safe-read-char e-p))
(define-inline 3 lookahead-char
[(e-p) (go src sexpr e-p #f #f unsafe-lookahead-char-libcall)])
(define-inline 2 lookahead-char
[(e-p) (go src sexpr e-p #t #f safe-lookahead-char-libcall)])
(define-inline 3 peek-char
[() (go src sexpr (%tc-ref current-input) #f #f unsafe-peek-char-libcall)]
[(e-p) (go src sexpr e-p #f #f unsafe-peek-char-libcall)])
(define-inline 2 peek-char
[() (go src sexpr (%tc-ref current-input) #f #f unsafe-peek-char-libcall)]
[(e-p) (go src sexpr e-p #t #f safe-peek-char-libcall)])
(define-inline 3 get-char
[(e-p) (go src sexpr e-p #f #t unsafe-get-char-libcall)])
(define-inline 2 get-char
[(e-p) (go src sexpr e-p #t #t safe-get-char-libcall)])
(define-inline 3 read-char
[() (go src sexpr (%tc-ref current-input) #f #t unsafe-read-char-libcall)]
[(e-p) (go src sexpr e-p #f #t unsafe-read-char-libcall)])
(define-inline 2 read-char
[() (go src sexpr (%tc-ref current-input) #f #t unsafe-read-char-libcall)]
[(e-p) (go src sexpr e-p #t #t safe-read-char-libcall)]))
(let ()
(define (go src sexpr e-p e-c check-port? check-char? do-libcall)
(let ([const-char? (constant? char? e-c)])
(let ([Llib (and (or check-char? check-port? (not const-char?)) (make-local-label 'Llib))])
(define maybe-add-port-check
(lambda (e-p body)
(if check-port?
`(if (if ,(%type-check mask-typed-object type-typed-object ,e-p)
,(%type-check mask-textual-input-port type-textual-input-port
,(%mref ,e-p ,(constant typed-object-type-disp)))
,(%constant sfalse))
,body
(goto ,Llib))
body)))
(define maybe-add-eof-check
(lambda (e-c body)
(if const-char?
body
`(if ,(%inline eq? ,e-c ,(%constant seof))
(goto ,Llib)
,body))))
(define maybe-add-char-check
(lambda (e-c body)
(if check-char?
`(if ,(%type-check mask-char type-char ,e-c)
,body
(goto ,Llib))
body)))
(bind #t (e-c e-p)
(let ([e-icount (%mref ,e-p ,(constant port-icount-disp))])
(maybe-add-port-check e-p
(maybe-add-eof-check e-c
(maybe-add-char-check e-c
(bind #t ([t0 e-icount])
`(if ,(%inline eq? ,t0
,(%inline -
,(%inline +
,(%mref ,e-p ,(constant port-ibuffer-disp))
,(%constant string-data-disp))
,(%mref ,e-p ,(constant port-ilast-disp))))
,(maybe-add-label Llib (do-libcall src sexpr e-p e-c))
(set! ,e-icount ,(%inline - ,t0 ,(%constant string-char-bytes)))))))))))))
(define (unsafe-unget-char-libcall src sexpr e-p e-c) (build-libcall #t src sexpr unsafe-unget-char e-p e-c))
(define (safe-unget-char-libcall src sexpr e-p e-c) (build-libcall #t src sexpr safe-unget-char e-p e-c))
(define (unsafe-unread-char-libcall src sexpr e-p e-c) (build-libcall #t src sexpr unsafe-unread-char e-c e-p))
(define (safe-unread-char-libcall src sexpr e-p e-c) (build-libcall #t src sexpr safe-unread-char e-c e-p))
(define-inline 3 unget-char
[(e-p e-c) (go src sexpr e-p e-c #f #f unsafe-unget-char-libcall)])
(define-inline 2 unget-char
[(e-p e-c) (go src sexpr e-p e-c #t (not (constant? char? e-c)) safe-unget-char-libcall)])
(define-inline 3 unread-char
[(e-c) (go src sexpr (%tc-ref current-input) e-c #f #f unsafe-unread-char-libcall)]
[(e-c e-p) (go src sexpr e-p e-c #f #f unsafe-unread-char-libcall)])
(define-inline 2 unread-char
[(e-c) (if (constant? char? e-c)
(go src sexpr (%tc-ref current-input) e-c #f #f unsafe-unread-char-libcall)
(go src sexpr (%tc-ref current-input) e-c #f #t safe-unread-char-libcall))]
[(e-c e-p) (go src sexpr e-p e-c #t (not (constant? char? e-c)) safe-unread-char-libcall)]))
(let ()
(define octet?
(lambda (x)
(and (fixnum? x) (fx<= 0 x 255))))
(define maybe-add-octet-check
(lambda (check-octet? Llib e-o body)
(if check-octet?
`(if ,(%type-check mask-octet type-octet ,e-o)
,body
(goto ,Llib))
body)))
(let ()
(define (go src sexpr e-p e-o check-port? check-octet? do-libcall)
(let ([const-octet? (constant? octet? e-o)])
(let ([Llib (and (or check-octet? check-port? (not const-octet?)) (make-local-label 'Llib))])
(define maybe-add-port-check
(lambda (e-p body)
(if check-port?
`(if (if ,(%type-check mask-typed-object type-typed-object ,e-p)
,(%type-check mask-binary-input-port type-binary-input-port
,(%mref ,e-p ,(constant typed-object-type-disp)))
,(%constant sfalse))
,body
(goto ,Llib))
body)))
(define maybe-add-eof-check
(lambda (e-o body)
(if const-octet?
body
`(if ,(%inline eq? ,e-o ,(%constant seof))
(goto ,Llib)
,body))))
(bind #t (e-o e-p)
(let ([e-icount (%mref ,e-p ,(constant port-icount-disp))])
(maybe-add-port-check e-p
(maybe-add-eof-check e-o
(maybe-add-octet-check check-octet? Llib e-o
(bind #t ([t0 e-icount])
`(if ,(%inline eq? ,t0
,(%inline -
,(%inline +
,(%mref ,e-p ,(constant port-ibuffer-disp))
,(%constant bytevector-data-disp))
,(%mref ,e-p ,(constant port-ilast-disp))))
,(maybe-add-label Llib (do-libcall src sexpr e-p e-o))
(set! ,e-icount ,(%inline - ,t0 (immediate 1)))))))))))))
(define (unsafe-unget-u8-libcall src sexpr e-p e-o) (build-libcall #t src sexpr unsafe-unget-u8 e-p e-o))
(define (safe-unget-u8-libcall src sexpr e-p e-o) (build-libcall #t src sexpr safe-unget-u8 e-p e-o))
(define-inline 3 unget-u8
[(e-p e-o) (go src sexpr e-p e-o #f #f unsafe-unget-u8-libcall)])
(define-inline 2 unget-u8
[(e-p e-o) (go src sexpr e-p e-o #t (not (constant? octet? e-o)) safe-unget-u8-libcall)]))
(let ()
(define (go src sexpr e-p e-o check-port? check-octet? do-libcall)
(let ([Llib (and (or check-octet? check-port?) (make-local-label 'Llib))])
(define maybe-add-port-check
(lambda (e-p body)
(if check-port?
`(if (if ,(%type-check mask-typed-object type-typed-object ,e-p)
,(%type-check mask-binary-output-port type-binary-output-port
,(%mref ,e-p ,(constant typed-object-type-disp)))
,(%constant sfalse))
,body
(goto ,Llib))
body)))
(define add-update
(lambda (t0 e-ocount body)
`(seq
(set! ,e-ocount ,(%inline + ,t0 (immediate 1)))
,body)))
(bind check-octet? (e-o)
(bind #t (e-p)
(let ([e-ocount (%mref ,e-p ,(constant port-ocount-disp))])
(maybe-add-octet-check check-octet? Llib e-o
(maybe-add-port-check e-p
(bind #t ([t0 e-ocount])
`(if ,(%inline eq? ,t0 (immediate 0))
,(maybe-add-label Llib (do-libcall src sexpr e-o e-p))
,(add-update t0 e-ocount
`(inline ,(make-info-load 'unsigned-8 #f) ,%store
,t0
,(%mref ,e-p ,(constant port-olast-disp))
(immediate 0)
,(build-unfix e-o))))))))))))
(define (unsafe-put-u8-libcall src sexpr e-o e-p) (build-libcall #t src sexpr unsafe-put-u8 e-p e-o))
(define (safe-put-u8-libcall src sexpr e-o e-p) (build-libcall #t src sexpr safe-put-u8 e-p e-o))
(define-inline 3 put-u8
[(e-p e-o) (go src sexpr e-p e-o #f #f unsafe-put-u8-libcall)])
(define-inline 2 put-u8
[(e-p e-o) (go src sexpr e-p e-o #t (not (constant? octet? e-o)) safe-put-u8-libcall)])))
(let ()
(define (go src sexpr e-p e-c check-port? check-char? do-libcall)
(let ([Llib (and (or check-char? check-port?) (make-local-label 'Llib))])
(define maybe-add-char-check
(lambda (e-c body)
(if check-char?
`(if ,(%type-check mask-char type-char ,e-c)
,body
(goto ,Llib))
body)))
(define maybe-add-port-check
(lambda (e-p body)
(if check-port?
`(if (if ,(%type-check mask-typed-object type-typed-object ,e-p)
,(%type-check mask-textual-output-port type-textual-output-port
,(%mref ,e-p ,(constant typed-object-type-disp)))
,(%constant sfalse))
,body
(goto ,Llib))
body)))
(define add-update
(lambda (t0 e-ocount body)
`(seq
(set! ,e-ocount ,(%inline + ,t0 ,(%constant string-char-bytes)))
,body)))
(bind check-char? (e-c)
(bind #t (e-p)
(let ([e-ocount (%mref ,e-p ,(constant port-ocount-disp))])
(maybe-add-char-check e-c
(maybe-add-port-check e-p
(bind #t ([t0 e-ocount])
`(if ,(%inline eq? ,t0 (immediate 0))
,(maybe-add-label Llib (do-libcall src sexpr e-c e-p))
,(add-update t0 e-ocount
`(inline ,(make-info-load (string-char-type) #f) ,%store
,t0
,(%mref ,e-p ,(constant port-olast-disp))
(immediate 0)
,e-c)))))))))))
(define (unsafe-put-char-libcall src sexpr e-c e-p) (build-libcall #t src sexpr unsafe-put-char e-p e-c))
(define (safe-put-char-libcall src sexpr e-c e-p) (build-libcall #t src sexpr safe-put-char e-p e-c))
(define (unsafe-write-char-libcall src sexpr e-c e-p) (build-libcall #t src sexpr unsafe-write-char e-c e-p))
(define (safe-write-char-libcall src sexpr e-c e-p) (build-libcall #t src sexpr safe-write-char e-c e-p))
(define (unsafe-newline-libcall src sexpr e-c e-p) (build-libcall #t src sexpr unsafe-newline e-p))
(define (safe-newline-libcall src sexpr e-c e-p) (build-libcall #t src sexpr safe-newline e-p))
(define-inline 3 put-char
[(e-p e-c) (go src sexpr e-p e-c #f #f unsafe-put-char-libcall)])
(define-inline 2 put-char
[(e-p e-c) (go src sexpr e-p e-c #t (not (constant? char? e-c)) safe-put-char-libcall)])
(define-inline 3 write-char
[(e-c) (go src sexpr (%tc-ref current-output) e-c #f #f unsafe-write-char-libcall)]
[(e-c e-p) (go src sexpr e-p e-c #f #f unsafe-write-char-libcall)])
(define-inline 2 write-char
[(e-c) (if (constant? char? e-c)
(go src sexpr (%tc-ref current-output) e-c #f #f unsafe-write-char-libcall)
(go src sexpr (%tc-ref current-output) e-c #f #t safe-write-char-libcall))]
[(e-c e-p) (go src sexpr e-p e-c #t (not (constant? char? e-c)) safe-write-char-libcall)])
(define-inline 3 newline
[() (go src sexpr (%tc-ref current-output) `(quote #\newline) #f #f unsafe-newline-libcall)]
[(e-p) (go src sexpr e-p `(quote #\newline) #f #f unsafe-newline-libcall)])
(define-inline 2 newline
[() (go src sexpr (%tc-ref current-output) `(quote #\newline) #f #f unsafe-newline-libcall)]
[(e-p) (go src sexpr e-p `(quote #\newline) #t #f safe-newline-libcall)]))
(let ()
(define build-fxop?
(lambda (op overflow-flag e1 e2 adjust k)
(let ([Lfail (make-local-label 'Lfail)])
(bind #t (e1 e2)
`(if ,(build-fixnums? (list e1 e2))
,(bind #f ([t `(inline ,null-info ,op ,e1 ,(adjust e2))])
`(if (inline ,(make-info-condition-code overflow-flag #f #t) ,%condition-code)
(label ,Lfail ,(k e1 e2))
,t))
(goto ,Lfail))))))
(define-inline 2 +
[() `(immediate ,(fix 0))]
[(e) (build-fxop? %+/ovfl 'overflow e `(quote 0) values (lambda (e1 e2) (build-libcall #t src sexpr + e1 e2)))]
[(e1 e2) (build-fxop? %+/ovfl 'overflow e1 e2 values (lambda (e1 e2) (build-libcall #t src sexpr + e1 e2)))]
; TODO: handle 3-operand case ala fx+, w/3-operand library +
[(e1 . e*) #f])
(define-inline 2 *
[() `(immediate ,(fix 1))]
[(e) (build-fxop? %*/ovfl 'multiply-overflow e `(quote 1) build-unfix (lambda (e1 e2) (build-libcall #t src sexpr * e1 e2)))]
; TODO: swap e1 & e2 if e1 is constant
[(e1 e2) (build-fxop? %*/ovfl 'multiply-overflow e1 e2 build-unfix (lambda (e1 e2) (build-libcall #t src sexpr * e1 e2)))]
; TODO: handle 3-operand case ala fx+, w/3-operand library *
[(e1 . e*) #f])
(define-inline 2 -
[(e) (build-fxop? %-/ovfl 'overflow `(quote 0) e values (lambda (e1 e2) (build-libcall #t src sexpr - e1 e2)))]
[(e1 e2) (build-fxop? %-/ovfl 'overflow e1 e2 values (lambda (e1 e2) (build-libcall #t src sexpr - e1 e2)))]
; TODO: handle 3-operand case ala fx+, w/3-operand library -
[(e1 e2 . e*) #f]))
(let ()
(define build-fxop?
(lambda (op e k)
(let ([Lfail (make-local-label 'Lfail)])
(bind #t (e)
`(if ,(%type-check mask-fixnum type-fixnum ,e)
,(bind #f ([t `(inline ,null-info ,op ,e (immediate ,(fix 1)))])
`(if (inline ,(make-info-condition-code 'overflow #f #t) ,%condition-code)
(label ,Lfail ,(k e))
,t))
(goto ,Lfail))))))
(define-syntax define-inline-1op
(syntax-rules ()
[(_ op name)
(define-inline 2 name
[(e) (build-fxop? op e (lambda (e) (build-libcall #t src sexpr name e)))])]))
(define-inline-1op %-/ovfl 1-)
(define-inline-1op %-/ovfl -1+)
(define-inline-1op %-/ovfl sub1)
(define-inline-1op %+/ovfl 1+)
(define-inline-1op %+/ovfl add1))
(define-inline 2 /
[(e) (build-libcall #f src sexpr / `(immediate ,(fix 1)) e)]
[(e1 e2) (build-libcall #f src sexpr / e1 e2)]
[(e1 . e*) #f])
(let ()
(define (zgo src sexpr e e1 e2)
(build-simple-or
(%inline eq? ,e (immediate 0))
`(if ,(build-fixnums? (list e))
,(%constant sfalse)
,(build-libcall #t src sexpr = e1 e2))))
(define (go src sexpr e1 e2)
(or (eqvop-null-fptr e1 e2)
(relop-length RELOP= e1 e2)
(cond
[(constant? (lambda (x) (eqv? x 0)) e1)
(bind #t (e2) (zgo src sexpr e2 e1 e2))]
[(constant? (lambda (x) (eqv? x 0)) e2)
(bind #t (e1) (zgo src sexpr e1 e1 e2))]
[else (bind #t (e1 e2)
`(if ,(build-fixnums? (list e1 e2))
,(%inline eq? ,e1 ,e2)
,(build-libcall #t src sexpr = e1 e2)))])))
(define-inline 2 =
[(e1 e2) (go src sexpr e1 e2)]
[(e1 . e*) #f])
(define-inline 2 r6rs:=
[(e1 e2) (go src sexpr e1 e2)]
[(e1 e2 . e*) #f]))
(let ()
(define-syntax define-relop-inline
(syntax-rules ()
[(_ name r6rs:name relop op)
(let ()
(define builder
(lambda (e1 e2 libcall)
(or (relop-length relop e1 e2)
(bind #t (e1 e2)
`(if ,(build-fixnums? (list e1 e2))
,(%inline op ,e1 ,e2)
,(libcall e1 e2))))))
(define-inline 2 name
[(e1 e2)
(builder e1 e2
(lambda (e1 e2) (build-libcall #t src sexpr name e1 e2)))]
; TODO: handle 3-operand case w/3-operand library routine
[(e1 . e*) #f])
(define-inline 2 r6rs:name
[(e1 e2)
(builder e1 e2
(lambda (e1 e2) (build-libcall #t src sexpr name e1 e2)))]
; TODO: handle 3-operand case w/3-operand library routine
[(e1 e2 . e*) #f]))]))
(define-relop-inline < r6rs:< RELOP< <)
(define-relop-inline <= r6rs:<= RELOP<= <=)
(define-relop-inline >= r6rs:>= RELOP>= >=)
(define-relop-inline > r6rs:> RELOP> >))
(define-inline 3 positive? ; 3 so opt-level 2 errors come from positive?
[(e) (handle-prim src sexpr 3 '> (list e `(quote 0)))])
(define-inline 3 nonnegative? ; 3 so opt-level 2 errors come from nonnegative?
[(e) (handle-prim src sexpr 3 '>= (list e `(quote 0)))])
(define-inline 3 negative? ; 3 so opt-level 2 errors come from negative?
[(e) (handle-prim src sexpr 3 '< (list e `(quote 0)))])
(define-inline 3 nonpositive? ; 3 so opt-level 2 errors come from nonpositive?
[(e) (handle-prim src sexpr 3 '<= (list e `(quote 0)))])
(define-inline 2 zero?
[(e)
(or (relop-length RELOP= e)
(nanopass-case (L7 Expr) e
[(call ,info ,mdcl ,pr ,e)
(guard
(eq? (primref-name pr) 'ftype-pointer-address)
(all-set? (prim-mask unsafe) (primref-flags pr)))
(make-ftype-pointer-null? e)]
[else
(bind #t (e)
(build-simple-or
(%inline eq? ,e (immediate ,(fix 0)))
`(if ,(%type-check mask-fixnum type-fixnum ,e)
,(%constant sfalse)
,(build-libcall #t src sexpr zero? e))))]))])
(define-inline 2 positive? [(e) (relop-length RELOP> e)])
(define-inline 2 nonnegative? [(e) (relop-length RELOP>= e)])
(define-inline 2 negative? [(e) (relop-length RELOP< e)])
(define-inline 2 nonpositive? [(e) (relop-length RELOP<= e)])
(let ()
(define-syntax define-logorop-inline
(syntax-rules ()
[(_ name ...)
(let ()
(define build-logop
(lambda (src sexpr e1 e2 libcall)
(bind #t (e1 e2)
(bind #t ([t (%inline logor ,e1 ,e2)])
`(if ,(%type-check mask-fixnum type-fixnum ,t)
,t
,(libcall src sexpr e1 e2))))))
(let ()
(define libcall (lambda (src sexpr e1 e2) (build-libcall #t src sexpr name e1 e2)))
(define-inline 2 name
[() `(immediate ,(fix 0))]
[(e) (build-logop src sexpr e `(immediate ,(fix 0)) libcall)]
[(e1 e2) (build-logop src sexpr e1 e2 libcall)]
[(e1 . e*) #f]))
...)]))
(define-logorop-inline logor logior bitwise-ior))
(let ()
(define-syntax define-logop-inline
(syntax-rules ()
[(_ op unit name ...)
(let ()
(define build-logop
(lambda (src sexpr e1 e2 libcall)
(bind #t (e1 e2)
`(if ,(build-fixnums? (list e1 e2))
,(%inline op ,e1 ,e2)
,(libcall src sexpr e1 e2)))))
(let ()
(define libcall (lambda (src sexpr e1 e2) (build-libcall #t src sexpr name e1 e2)))
(define-inline 2 name
[() `(immediate ,(fix unit))]
[(e) (build-logop src sexpr e `(immediate ,(fix unit)) libcall)]
[(e1 e2) (build-logop src sexpr e1 e2 libcall)]
[(e1 . e*) #f]))
...)]))
(define-logop-inline logand -1 logand bitwise-and)
(define-logop-inline logxor 0 logxor bitwise-xor))
(let ()
(define build-lognot
(lambda (e libcall)
(bind #t (e)
`(if ,(%type-check mask-fixnum type-fixnum ,e)
,(%inline logxor ,e (immediate ,(fxlognot (constant mask-fixnum))))
,(libcall e)))))
(define-inline 2 lognot
[(e) (build-lognot e (lambda (e) (build-libcall #t src sexpr lognot e)))])
(define-inline 2 bitwise-not
[(e) (build-lognot e (lambda (e) (build-libcall #t src sexpr bitwise-not e)))]))
(let ()
(define build-logbit?
(lambda (e1 e2 libcall)
(or (nanopass-case (L7 Expr) e1
[(quote ,d)
(or (and (and (fixnum? d) (fx<= 0 d (fx- (constant fixnum-bits) 2)))
(bind #t (e2)
`(if ,(%type-check mask-fixnum type-fixnum ,e2)
,(%inline logtest ,e2 (immediate ,(fix (ash 1 d))))
,(libcall e1 e2))))
(and (and (target-fixnum? d) (> d (fx- (constant fixnum-bits) 2)))
(bind #t (e2)
`(if ,(%type-check mask-fixnum type-fixnum ,e2)
,(%inline < ,e2 (immediate ,(fix 0)))
,(libcall e1 e2)))))]
[else #f])
(bind #t (e1 e2)
`(if ,(build-and
(build-fixnums? (list e1 e2))
(%inline u< ,e1 (immediate ,(fix (constant fixnum-bits)))))
,(%inline logtest
,(%inline sra ,e2 ,(build-unfix e1))
(immediate ,(fix 1)))
,(libcall e1 e2))))))
(define-inline 2 logbit?
[(e1 e2) (build-logbit? e1 e2 (lambda (e1 e2) (build-libcall #t src sexpr logbit? e1 e2)))])
(define-inline 2 bitwise-bit-set?
[(e1 e2) (build-logbit? e2 e1 (lambda (e2 e1) (build-libcall #t src sexpr bitwise-bit-set? e1 e2)))]))
(define-inline 2 logbit1
[(e1 e2) (or (nanopass-case (L7 Expr) e1
[(quote ,d)
(and (and (fixnum? d) (fx<= 0 d (fx- (constant fixnum-bits) 2)))
(bind #t (e2)
`(if ,(%type-check mask-fixnum type-fixnum ,e2)
,(%inline logor ,e2 (immediate ,(fix (ash 1 d))))
,(build-libcall #t src sexpr logbit1 e1 e2))))]
[else #f])
(bind #t (e1 e2)
`(if ,(build-and
(build-fixnums? (list e1 e2))
(%inline u< ,e1 (immediate ,(fix (fx- (constant fixnum-bits) 1)))))
,(%inline logor ,e2
,(%inline sll (immediate ,(fix 1)) ,(build-unfix e1)))
,(build-libcall #t src sexpr logbit1 e1 e2))))])
(define-inline 2 logbit0
[(e1 e2) (or (nanopass-case (L7 Expr) e1
[(quote ,d)
(and (and (fixnum? d) (fx<= 0 d (fx- (constant fixnum-bits) 2)))
(bind #t (e2)
`(if ,(%type-check mask-fixnum type-fixnum ,e2)
,(%inline logand ,e2 (immediate ,(fix (lognot (ash 1 d)))))
,(build-libcall #t src sexpr logbit0 e1 e2))))]
[else #f])
(bind #t (e1 e2)
`(if ,(build-and
(build-fixnums? (list e1 e2))
(%inline u< ,e1 (immediate ,(fix (fx- (constant fixnum-bits) 1)))))
,(%inline logand ,e2
,(%inline lognot
,(%inline sll (immediate ,(fix 1)) ,(build-unfix e1))))
,(build-libcall #t src sexpr logbit0 e1 e2))))])
(define-inline 2 logtest
[(e1 e2) (bind #t (e1 e2)
`(if ,(build-fixnums? (list e1 e2))
,(%inline logtest ,e1 ,e2)
,(build-libcall #t src sexpr logtest e1 e2)))])
(define-inline 3 $flhash
[(e) (bind #t (e)
(%inline logand
,(%inline srl
,(constant-case ptr-bits
[(32) (%inline +
,(%mref ,e ,(constant flonum-data-disp))
,(%mref ,e ,(fx+ (constant flonum-data-disp) 4)))]
[(64) (%mref ,e ,(constant flonum-data-disp))])
(immediate 1))
(immediate ,(- (constant fixnum-factor)))))])
(let ()
(define build-flonum-extractor
(lambda (pos size e1)
(let ([cnt (- pos (constant fixnum-offset))]
[mask (* (- (expt 2 size) 1) (expt 2 (constant fixnum-offset)))])
(%inline logand
,(let ([body `(inline ,(make-info-load 'integer-32 #f) ,%load ,e1 ,%zero
(immediate ,(constant-case native-endianness
[(little) (fx+ (constant flonum-data-disp) 4)]
[(big) (constant flonum-data-disp)])))])
(let ([body (if (fx> cnt 0)
(%inline srl ,body (immediate ,cnt))
body)])
(if (fx< cnt 0)
(%inline sll ,body (immediate ,(fx- 0 cnt)))
body)))
(immediate ,mask)))))
(define-inline 3 fllp
[(e) (build-flonum-extractor 19 12 e)])
(define-inline 3 $flonum-sign
[(e) (build-flonum-extractor 31 1 e)])
(define-inline 3 $flonum-exponent
[(e) (build-flonum-extractor 20 11 e)]))
(define-inline 3 $fleqv?
[(e1 e2)
(constant-case ptr-bits
[(32) (build-and
(%inline eq?
,(%mref ,e1 ,(constant flonum-data-disp))
,(%mref ,e2 ,(constant flonum-data-disp)))
(%inline eq?
,(%mref ,e1 ,(fx+ (constant flonum-data-disp) 4))
,(%mref ,e2 ,(fx+ (constant flonum-data-disp) 4))))]
[(64) (%inline eq?
,(%mref ,e1 ,(constant flonum-data-disp))
,(%mref ,e2 ,(constant flonum-data-disp)))]
[else ($oops 'compiler-internal
"$fleqv doesn't handle ptr-bits = ~s"
(constant ptr-bits))])])
(let ()
(define build-flop-1
; NB: e must be bound
(lambda (op e)
(bind #t ([t (%constant-alloc type-flonum (constant size-flonum))])
`(seq (inline ,null-info ,op ,e ,t) ,t))))
(define build-flop-2
; NB: e1 and e2 must be bound
(lambda (op e1 e2)
(bind #t ([t (%constant-alloc type-flonum (constant size-flonum))])
`(seq (inline ,null-info ,op ,e1 ,e2 ,t) ,t))))
(define build-flabs
(lambda (e)
(bind (constant-case ptr-bits [(32) #t] [(64) #f]) (e)
(bind #t ([t (%constant-alloc type-flonum (constant size-flonum))])
(%seq
,(constant-case ptr-bits
[(64)
`(set! ,(%mref ,t ,(constant flonum-data-disp))
,(%inline logand
,(%mref ,e ,(constant flonum-data-disp))
,(%inline srl (immediate -1) (immediate 1))))]
[(32)
(let ()
(constant-case native-endianness
[(big)
(begin
(define disp-high (constant flonum-data-disp))
(define disp-low (fx+ (constant flonum-data-disp) 4)))]
[(little)
(begin
(define disp-low (constant flonum-data-disp))
(define disp-high (fx+ (constant flonum-data-disp) 4)))])
(%seq
(set! ,(%mref ,t ,disp-high)
,(%inline logand
,(%mref ,e ,disp-high)
,(%inline srl (immediate -1) (immediate 1))))
(set! ,(%mref ,t ,disp-low)
,(%mref ,e ,disp-low))))])
,t)))))
(define build-flneg
(lambda (e)
(bind (constant-case ptr-bits [(32) #t] [(64) #f]) (e)
(bind #t ([t (%constant-alloc type-flonum (constant size-flonum))])
(%seq
,(constant-case ptr-bits
[(64)
`(set! ,(%mref ,t ,(constant flonum-data-disp))
,(%inline logxor
,(%mref ,e ,(constant flonum-data-disp))
,(%inline sll (immediate 1) (immediate 63))))]
[(32)
(let ()
(constant-case native-endianness
[(big)
(begin
(define disp-high (constant flonum-data-disp))
(define disp-low (fx+ (constant flonum-data-disp) 4)))]
[(little)
(begin
(define disp-low (constant flonum-data-disp))
(define disp-high (fx+ (constant flonum-data-disp) 4)))])
(%seq
(set! ,(%mref ,t ,disp-high)
,(%inline logxor
,(%mref ,e ,disp-high)
,(%inline sll (immediate 1) (immediate 31))))
(set! ,(%mref ,t ,disp-low)
,(%mref ,e ,disp-low))))])
,t)))))
;; TODO: Rather then reducing here, (which will allocate a new flonum for each interim result)
;; we could allocate a single flonum and reuse it until the final result is calculated.
;; Better yet, we could do this across nested fl operations, so that only one flonum is
;; allocated across nested fl+, fl*, fl-, fl/ etc. operation
(define-inline 3 fl+
[() `(quote 0.0)]
[(e) e]
[(e1 e2) (bind #f (e1 e2) (build-flop-2 %fl+ e1 e2))]
[(e1 . e*) (reduce src sexpr moi e1 e*)])
(define-inline 3 fl*
[() `(quote 1.0)]
[(e) e]
[(e1 e2) (bind #f (e1 e2) (build-flop-2 %fl* e1 e2))]
[(e1 . e*) (reduce src sexpr moi e1 e*)])
(define-inline 3 fl-
[(e) (build-flneg e)]
[(e1 e2) (bind #f (e1 e2) (build-flop-2 %fl- e1 e2))]
[(e1 . e*) (reduce src sexpr moi e1 e*)])
(define-inline 3 fl/
[(e) (bind #f (e) (build-flop-2 %fl/ `(quote 1.0) e))]
[(e1 e2) (bind #f (e1 e2) (build-flop-2 %fl/ e1 e2))]
[(e1 . e*) (reduce src sexpr moi e1 e*)])
(define-inline 3 flsqrt
[(e)
(constant-case architecture
[(x86 x86_64 arm32) (bind #f (e) (build-flop-1 %flsqrt e))]
[(ppc32) #f])])
(define-inline 3 flround
; NB: there is no support in SSE2 for flround, though this was added in SSE4.1
[(e) (build-libcall #f src sexpr flround e)])
(define-inline 3 flabs
[(e) (build-flabs e)])
(let ()
(define build-fl-make-rectangular
(lambda (e1 e2)
(bind #f (e1 e2)
(bind #t ([t (%constant-alloc type-typed-object (constant size-inexactnum))])
`(seq
(set! ,(%mref ,t ,(constant inexactnum-type-disp))
,(%constant type-inexactnum))
,(%seq
(inline ,(make-info-loadfl %flreg1) ,%load-double
,e1 ,%zero ,(%constant flonum-data-disp))
(inline ,(make-info-loadfl %flreg1) ,%store-double
,t ,%zero ,(%constant inexactnum-real-disp))
(inline ,(make-info-loadfl %flreg1) ,%load-double
,e2 ,%zero ,(%constant flonum-data-disp))
(inline ,(make-info-loadfl %flreg1) ,%store-double
,t ,%zero ,(%constant inexactnum-imag-disp))
,t))))))
(define-inline 3 fl-make-rectangular
[(e1 e2) (build-fl-make-rectangular e1 e2)])
(define-inline 3 cfl-
[(e) (bind #t (e)
`(if ,(%type-check mask-flonum type-flonum ,e)
,(build-flneg e)
,(build-fl-make-rectangular
(build-flneg (build-$inexactnum-real-part e))
(build-flneg (build-$inexactnum-imag-part e)))))]
[(e1 e2) (build-libcall #f src sexpr cfl- e1 e2)]
; TODO: add 3 argument version of cfl- library function
#;[(e1 e2 e3) (build-libcall #f src sexpr cfl- e1 e2 e3)]
[(e1 e2 . e*) #f])
(define-inline 3 cfl+
[() `(quote 0.0)]
[(e) e]
[(e1 e2) (build-libcall #f src sexpr cfl+ e1 e2)]
; TODO: add 3 argument version of cfl+ library function
#;[(e1 e2 e3) (build-libcall #f src sexpr cfl+ e1 e2 e3)]
[(e1 e2 . e*) #f])
(define-inline 3 cfl*
[() `(quote 1.0)]
[(e) e]
[(e1 e2) (build-libcall #f src sexpr cfl* e1 e2)]
; TODO: add 3 argument version of cfl* library function
#;[(e1 e2 e3) (build-libcall #f src sexpr cfl* e1 e2 e3)]
[(e1 e2 . e*) #f])
(define-inline 3 cfl/
[(e) (build-libcall #f src sexpr cfl/ `(quote 1.0) e)]
[(e1 e2) (build-libcall #f src sexpr cfl/ e1 e2)]
; TODO: add 3 argument version of cfl/ library function
#;[(e1 e2 e3) (build-libcall #f src sexpr cfl/ e1 e2 e3)]
[(e1 e2 . e*) #f])
(define-inline 3 cfl-conjugate
[(e) (bind #t (e)
`(if ,(%type-check mask-flonum type-flonum ,e)
,e
,(build-fl-make-rectangular
(build-$inexactnum-real-part e)
(build-flneg (build-$inexactnum-imag-part e)))))]))
(define-inline 3 $make-exactnum
[(e1 e2) (bind #f (e1 e2)
(bind #t ([t (%constant-alloc type-typed-object (constant size-exactnum))])
(%seq
(set! ,(%mref ,t ,(constant exactnum-type-disp))
,(%constant type-exactnum))
(set! ,(%mref ,t ,(constant exactnum-real-disp)) ,e1)
(set! ,(%mref ,t ,(constant exactnum-imag-disp)) ,e2)
,t)))])
(let ()
(define (build-fl< e1 e2) (%inline fl< ,e1 ,e2))
(define (build-fl= e1 e2) (%inline fl= ,e1 ,e2))
(define (build-fl<= e1 e2) (%inline fl<= ,e1 ,e2))
(let ()
(define-syntax define-fl-cmp-inline
(lambda (x)
(syntax-case x ()
[(_ op r6rs:op builder inequality? swapped?)
(with-syntax ([(args ...) (if (datum swapped?) #'(e2 e1) #'(e1 e2))]
[reducer (if (datum inequality?)
#'reduce-inequality
#'reduce-equality)])
#'(begin
(define-inline 3 op
[(e) (bind #t (e) (build-fl= e e))]
[(e1 e2) (builder args ...)]
[(e1 e2 . e*) (reducer src sexpr moi e1 e2 e*)])
(define-inline 3 r6rs:op
[(e1 e2) (builder args ...)]
[(e1 e2 . e*) (reducer src sexpr moi e1 e2 e*)])))])))
(define-fl-cmp-inline fl= fl=? build-fl= #f #f)
(define-fl-cmp-inline fl< fl<? build-fl< #t #f)
(define-fl-cmp-inline fl> fl>? build-fl< #t #t)
(define-fl-cmp-inline fl<= fl<=? build-fl<= #t #f)
(define-fl-cmp-inline fl>= fl>=? build-fl<= #t #t))
(let ()
(define-syntax build-bind-and-check
(syntax-rules ()
[(_ src sexpr op e1 e2 body)
(bind #t (e1 e2)
`(if ,(build-and
(%type-check mask-flonum type-flonum ,e1)
(%type-check mask-flonum type-flonum ,e2))
,body
,(build-libcall #t src sexpr op e1 e2)))]))
(define-syntax define-fl-cmp-inline
(lambda (x)
(syntax-case x ()
[(_ op r6rs:op builder inequality? swapped?)
(with-syntax ([(args ...) (if (datum swapped?) #'(e2 e1) #'(e1 e2))])
#'(begin
(define-inline 2 op
[(e) #f]
[(e1 e2) (build-bind-and-check src sexpr op e1 e2 (builder args ...))]
[(e1 e2 . e*) #f])
(define-inline 2 r6rs:op
[(e1 e2) (build-bind-and-check src sexpr r6rs:op e1 e2 (builder args ...))]
[(e1 e2 . e*) #f])))])))
(define-fl-cmp-inline fl= fl=? build-fl= #f #f)
(define-fl-cmp-inline fl< fl<? build-fl< #t #f)
(define-fl-cmp-inline fl> fl>? build-fl< #t #t)
(define-fl-cmp-inline fl<= fl<=? build-fl<= #t #f)
(define-fl-cmp-inline fl>= fl>=? build-fl<= #t #t))
(let ()
(define build-cfl=
; NB: e1 and e2 must be bound
(lambda (e1 e2)
`(if ,(%type-check mask-flonum type-flonum ,e1)
(if ,(%type-check mask-flonum type-flonum ,e2)
,(build-fl= e1 e2)
,(build-and
(build-fl= `(quote 0.0) (build-$inexactnum-imag-part e2))
(build-fl= e1 (build-$inexactnum-real-part e2))))
(if ,(%type-check mask-flonum type-flonum ,e2)
,(build-and
(build-fl= `(quote 0.0) (build-$inexactnum-imag-part e1))
(build-fl= e2 (build-$inexactnum-real-part e1)))
,(build-and
(build-fl=
(build-$inexactnum-imag-part e1)
(build-$inexactnum-imag-part e2))
(build-fl=
(build-$inexactnum-real-part e1)
(build-$inexactnum-real-part e2)))))))
(define-inline 3 cfl=
[(e) (bind #f (e) (build-cfl= e e))] ; this is weird, why not just true?
[(e1 e2) (bind #f (e1 e2) (build-cfl= e1 e2))]
; TODO: should we avoid building for more then the 3 item case?
[(e1 e2 . e*) (reduce-equality src sexpr moi e1 e2 e*)])))
(let ()
(define build-flop-3
; NB: e1, e2, and e3 must be bound
(lambda (op e1 e2 e3)
(build-flop-2 op e1
(build-flop-2 op e2 e3))))
(define build-checked-flop
(case-lambda
[(e k)
(bind #t (e)
`(if ,(build-flonums? (list e))
,e
,(k e)))]
[(e1 e2 op k)
(bind #t (e1 e2)
`(if ,(build-flonums? (list e1 e2))
,(build-flop-2 op e1 e2)
,(k e1 e2)))]
[(e1 e2 e3 op k)
(bind #f (e1 e2 e3)
`(if ,(build-flonums? (list e1 e2 e3))
,(build-flop-3 op e1 e2 e3)
,(k e1 e2 e3)))]))
(define-inline 2 fl+
[() `(quote 0.0)]
[(e) (build-checked-flop e
(lambda (e)
(build-libcall #t src sexpr fl+ e `(quote 0.0))))]
[(e1 e2) (build-checked-flop e1 e2 %fl+
(lambda (e1 e2)
(build-libcall #t src sexpr fl+ e1 e2)))]
; TODO: add 3 argument fl+ library function
#;[(e1 e2 e3) (build-checked flop e1 e2 e3 %fl+
(lambda (e1 e2 e3)
(build-libcall #t src sexpr fl+ e1 e2 e3)))]
[(e1 . e*) #f])
(define-inline 2 fl*
[() `(quote 1.0)]
[(e) (build-checked-flop e
(lambda (e)
(build-libcall #t src sexpr fl* e `(quote 1.0))))]
[(e1 e2) (build-checked-flop e1 e2 %fl*
(lambda (e1 e2)
(build-libcall #t src sexpr fl* e1 e2)))]
; TODO: add 3 argument fl* library function
#;[(e1 e2 e3) (build-checked flop e1 e2 e3 %fl*
(lambda (e1 e2 e3)
(build-libcall #t src sexpr fl* e1 e2 e3)))]
[(e1 . e*) #f])
(define-inline 2 fl-
[(e)
(bind #t (e)
`(if ,(build-flonums? (list e))
,(build-flneg e)
,(build-libcall #t src sexpr flnegate e)))]
[(e1 e2) (build-checked-flop e1 e2 %fl-
(lambda (e1 e2)
(build-libcall #t src sexpr fl- e1 e2)))]
; TODO: add 3 argument fl- library function
#;[(e1 e2 e3) (build-checked flop e1 e2 e3 %fl-
(lambda (e1 e2 e3)
(build-libcall #t src sexpr fl- e1 e2 e3)))]
[(e1 . e*) #f])
(define-inline 2 fl/
[(e) (build-checked-flop `(quote 1.0) e %fl/
(lambda (e1 e2)
(build-libcall #t src sexpr fl/ e1 e2)))]
[(e1 e2) (build-checked-flop e1 e2 %fl/
(lambda (e1 e2)
(build-libcall #t src sexpr fl/ e1 e2)))]
; TODO: add 3 argument fl/ library function
#;[(e1 e2 e3) (build-checked flop e1 e2 e3 %fl/
(lambda (e1 e2 e3)
(build-libcall #t src sexpr fl/ e1 e2 e3)))]
[(e1 . e*) #f])))
; NB: assuming that we have a trunc instruction for now, will need to change to support Sparc
(define-inline 3 flonum->fixnum
[(e-x) (bind #f (e-x)
(build-fix
(%inline trunc ,e-x)))])
(let ()
(define build-fixnum->flonum
; NB: x must already be bound in order to ensure it is done before the flonum is allocated
(lambda (e-x)
(bind #t ([t (%constant-alloc type-flonum (constant size-flonum))])
(%seq
,(%inline flt ,(build-unfix e-x) ,t)
,t))))
(define-inline 3 fixnum->flonum
[(e-x) (bind #f (e-x) (build-fixnum->flonum e-x))])
(define-inline 2 real->flonum
[(e-x)
(if (constant? flonum? e-x)
e-x
(bind #t (e-x)
`(if ,(%type-check mask-fixnum type-fixnum ,e-x)
,(build-fixnum->flonum e-x)
(if ,(%type-check mask-flonum type-flonum ,e-x)
,e-x
,(build-libcall #t src sexpr real->flonum e-x `(quote real->flonum))))))]))
(define-inline 3 $real->flonum
[(x who) (build-$real->flonum src sexpr x who)])
(define-inline 2 $record
[(tag . args) (build-$record tag args)])
(define-inline 3 $object-address
[(e-ptr e-offset)
(unsigned->ptr
(%inline + ,e-ptr ,(build-unfix e-offset))
(type->width ptr-type))])
(define-inline 3 $address->object
[(e-addr e-roffset)
(bind #f (e-roffset)
(%inline -
,(ptr->integer e-addr (type->width ptr-type))
,(build-unfix e-roffset)))])
(define-inline 2 $object-ref
[(type base offset)
(nanopass-case (L7 Expr) type
[(quote ,d)
(let ([type (filter-foreign-type d)])
(and (memq type (record-datatype list))
(not (memq type '(char wchar boolean)))
(build-object-ref #f type base offset)))]
[else #f])])
(define-inline 2 $swap-object-ref
[(type base offset)
(nanopass-case (L7 Expr) type
[(quote ,d)
(let ([type (filter-foreign-type d)])
(and (memq type (record-datatype list))
(not (memq type '(char wchar boolean)))
(build-object-ref #t type base offset)))]
[else #f])])
(define-inline 3 foreign-ref
[(e-type e-addr e-offset)
(nanopass-case (L7 Expr) e-type
[(quote ,d)
(let ([type (filter-foreign-type d)])
(and (memq type (record-datatype list))
(not (memq type '(char wchar boolean)))
(bind #f (e-offset)
(build-object-ref #f type
(ptr->integer e-addr (constant ptr-bits))
e-offset))))]
[else #f])])
(define-inline 2 $object-set!
[(type base offset value)
(nanopass-case (L7 Expr) type
[(quote ,d)
(let ([type (filter-foreign-type d)])
(and (memq type (record-datatype list))
(not (memq type '(char wchar boolean)))
(or (>= (constant ptr-bits) (type->width type)) (eq? type 'double-float))
(build-object-set! type base offset value)))]
[else #f])])
(define-inline 3 foreign-set!
[(e-type e-addr e-offset e-value)
(nanopass-case (L7 Expr) e-type
[(quote ,d)
(let ([type (filter-foreign-type d)])
(and (memq type (record-datatype list))
(not (memq type '(char wchar boolean)))
(or (>= (constant ptr-bits) (type->width type)) (eq? type 'double-float))
(bind #f (e-offset e-value)
(build-object-set! type
(ptr->integer e-addr (constant ptr-bits))
e-offset
e-value))))]
[else #f])])
(define-inline 2 $make-fptr
[(e-ftype e-addr)
(nanopass-case (L7 Expr) e-addr
[(call ,info ,mdcl ,pr ,e1)
(guard
(eq? (primref-name pr) 'ftype-pointer-address)
(all-set? (prim-mask unsafe) (primref-flags pr)))
(bind #f (e-ftype e1)
(bind #t ([t (%constant-alloc type-typed-object (fx* 2 (constant ptr-bytes)))])
(%seq
(set! ,(%mref ,t ,(constant record-type-disp)) ,e-ftype)
(set! ,(%mref ,t ,(constant record-data-disp))
,(%mref ,e1 ,(constant record-data-disp)))
,t)))]
[else
(bind #f (e-ftype e-addr)
(bind #t ([t (%constant-alloc type-typed-object (fx* 2 (constant ptr-bytes)))])
(%seq
(set! ,(%mref ,t ,(constant record-type-disp)) ,e-ftype)
(set! ,(%mref ,t ,(constant record-data-disp))
,(ptr->integer e-addr (constant ptr-bits)))
,t)))])])
(define-inline 3 ftype-pointer-address
[(e-fptr)
(build-object-ref #f
(constant-case ptr-bits
[(64) 'unsigned-64]
[(32) 'unsigned-32])
e-fptr %zero (constant record-data-disp))])
(define-inline 3 ftype-pointer-null?
[(e-fptr) (make-ftype-pointer-null? e-fptr)])
(define-inline 3 ftype-pointer=?
[(e1 e2) (make-ftype-pointer-equal? e1 e2)])
(let ()
(define build-fx+raw
(lambda (fx-arg raw-arg)
(if (constant? (lambda (x) (eqv? x 0)) fx-arg)
raw-arg
(%inline + ,raw-arg ,(build-unfix fx-arg)))))
(define $extract-fptr-address
(lambda (e-fptr)
(define suppress-unsafe-cast
(lambda (e-fptr)
(nanopass-case (L7 Expr) e-fptr
[(call ,info1 ,mdcl1 ,pr1 (quote ,d) (call ,info2 ,mdcl2 ,pr2 ,e))
(guard
(eq? (primref-name pr1) '$make-fptr)
(all-set? (prim-mask unsafe) (primref-flags pr2))
(eq? (primref-name pr2) 'ftype-pointer-address)
(all-set? (prim-mask unsafe) (primref-flags pr2)))
e]
[else e-fptr])))
(nanopass-case (L7 Expr) e-fptr
; skip allocation and dereference of ftype-pointer for $fptr-fptr-ref
[(call ,info ,mdcl ,pr ,e1 ,e2 ,e3) ; e1, e2, e3 = fptr, offset, ftd
(guard
(eq? (primref-name pr) '$fptr-fptr-ref)
(all-set? (prim-mask unsafe) (primref-flags pr)))
(let-values ([(e-index imm-offset) (offset-expr->index+offset e2)])
(bind #f (e-index e3)
`(inline ,(make-info-load ptr-type #f) ,%load
,($extract-fptr-address e1)
,e-index (immediate ,imm-offset))))]
; skip allocation and dereference of ftype-pointer for $fptr-&ref
[(call ,info ,mdcl ,pr ,e1 ,e2 ,e3) ; e1, e2, e3 = fptr, offset, ftd
(guard
(eq? (primref-name pr) '$fptr-&ref)
(all-set? (prim-mask unsafe) (primref-flags pr)))
(build-fx+raw e2 ($extract-fptr-address e1))]
; skip allocation and dereference of ftype-pointer for $make-fptr
[(call ,info ,mdcl ,pr ,e1 ,e2) ; e1, e2 = ftd, (ptr) addr
(guard
(eq? (primref-name pr) '$make-fptr)
(all-set? (prim-mask unsafe) (primref-flags pr)))
(nanopass-case (L7 Expr) e2
[(call ,info ,mdcl ,pr ,e3)
(guard
(eq? (primref-name pr) 'ftype-pointer-address)
(all-set? (prim-mask unsafe) (primref-flags pr)))
(bind #f (e1)
(%mref ,e3 ,(constant record-data-disp)))]
[else
(bind #f (e1)
(ptr->integer e2 (constant ptr-bits)))])]
[else
`(inline ,(make-info-load ptr-type #f) ,%load ,(suppress-unsafe-cast e-fptr) ,%zero
,(%constant record-data-disp))])))
(let ()
(define-inline 3 $fptr-offset-addr
[(e-fptr e-offset)
; bind offset before doing the load (a) to maintain applicative order---the
; load can cause an invalid memory reference---and (b) so that the raw value
; isn't live across any calls
(bind #f (e-offset)
(build-fx+raw e-offset
($extract-fptr-address e-fptr)))])
(define-inline 3 $fptr-&ref
[(e-fptr e-offset e-ftd)
; see comment in $fptr-offset-addr
(bind #f (e-offset e-ftd)
(build-$record e-ftd
(list (build-fx+raw e-offset ($extract-fptr-address e-fptr)))))]))
(define-inline 3 $fptr-fptr-ref
[(e-fptr e-offset e-ftd)
(let-values ([(e-index imm-offset) (offset-expr->index+offset e-offset)])
(bind #f (e-index)
(build-$record e-ftd
(list `(inline ,(make-info-load ptr-type #f) ,%load
,($extract-fptr-address e-fptr)
,e-index (immediate ,imm-offset))))))])
(define-inline 3 $fptr-fptr-set!
[(e-fptr e-offset e-val)
(let-values ([(e-index imm-offset) (offset-expr->index+offset e-offset)])
(bind #f ([e-addr ($extract-fptr-address e-fptr)] e-index e-val)
`(inline ,(make-info-load ptr-type #f) ,%store ,e-addr ,e-index (immediate ,imm-offset)
(inline ,(make-info-load ptr-type #f) ,%load ,e-val ,%zero
,(%constant record-data-disp)))))])
(let ()
(define $do-fptr-ref-inline
(lambda (swapped? type e-fptr e-offset)
(bind #f (e-offset)
(build-object-ref swapped? type ($extract-fptr-address e-fptr) e-offset))))
(define-syntax define-fptr-ref-inline
(lambda (x)
(define build-inline
(lambda (name type ref maybe-k)
#`(define-inline 3 #,name
[(e-fptr e-offset)
#,((lambda (body) (if maybe-k #`(#,maybe-k #,body) body))
#`($do-fptr-ref-inline #,ref #,type e-fptr e-offset))])))
(syntax-case x ()
[(_ name ?type ref) (build-inline #'name #'?type #'ref #f)]
[(_ name ?type ref ?k) (build-inline #'name #'?type #'ref #'?k)])))
(define-fptr-ref-inline $fptr-ref-integer-8 'integer-8 #f)
(define-fptr-ref-inline $fptr-ref-unsigned-8 'unsigned-8 #f)
(define-fptr-ref-inline $fptr-ref-integer-16 'integer-16 #f)
(define-fptr-ref-inline $fptr-ref-unsigned-16 'unsigned-16 #f)
(define-fptr-ref-inline $fptr-ref-swap-integer-16 'integer-16 #t)
(define-fptr-ref-inline $fptr-ref-swap-unsigned-16 'unsigned-16 #t)
(define-fptr-ref-inline $fptr-ref-integer-24 'integer-24 #f)
(define-fptr-ref-inline $fptr-ref-unsigned-24 'unsigned-24 #f)
(define-fptr-ref-inline $fptr-ref-swap-integer-24 'integer-24 #t)
(define-fptr-ref-inline $fptr-ref-swap-unsigned-24 'unsigned-24 #t)
(define-fptr-ref-inline $fptr-ref-integer-32 'integer-32 #f)
(define-fptr-ref-inline $fptr-ref-unsigned-32 'unsigned-32 #f)
(define-fptr-ref-inline $fptr-ref-swap-integer-32 'integer-32 #t)
(define-fptr-ref-inline $fptr-ref-swap-unsigned-32 'unsigned-32 #t)
(define-fptr-ref-inline $fptr-ref-integer-40 'integer-40 #f)
(define-fptr-ref-inline $fptr-ref-unsigned-40 'unsigned-40 #f)
(define-fptr-ref-inline $fptr-ref-swap-integer-40 'integer-40 #t)
(define-fptr-ref-inline $fptr-ref-swap-unsigned-40 'unsigned-40 #t)
(define-fptr-ref-inline $fptr-ref-integer-48 'integer-48 #f)
(define-fptr-ref-inline $fptr-ref-unsigned-48 'unsigned-48 #f)
(define-fptr-ref-inline $fptr-ref-swap-integer-48 'integer-48 #t)
(define-fptr-ref-inline $fptr-ref-swap-unsigned-48 'unsigned-48 #t)
(define-fptr-ref-inline $fptr-ref-integer-56 'integer-56 #f)
(define-fptr-ref-inline $fptr-ref-unsigned-56 'unsigned-56 #f)
(define-fptr-ref-inline $fptr-ref-swap-integer-56 'integer-56 #t)
(define-fptr-ref-inline $fptr-ref-swap-unsigned-56 'unsigned-56 #t)
(define-fptr-ref-inline $fptr-ref-integer-64 'integer-64 #f)
(define-fptr-ref-inline $fptr-ref-unsigned-64 'unsigned-64 #f)
(define-fptr-ref-inline $fptr-ref-swap-integer-64 'integer-64 #t)
(define-fptr-ref-inline $fptr-ref-swap-unsigned-64 'unsigned-64 #t)
(define-fptr-ref-inline $fptr-ref-double-float 'double-float #f)
(define-fptr-ref-inline $fptr-ref-swap-double-float 'double-float #t)
(define-fptr-ref-inline $fptr-ref-single-float 'single-float #f)
(define-fptr-ref-inline $fptr-ref-swap-single-float 'single-float #t)
(define-fptr-ref-inline $fptr-ref-char 'unsigned-8 #f
(lambda (x) (build-integer->char x)))
(define-fptr-ref-inline $fptr-ref-wchar
(constant-case wchar-bits [(16) 'unsigned-16] [(32) 'unsigned-32])
#f
(lambda (x) (build-integer->char x)))
(define-fptr-ref-inline $fptr-ref-swap-wchar
(constant-case wchar-bits [(16) 'unsigned-16] [(32) 'unsigned-32])
#t
(lambda (x) (build-integer->char x)))
(define-fptr-ref-inline $fptr-ref-boolean
(constant-case int-bits [(32) 'unsigned-32] [(64) 'unsigned-64])
#f
(lambda (x)
`(if ,(%inline eq? ,x (immediate 0))
,(%constant sfalse)
,(%constant strue))))
(define-fptr-ref-inline $fptr-ref-swap-boolean
(constant-case int-bits [(32) 'unsigned-32] [(64) 'unsigned-64])
#t
(lambda (x)
`(if ,(%inline eq? ,x (immediate 0))
,(%constant sfalse)
,(%constant strue))))
(define-fptr-ref-inline $fptr-ref-fixnum 'fixnum #f)
(define-fptr-ref-inline $fptr-ref-swap-fixnum 'fixnum #t))
(let ()
(define $do-fptr-set!-inline
(lambda (set type e-fptr e-offset e-val)
(bind #f (e-offset)
(set type ($extract-fptr-address e-fptr) e-offset e-val))))
(define-syntax define-fptr-set!-inline
(lambda (x)
(define build-body
(lambda (type set maybe-massage-val)
#``(seq ,e-info
#,(let ([body #`($do-fptr-set!-inline #,set #,type e-fptr e-offset e-val)])
(if maybe-massage-val
#`,(bind #f (e-offset [e-val (#,maybe-massage-val e-val)]) #,body)
#`,(bind #f (e-offset e-val) #,body))))))
(define build-inline
(lambda (name check-64? body)
#`(define-inline 3 #,name
[(e-info e-fptr e-offset e-val)
#,(if check-64?
#`(and (fx>= (constant ptr-bits) 64) #,body)
body)])))
(syntax-case x ()
[(_ check-64? name ?type set)
(build-inline #'name (datum check-64?) (build-body #'?type #'set #f))]
[(_ check-64? name ?type set ?massage-value)
(build-inline #'name (datum check-64?) (build-body #'?type #'set #'?massage-value))])))
(define-fptr-set!-inline #f $fptr-set-integer-8! 'integer-8 build-object-set!)
(define-fptr-set!-inline #f $fptr-set-unsigned-8! 'unsigned-8 build-object-set!)
(define-fptr-set!-inline #f $fptr-set-integer-16! 'integer-16 build-object-set!)
(define-fptr-set!-inline #f $fptr-set-unsigned-16! 'unsigned-16 build-object-set!)
(define-fptr-set!-inline #f $fptr-set-swap-integer-16! 'integer-16 build-swap-object-set!)
(define-fptr-set!-inline #f $fptr-set-swap-unsigned-16! 'unsigned-16 build-swap-object-set!)
(define-fptr-set!-inline #f $fptr-set-integer-24! 'integer-24 build-object-set!)
(define-fptr-set!-inline #f $fptr-set-unsigned-24! 'unsigned-24 build-object-set!)
(define-fptr-set!-inline #f $fptr-set-swap-integer-24! 'integer-24 build-swap-object-set!)
(define-fptr-set!-inline #f $fptr-set-swap-unsigned-24! 'unsigned-24 build-swap-object-set!)
(define-fptr-set!-inline #f $fptr-set-integer-32! 'integer-32 build-object-set!)
(define-fptr-set!-inline #f $fptr-set-unsigned-32! 'unsigned-32 build-object-set!)
(define-fptr-set!-inline #f $fptr-set-swap-integer-32! 'integer-32 build-swap-object-set!)
(define-fptr-set!-inline #f $fptr-set-swap-unsigned-32! 'unsigned-32 build-swap-object-set!)
(define-fptr-set!-inline #t $fptr-set-integer-40! 'integer-40 build-object-set!)
(define-fptr-set!-inline #t $fptr-set-unsigned-40! 'unsigned-40 build-object-set!)
(define-fptr-set!-inline #t $fptr-set-swap-integer-40! 'integer-40 build-swap-object-set!)
(define-fptr-set!-inline #t $fptr-set-swap-unsigned-40! 'unsigned-40 build-swap-object-set!)
(define-fptr-set!-inline #t $fptr-set-integer-48! 'integer-48 build-object-set!)
(define-fptr-set!-inline #t $fptr-set-unsigned-48! 'unsigned-48 build-object-set!)
(define-fptr-set!-inline #t $fptr-set-swap-integer-48! 'integer-48 build-swap-object-set!)
(define-fptr-set!-inline #t $fptr-set-swap-unsigned-48! 'unsigned-48 build-swap-object-set!)
(define-fptr-set!-inline #t $fptr-set-integer-56! 'integer-56 build-object-set!)
(define-fptr-set!-inline #t $fptr-set-unsigned-56! 'unsigned-56 build-object-set!)
(define-fptr-set!-inline #t $fptr-set-swap-integer-56! 'integer-56 build-swap-object-set!)
(define-fptr-set!-inline #t $fptr-set-swap-unsigned-56! 'unsigned-56 build-swap-object-set!)
(define-fptr-set!-inline #t $fptr-set-integer-64! 'integer-64 build-object-set!)
(define-fptr-set!-inline #t $fptr-set-unsigned-64! 'unsigned-64 build-object-set!)
(define-fptr-set!-inline #t $fptr-set-swap-integer-64! 'integer-64 build-swap-object-set!)
(define-fptr-set!-inline #t $fptr-set-swap-unsigned-64! 'unsigned-64 build-swap-object-set!)
(define-fptr-set!-inline #f $fptr-set-double-float! 'double-float build-object-set!)
(define-fptr-set!-inline #t $fptr-set-swap-double-float! 'double-float build-swap-object-set!)
(define-fptr-set!-inline #f $fptr-set-single-float! 'single-float build-object-set!)
(define-fptr-set!-inline #f $fptr-set-char! 'unsigned-8 build-object-set!
(lambda (z) (build-char->integer z)))
(define-fptr-set!-inline #f $fptr-set-wchar!
(constant-case wchar-bits
[(16) 'unsigned-16]
[(32) 'unsigned-32])
build-object-set!
(lambda (z) (build-char->integer z)))
(define-fptr-set!-inline #f $fptr-set-swap-wchar!
(constant-case wchar-bits
[(16) 'unsigned-16]
[(32) 'unsigned-32])
build-swap-object-set!
(lambda (z) (build-char->integer z)))
(define-fptr-set!-inline #f $fptr-set-boolean!
(constant-case int-bits
[(32) 'unsigned-32]
[(64) 'unsigned-64])
build-object-set!
(lambda (z) `(if ,z (immediate ,(fix 1)) (immediate ,(fix 0)))))
(define-fptr-set!-inline #f $fptr-set-swap-boolean!
(constant-case int-bits
[(32) 'unsigned-32]
[(64) 'unsigned-64])
build-swap-object-set!
(lambda (z) `(if ,z (immediate ,(fix 1)) (immediate ,(fix 0)))))
(define-fptr-set!-inline #f $fptr-set-fixnum! 'fixnum build-object-set!)
(define-fptr-set!-inline #f $fptr-set-swap-fixnum! 'fixnum build-swap-object-set!))
(let ()
(define-syntax define-fptr-bits-ref-inline
(lambda (x)
(syntax-case x ()
[(_ name signed? type swapped?)
#'(define-inline 3 name
[(e-fptr e-offset e-start e-end)
(and (fixnum-constant? e-start) (fixnum-constant? e-end)
(let ([imm-start (constant-value e-start)] [imm-end (constant-value e-end)])
(and (<= (type->width 'type) (constant ptr-bits))
(and (fx>= imm-start 0) (fx> imm-end imm-start) (fx<= imm-end (constant ptr-bits)))
((if signed? fx<= fx<) (fx- imm-end imm-start) (constant fixnum-bits))
(let-values ([(e-index imm-offset) (offset-expr->index+offset e-offset)])
(bind #f (e-index)
(build-int-load swapped? 'type ($extract-fptr-address e-fptr) e-index imm-offset
(lambda (x)
((if signed? extract-signed-bitfield extract-unsigned-bitfield) #t imm-start imm-end x))))))))])])))
(define-fptr-bits-ref-inline $fptr-ref-ibits-unsigned-8 #t unsigned-8 #f)
(define-fptr-bits-ref-inline $fptr-ref-ubits-unsigned-8 #f unsigned-8 #f)
(define-fptr-bits-ref-inline $fptr-ref-ibits-unsigned-16 #t unsigned-16 #f)
(define-fptr-bits-ref-inline $fptr-ref-ubits-unsigned-16 #f unsigned-16 #f)
(define-fptr-bits-ref-inline $fptr-ref-ibits-swap-unsigned-16 #t unsigned-16 #t)
(define-fptr-bits-ref-inline $fptr-ref-ubits-swap-unsigned-16 #f unsigned-16 #t)
(define-fptr-bits-ref-inline $fptr-ref-ibits-unsigned-24 #t unsigned-24 #f)
(define-fptr-bits-ref-inline $fptr-ref-ubits-unsigned-24 #f unsigned-24 #f)
(define-fptr-bits-ref-inline $fptr-ref-ibits-swap-unsigned-24 #t unsigned-24 #t)
(define-fptr-bits-ref-inline $fptr-ref-ubits-swap-unsigned-24 #f unsigned-24 #t)
(define-fptr-bits-ref-inline $fptr-ref-ibits-unsigned-32 #t unsigned-32 #f)
(define-fptr-bits-ref-inline $fptr-ref-ubits-unsigned-32 #f unsigned-32 #f)
(define-fptr-bits-ref-inline $fptr-ref-ibits-swap-unsigned-32 #t unsigned-32 #t)
(define-fptr-bits-ref-inline $fptr-ref-ubits-swap-unsigned-32 #f unsigned-32 #t)
(define-fptr-bits-ref-inline $fptr-ref-ibits-unsigned-40 #t unsigned-40 #f)
(define-fptr-bits-ref-inline $fptr-ref-ubits-unsigned-40 #f unsigned-40 #f)
(define-fptr-bits-ref-inline $fptr-ref-ibits-swap-unsigned-40 #t unsigned-40 #t)
(define-fptr-bits-ref-inline $fptr-ref-ubits-swap-unsigned-40 #f unsigned-40 #t)
(define-fptr-bits-ref-inline $fptr-ref-ibits-unsigned-48 #t unsigned-48 #f)
(define-fptr-bits-ref-inline $fptr-ref-ubits-unsigned-48 #f unsigned-48 #f)
(define-fptr-bits-ref-inline $fptr-ref-ibits-swap-unsigned-48 #t unsigned-48 #t)
(define-fptr-bits-ref-inline $fptr-ref-ubits-swap-unsigned-48 #f unsigned-48 #t)
(define-fptr-bits-ref-inline $fptr-ref-ibits-unsigned-56 #t unsigned-56 #f)
(define-fptr-bits-ref-inline $fptr-ref-ubits-unsigned-56 #f unsigned-56 #f)
(define-fptr-bits-ref-inline $fptr-ref-ibits-swap-unsigned-56 #t unsigned-56 #t)
(define-fptr-bits-ref-inline $fptr-ref-ubits-swap-unsigned-56 #f unsigned-56 #t)
(define-fptr-bits-ref-inline $fptr-ref-ibits-unsigned-64 #t unsigned-64 #f)
(define-fptr-bits-ref-inline $fptr-ref-ubits-unsigned-64 #f unsigned-64 #f)
(define-fptr-bits-ref-inline $fptr-ref-ibits-swap-unsigned-64 #t unsigned-64 #t)
(define-fptr-bits-ref-inline $fptr-ref-ubits-swap-unsigned-64 #f unsigned-64 #t))
(let ()
(define-syntax define-fptr-bits-set-inline
(lambda (x)
(syntax-case x ()
[(_ check-64? name type swapped?)
(with-syntax ([(checks ...) #'((fixnum-constant? e-start) (fixnum-constant? e-end))])
(with-syntax ([(checks ...) (if (datum check-64?)
#'((fx>= (constant ptr-bits) 64) checks ...)
#'(checks ...))])
#`(define-inline 3 name
[(e-fptr e-offset e-start e-end e-val)
(and
checks ...
(let ([imm-start (constant-value e-start)] [imm-end (constant-value e-end)])
(and (<= (type->width 'type) (constant ptr-bits))
(and (fx>= imm-start 0) (fx> imm-end imm-start) (fx<= imm-end (constant ptr-bits)))
(fx< (fx- imm-end imm-start) (constant fixnum-bits))
(let-values ([(e-index imm-offset) (offset-expr->index+offset e-offset)])
(bind #t (e-index)
(bind #f (e-val)
(bind #t ([e-addr ($extract-fptr-address e-fptr)])
(build-int-load swapped? 'type e-addr e-index imm-offset
(lambda (x)
(build-int-store swapped? 'type e-addr e-index imm-offset
(insert-bitfield #t imm-start imm-end (type->width 'type) x
e-val)))))))))))])))])))
(define-fptr-bits-set-inline #f $fptr-set-bits-unsigned-8! unsigned-8 #f)
(define-fptr-bits-set-inline #f $fptr-set-bits-unsigned-16! unsigned-16 #f)
(define-fptr-bits-set-inline #f $fptr-set-bits-swap-unsigned-16! unsigned-16 #t)
(define-fptr-bits-set-inline #f $fptr-set-bits-unsigned-24! unsigned-24 #f)
(define-fptr-bits-set-inline #f $fptr-set-bits-swap-unsigned-24! unsigned-24 #t)
(define-fptr-bits-set-inline #f $fptr-set-bits-unsigned-32! unsigned-32 #f)
(define-fptr-bits-set-inline #f $fptr-set-bits-swap-unsigned-32! unsigned-32 #t)
(define-fptr-bits-set-inline #f $fptr-set-bits-unsigned-40! unsigned-40 #f)
(define-fptr-bits-set-inline #f $fptr-set-bits-swap-unsigned-40! unsigned-40 #t)
(define-fptr-bits-set-inline #f $fptr-set-bits-unsigned-48! unsigned-48 #f)
(define-fptr-bits-set-inline #f $fptr-set-bits-swap-unsigned-48! unsigned-48 #t)
(define-fptr-bits-set-inline #f $fptr-set-bits-unsigned-56! unsigned-56 #f)
(define-fptr-bits-set-inline #f $fptr-set-bits-swap-unsigned-56! unsigned-56 #t)
(define-fptr-bits-set-inline #t $fptr-set-bits-unsigned-64! unsigned-64 #f)
(define-fptr-bits-set-inline #t $fptr-set-bits-swap-unsigned-64! unsigned-64 #t))
(define-inline 3 $fptr-locked-decr!
[(e-fptr e-offset)
`(seq
,(let-values ([(e-index imm-offset) (offset-expr->index+offset e-offset)])
(%inline locked-decr!
,($extract-fptr-address e-fptr)
,e-index (immediate ,imm-offset)))
(inline ,(make-info-condition-code 'eq? #f #t) ,%condition-code))])
(define-inline 3 $fptr-locked-incr!
[(e-fptr e-offset)
`(seq
,(let-values ([(e-index imm-offset) (offset-expr->index+offset e-offset)])
(%inline locked-incr!
,($extract-fptr-address e-fptr)
,e-index (immediate ,imm-offset)))
(inline ,(make-info-condition-code 'eq? #f #t) ,%condition-code))])
(let ()
(define clear-lock
(lambda (e-fptr e-offset)
(let ([lock-type (constant-case ptr-bits [(32) 'integer-32] [(64) 'integer-64])])
(let-values ([(e-index imm-offset) (offset-expr->index+offset e-offset)])
`(inline ,(make-info-load lock-type #f) ,%store
,($extract-fptr-address e-fptr)
,e-index (immediate ,imm-offset) (immediate 0))))))
(define-inline 3 $fptr-init-lock!
[(e-fptr e-offset) (clear-lock e-fptr e-offset)])
(define-inline 3 $fptr-unlock!
[(e-fptr e-offset) (clear-lock e-fptr e-offset)]))
(define-inline 3 $fptr-lock!
[(e-fptr e-offset)
(let-values ([(e-index imm-offset) (offset-expr->index+offset e-offset)])
(bind #t ([e-base ($extract-fptr-address e-fptr)])
(%inline lock! ,e-base ,e-index (immediate ,imm-offset))))])
(define-inline 3 $fptr-spin-lock!
[(e-fptr e-offset)
(let-values ([(e-index imm-offset) (offset-expr->index+offset e-offset)])
(bind #t ([e-base ($extract-fptr-address e-fptr)])
(bind #t (e-index)
(let ([L1 (make-local-label 'L1)] [L2 (make-local-label 'L2)])
`(label ,L1
(if ,(%inline lock! ,e-base ,e-index (immediate ,imm-offset))
,(%constant svoid)
(seq
(pariah)
(label ,L2
(seq
,(%inline pause)
(if ,(%inline eq? (mref ,e-base ,e-index ,imm-offset) (immediate 0))
(goto ,L1)
(goto ,L2)))))))))))]))
(let ()
(define build-port-flags-set?
(lambda (e-p e-flags)
(%inline logtest
,(%mref ,e-p ,(constant port-type-disp))
,(nanopass-case (L7 Expr) e-flags
[(quote ,d) `(immediate ,(ash d (constant port-flags-offset)))]
[else (%inline sll ,e-flags
(immediate ,(fx- (constant port-flags-offset) (constant fixnum-offset))))]))))
(define build-port-input-empty?
(lambda (e-p)
(%inline eq?
,(%mref ,e-p ,(constant port-icount-disp))
(immediate 0))))
(define-inline 3 binary-port?
[(e-p) (build-port-flags-set? e-p `(quote ,(constant port-flag-binary)))])
(define-inline 3 textual-port?
[(e-p) (build-not (build-port-flags-set? e-p `(quote ,(constant port-flag-binary))))])
(define-inline 3 port-closed?
[(e-p) (build-port-flags-set? e-p `(quote ,(constant port-flag-closed)))])
(define-inline 3 $port-flags-set?
[(e-p e-flags) (build-port-flags-set? e-p e-flags)])
(define-inline 3 port-eof?
[(e-p)
(bind #t (e-p)
`(if ,(build-port-input-empty? e-p)
(if ,(build-port-flags-set? e-p `(quote ,(constant port-flag-eof)))
(immediate ,(constant strue))
,(build-libcall #t src sexpr unsafe-port-eof? e-p))
(immediate ,(constant sfalse))))])
(define-inline 2 port-eof?
[(e-p)
(let ([Llib (make-local-label 'Llib)])
(bind #t (e-p)
`(if ,(%type-check mask-typed-object type-typed-object ,e-p)
,(bind #t ([t0 (%mref ,e-p ,(constant typed-object-type-disp))])
`(if ,(%type-check mask-input-port type-input-port ,t0)
(if ,(build-port-input-empty? e-p)
(if ,(%inline logtest ,t0
(immediate ,(ash (constant port-flag-eof) (constant port-flags-offset))))
(immediate ,(constant strue))
(label ,Llib ,(build-libcall #t src sexpr safe-port-eof? e-p)))
(immediate ,(constant sfalse)))
(goto ,Llib)))
(goto ,Llib))))])
(define-inline 3 port-input-empty?
[(e-p) (build-port-input-empty? e-p)])
(define-inline 3 port-output-full?
[(e-p)
(%inline eq?
,(%mref ,e-p ,(constant port-ocount-disp))
(immediate 0))]))
(let ()
(define build-set-port-flags!
(lambda (e-p e-flags)
(bind #t (e-p)
`(set! ,(%mref ,e-p ,(constant port-type-disp))
,(%inline logor
,(%mref ,e-p ,(constant port-type-disp))
,(nanopass-case (L7 Expr) e-flags
[(quote ,d) `(immediate ,(ash d (constant port-flags-offset)))]
[else
(translate e-flags
(constant fixnum-offset)
(constant port-flags-offset))]))))))
(define build-reset-port-flags!
(lambda (e-p e-flags)
(bind #t (e-p)
`(set! ,(%mref ,e-p ,(constant port-type-disp))
,(%inline logand
,(%mref ,e-p ,(constant port-type-disp))
,(nanopass-case (L7 Expr) e-flags
[(quote ,d) `(immediate ,(lognot (ash d (constant port-flags-offset))))]
[else
(%inline lognot
,(translate e-flags
(constant fixnum-offset)
(constant port-flags-offset)))]))))))
(define-inline 3 $set-port-flags!
[(e-p e-flags) (build-set-port-flags! e-p e-flags)])
(define-inline 3 $reset-port-flags!
[(e-p e-flags) (build-reset-port-flags! e-p e-flags)])
(define-inline 3 mark-port-closed!
[(e-p) (build-set-port-flags! e-p `(quote ,(constant port-flag-closed)))])
(let ()
(define (go e-p e-bool flag)
(let ([e-flags `(quote ,flag)])
(nanopass-case (L7 Expr) e-bool
[(quote ,d)
((if d build-set-port-flags! build-reset-port-flags!) e-p e-flags)]
[else
(bind #t (e-p)
`(if ,e-bool
,(build-set-port-flags! e-p e-flags)
,(build-reset-port-flags! e-p e-flags)))])))
(define-inline 3 set-port-bol!
[(e-p e-bool) (go e-p e-bool (constant port-flag-bol))])
(define-inline 3 set-port-eof!
[(e-p e-bool) (go e-p e-bool (constant port-flag-eof))])))
(let ()
(define (build-port-input-size port-type e-p)
(bind #t (e-p)
(translate
(%inline -
,(%inline -
,(%mref ,e-p ,(constant port-ilast-disp))
,(%mref ,e-p ,(constant port-ibuffer-disp)))
(immediate
,(if (eq? port-type 'textual)
(constant string-data-disp)
(constant bytevector-data-disp))))
(if (eq? port-type 'textual) (constant string-char-offset) 0)
(constant fixnum-offset))))
(define-inline 3 textual-port-input-size
[(e-p) (build-port-input-size 'textual e-p)])
(define-inline 3 binary-port-input-size
[(e-p) (build-port-input-size 'binary e-p)]))
(let ()
(define (build-port-output-size port-type e-p)
(bind #t (e-p)
(translate
(%inline -
,(%inline -
,(%mref ,e-p ,(constant port-olast-disp))
,(%mref ,e-p ,(constant port-obuffer-disp)))
(immediate
,(if (eq? port-type 'textual)
(constant string-data-disp)
(constant bytevector-data-disp))))
(if (eq? port-type 'textual) (constant string-char-offset) 0)
(constant fixnum-offset))))
(define-inline 3 textual-port-output-size
[(e-p) (build-port-output-size 'textual e-p)])
(define-inline 3 binary-port-output-size
[(e-p) (build-port-output-size 'binary e-p)]))
(let ()
(define (build-port-input-index port-type e-p)
(bind #t (e-p)
(translate
; TODO: use lea2?
(%inline +
,(%inline -
,(%inline -
,(%mref ,e-p ,(constant port-ilast-disp))
,(%mref ,e-p ,(constant port-ibuffer-disp)))
(immediate
,(if (eq? port-type 'textual)
(constant string-data-disp)
(constant bytevector-data-disp))))
,(%mref ,e-p ,(constant port-icount-disp)))
(if (eq? port-type 'textual) (constant string-char-offset) 0)
(constant fixnum-offset))))
(define-inline 3 textual-port-input-index
[(e-p) (build-port-input-index 'textual e-p)])
(define-inline 3 binary-port-input-index
[(e-p) (build-port-input-index 'binary e-p)]))
(let ()
(define (build-port-output-index port-type e-p)
(bind #t (e-p)
(translate
(%inline +
,(%inline -
,(%inline -
,(%mref ,e-p ,(constant port-olast-disp))
,(%mref ,e-p ,(constant port-obuffer-disp)))
(immediate
,(if (eq? port-type 'textual)
(constant string-data-disp)
(constant bytevector-data-disp))))
,(%mref ,e-p ,(constant port-ocount-disp)))
(if (eq? port-type 'textual) (constant string-char-offset) 0)
(constant fixnum-offset))))
(define-inline 3 textual-port-output-index
[(e-p) (build-port-output-index 'textual e-p)])
(define-inline 3 binary-port-output-index
[(e-p) (build-port-output-index 'binary e-p)]))
(let ()
(define (build-port-input-count port-type e-p)
(bind #t (e-p)
(translate
(%inline -
(immediate 0)
,(%mref ,e-p ,(constant port-icount-disp)))
(if (eq? port-type 'textual) (constant string-char-offset) 0)
(constant fixnum-offset))))
(define-inline 3 textual-port-input-count
[(e-p) (build-port-input-count 'textual e-p)])
(define-inline 3 binary-port-input-count
[(e-p) (build-port-input-count 'binary e-p)]))
(let ()
(define (build-port-output-count port-type e-p)
(bind #t (e-p)
(translate
(%inline -
(immediate 0)
,(%mref ,e-p ,(constant port-ocount-disp)))
(if (eq? port-type 'textual) (constant string-char-offset) 0)
(constant fixnum-offset))))
(define-inline 3 textual-port-output-count
[(e-p) (build-port-output-count 'textual e-p)])
(define-inline 3 binary-port-output-count
[(e-p) (build-port-output-count 'binary e-p)]))
(let ()
(define (build-set-port-input-size! port-type e-p e-x)
; actually, set last to buffer[0] + size; count to size
(bind #t (e-p)
(bind #t ([e-x (translate e-x
(constant fixnum-offset)
(if (eq? port-type 'textual) (constant string-char-offset) 0))])
`(seq
(set! ,(%mref ,e-p ,(constant port-icount-disp))
,(%inline - (immediate 0) ,e-x))
(set! ,(%mref ,e-p ,(constant port-ilast-disp))
,(%inline +
,(%inline +
,(%mref ,e-p ,(constant port-ibuffer-disp))
(immediate
,(if (eq? port-type 'textual)
(constant string-data-disp)
(constant bytevector-data-disp))))
,e-x))))))
(define-inline 3 set-textual-port-input-size!
[(e-p e-x) (build-set-port-input-size! 'textual e-p e-x)])
(define-inline 3 set-binary-port-input-size!
[(e-p e-x) (build-set-port-input-size! 'binary e-p e-x)]))
(let ()
(define (build-set-port-output-size! port-type e-p e-x)
; actually, set last to buffer[0] + size; count to size
(bind #t (e-p)
(bind #t ([e-x (translate e-x
(constant fixnum-offset)
(if (eq? port-type 'textual) (constant string-char-offset) 0))])
`(seq
(set! ,(%mref ,e-p ,(constant port-ocount-disp))
,(%inline - (immediate 0) ,e-x))
(set! ,(%mref ,e-p ,(constant port-olast-disp))
,(%inline +
,(%inline +
,(%mref ,e-p ,(constant port-obuffer-disp))
(immediate
,(if (eq? port-type 'textual)
(constant string-data-disp)
(constant bytevector-data-disp))))
,e-x))))))
(define-inline 3 set-textual-port-output-size!
[(e-p e-x) (build-set-port-output-size! 'textual e-p e-x)])
(define-inline 3 set-binary-port-output-size!
[(e-p e-x) (build-set-port-output-size! 'binary e-p e-x)]))
(let ()
(define (build-set-port-input-index! port-type e-p e-x)
; actually, set count to index - size, where size = last - buffer[0]
(bind #t (e-p)
`(set! ,(%mref ,e-p ,(constant port-icount-disp))
,(%inline -
,(translate e-x
(constant fixnum-offset)
(if (eq? port-type 'textual) (constant string-char-offset) 0))
,(%inline -
,(%mref ,e-p ,(constant port-ilast-disp))
,(%inline +
,(%mref ,e-p ,(constant port-ibuffer-disp))
(immediate
,(if (eq? port-type 'textual)
(constant string-data-disp)
(constant bytevector-data-disp)))))))))
(define-inline 3 set-textual-port-input-index!
[(e-p e-x) (build-set-port-input-index! 'textual e-p e-x)])
(define-inline 3 set-binary-port-input-index!
[(e-p e-x) (build-set-port-input-index! 'binary e-p e-x)]))
(let ()
(define (build-set-port-output-index! port-type e-p e-x)
; actually, set count to index - size, where size = last - buffer[0]
(bind #t (e-p)
`(set! ,(%mref ,e-p ,(constant port-ocount-disp))
,(%inline -
,(translate e-x
(constant fixnum-offset)
(if (eq? port-type 'textual) (constant string-char-offset) 0))
,(%inline -
,(%mref ,e-p ,(constant port-olast-disp))
,(%inline +
,(%mref ,e-p ,(constant port-obuffer-disp))
(immediate
,(if (eq? port-type 'textual)
(constant string-data-disp)
(constant bytevector-data-disp)))))))))
(define-inline 3 set-textual-port-output-index!
[(e-p e-x) (build-set-port-output-index! 'textual e-p e-x)])
(define-inline 3 set-binary-port-output-index!
[(e-p e-x) (build-set-port-output-index! 'binary e-p e-x)]))
(let ()
(define (make-build-set-port-buffer! port-type ibuffer-disp icount-disp ilast-disp)
(lambda (e-p e-b new?)
(bind #t (e-p e-b)
`(seq
,(if new?
`(set! ,(%mref ,e-p ,ibuffer-disp) ,e-b)
(build-dirty-store e-p ibuffer-disp e-b))
,(bind #t ([e-length (if (eq? port-type 'textual)
(translate
(%inline logand
,(%mref ,e-b ,(constant string-type-disp))
(immediate ,(fx- (expt 2 (constant string-length-offset)))))
(constant string-length-offset)
(constant string-char-offset))
(%inline srl
,(%mref ,e-b ,(constant bytevector-type-disp))
,(%constant bytevector-length-offset)))])
`(seq
(set! ,(%mref ,e-p ,icount-disp)
,(%inline - (immediate 0) ,e-length))
(set! ,(%mref ,e-p ,ilast-disp)
,(%lea ,e-b ,e-length
(if (eq? port-type 'textual)
(constant string-data-disp)
(constant bytevector-data-disp))))))))))
(define (make-port e-name e-handler e-ib e-ob e-info flags set-ibuf! set-obuf!)
(bind #f (e-name e-handler e-info e-ib e-ob)
(bind #t ([e-p (%constant-alloc type-typed-object (constant size-port))])
(%seq
(set! ,(%mref ,e-p ,(constant port-type-disp)) (immediate ,flags))
(set! ,(%mref ,e-p ,(constant port-handler-disp)) ,e-handler)
(set! ,(%mref ,e-p ,(constant port-name-disp)) ,e-name)
(set! ,(%mref ,e-p ,(constant port-info-disp)) ,e-info)
,(set-ibuf! e-p e-ib #t)
,(set-obuf! e-p e-ob #t)
,e-p))))
(define (make-build-clear-count count-disp)
(lambda (e-p e-b new?)
`(set! ,(%mref ,e-p ,count-disp) (immediate 0))))
(let ()
(define build-set-textual-port-input-buffer!
(make-build-set-port-buffer! 'textual
(constant port-ibuffer-disp)
(constant port-icount-disp)
(constant port-ilast-disp)))
(define build-set-textual-port-output-buffer!
(make-build-set-port-buffer! 'textual
(constant port-obuffer-disp)
(constant port-ocount-disp)
(constant port-olast-disp)))
(define-inline 3 set-textual-port-input-buffer!
[(e-p e-b) (build-set-textual-port-input-buffer! e-p e-b #f)])
(define-inline 3 set-textual-port-output-buffer!
[(e-p e-b) (build-set-textual-port-output-buffer! e-p e-b #f)])
(let ()
(define (go e-name e-handler e-ib e-info)
(make-port e-name e-handler e-ib `(quote "") e-info
(fxlogor (constant type-input-port) (constant PORT-FLAG-INPUT-MODE))
build-set-textual-port-input-buffer!
(make-build-clear-count (constant port-ocount-disp))))
(define-inline 3 $make-textual-input-port
[(e-name e-handler e-ib) (go e-name e-handler e-ib `(quote #f))]
[(e-name e-handler e-ib e-info) (go e-name e-handler e-ib e-info)]))
(let ()
(define (go e-name e-handler e-ob e-info)
(make-port e-name e-handler `(quote "") e-ob e-info
(constant type-output-port)
(make-build-clear-count (constant port-icount-disp))
build-set-textual-port-output-buffer!))
(define-inline 3 $make-textual-output-port
[(e-name e-handler e-ob) (go e-name e-handler e-ob `(quote #f))]
[(e-name e-handler e-ob e-info) (go e-name e-handler e-ob e-info)]))
(let ()
(define (go e-name e-handler e-ib e-ob e-info)
(make-port e-name e-handler e-ib e-ob e-info
(constant type-io-port)
build-set-textual-port-input-buffer!
build-set-textual-port-output-buffer!))
(define-inline 3 $make-textual-input/output-port
[(e-name e-handler e-ib e-ob) (go e-name e-handler e-ib e-ob `(quote #f))]
[(e-name e-handler e-ib e-ob e-info) (go e-name e-handler e-ib e-ob e-info)])))
(let ()
(define build-set-binary-port-input-buffer!
(make-build-set-port-buffer! 'binary
(constant port-ibuffer-disp)
(constant port-icount-disp)
(constant port-ilast-disp)))
(define build-set-binary-port-output-buffer!
(make-build-set-port-buffer! 'binary
(constant port-obuffer-disp)
(constant port-ocount-disp)
(constant port-olast-disp)))
(define-inline 3 set-binary-port-input-buffer!
[(e-p e-b) (build-set-binary-port-input-buffer! e-p e-b #f)])
(define-inline 3 set-binary-port-output-buffer!
[(e-p e-b) (build-set-binary-port-output-buffer! e-p e-b #f)])
(let ()
(define (go e-name e-handler e-ib e-info)
(make-port e-name e-handler e-ib `(quote #vu8()) e-info
(fxlogor (constant type-input-port) (constant PORT-FLAG-INPUT-MODE) (constant PORT-FLAG-BINARY))
build-set-binary-port-input-buffer!
(make-build-clear-count (constant port-ocount-disp))))
(define-inline 3 $make-binary-input-port
[(e-name e-handler e-ib) (go e-name e-handler e-ib `(quote #f))]
[(e-name e-handler e-ib e-info) (go e-name e-handler e-ib e-info)]))
(let ()
(define (go e-name e-handler e-ob e-info)
(make-port e-name e-handler `(quote #vu8()) e-ob e-info
(fxlogor (constant type-output-port) (constant PORT-FLAG-BINARY))
(make-build-clear-count (constant port-icount-disp))
build-set-binary-port-output-buffer!))
(define-inline 3 $make-binary-output-port
[(e-name e-handler e-ob) (go e-name e-handler e-ob `(quote #f))]
[(e-name e-handler e-ob e-info) (go e-name e-handler e-ob e-info)]))
(let ()
(define (go e-name e-handler e-ib e-ob e-info)
(make-port e-name e-handler e-ib e-ob e-info
(fxlogor (constant type-io-port) (constant PORT-FLAG-BINARY))
build-set-binary-port-input-buffer!
build-set-binary-port-output-buffer!))
(define-inline 3 $make-binary-input/output-port
[(e-name e-handler e-ib e-ob) (go e-name e-handler e-ib e-ob `(quote #f))]
[(e-name e-handler e-ib e-ob e-info) (go e-name e-handler e-ib e-ob e-info)]))))
(let ()
(define build-fxvector-ref-check (build-ref-check fxvector-type-disp maximum-fxvector-length fxvector-length-offset type-fxvector mask-fxvector fxvector-immutable-flag))
(define build-fxvector-set!-check (build-ref-check fxvector-type-disp maximum-fxvector-length fxvector-length-offset type-mutable-fxvector mask-mutable-fxvector fxvector-immutable-flag))
(define-inline 2 $fxvector-ref-check?
[(e-fv e-i) (bind #t (e-fv e-i) (build-fxvector-ref-check e-fv e-i #f))])
(define-inline 2 $fxvector-set!-check?
[(e-fv e-i) (bind #t (e-fv e-i) (build-fxvector-set!-check e-fv e-i #f))])
(let ()
(define (go e-fv e-i)
(cond
[(expr->index e-i 1 (constant maximum-fxvector-length)) =>
(lambda (index)
(%mref ,e-fv
,(+ (fix index) (constant fxvector-data-disp))))]
[else (%mref ,e-fv ,e-i ,(constant fxvector-data-disp))]))
(define-inline 3 fxvector-ref
[(e-fv e-i) (go e-fv e-i)])
(define-inline 2 fxvector-ref
[(e-fv e-i)
(bind #t (e-fv e-i)
`(if ,(build-fxvector-ref-check e-fv e-i #f)
,(go e-fv e-i)
,(build-libcall #t src sexpr fxvector-ref e-fv e-i)))]))
(let ()
(define (go e-fv e-i e-new)
`(set!
,(cond
[(expr->index e-i 1 (constant maximum-fxvector-length)) =>
(lambda (index)
(%mref ,e-fv
,(+ (fix index) (constant fxvector-data-disp))))]
[else (%mref ,e-fv ,e-i ,(constant fxvector-data-disp))])
,e-new))
(define-inline 3 fxvector-set!
[(e-fv e-i e-new)
(go e-fv e-i e-new)])
(define-inline 2 fxvector-set!
[(e-fv e-i e-new)
(bind #t (e-fv e-i e-new)
`(if ,(build-fxvector-set!-check e-fv e-i e-new)
,(go e-fv e-i e-new)
,(build-libcall #t src sexpr fxvector-set! e-fv e-i e-new)))])
(define-inline 3 $fxvector-set-immutable!
[(e-fv) ((build-set-immutable! fxvector-type-disp fxvector-immutable-flag) e-fv)])))
(let ()
(define build-string-ref-check
(lambda (e-s e-i)
((build-ref-check string-type-disp maximum-string-length string-length-offset type-string mask-string string-immutable-flag) e-s e-i #f)))
(define build-string-set!-check
(lambda (e-s e-i)
((build-ref-check string-type-disp maximum-string-length string-length-offset type-mutable-string mask-mutable-string string-immutable-flag) e-s e-i #f)))
(define-inline 2 $string-ref-check?
[(e-s e-i) (bind #t (e-s e-i) (build-string-ref-check e-s e-i))])
(define-inline 2 $string-set!-check?
[(e-s e-i) (bind #t (e-s e-i) (build-string-set!-check e-s e-i))])
(let ()
(define (go e-s e-i)
(cond
[(expr->index e-i 1 (constant maximum-string-length)) =>
(lambda (index)
`(inline ,(make-info-load (string-char-type) #f) ,%load ,e-s ,%zero
(immediate ,(+ (* (constant string-char-bytes) index) (constant string-data-disp)))))]
[else
`(inline ,(make-info-load (string-char-type) #f) ,%load ,e-s
,(translate e-i
(constant fixnum-offset)
(constant string-char-offset))
,(%constant string-data-disp))]))
(define-inline 3 string-ref
[(e-s e-i) (go e-s e-i)])
(define-inline 2 string-ref
[(e-s e-i)
(bind #t (e-s e-i)
`(if ,(build-string-ref-check e-s e-i)
,(go e-s e-i)
,(build-libcall #t src sexpr string-ref e-s e-i)))]))
(let ()
(define (go e-s e-i e-new)
(cond
[(expr->index e-i 1 (constant maximum-string-length)) =>
(lambda (index)
`(inline ,(make-info-load (string-char-type) #f) ,%store ,e-s ,%zero
(immediate ,(+ (* (constant string-char-bytes) index) (constant string-data-disp)))
,e-new))]
[else
`(inline ,(make-info-load (string-char-type) #f) ,%store ,e-s
,(translate e-i
(constant fixnum-offset)
(constant string-char-offset))
,(%constant string-data-disp)
,e-new)]))
(define-inline 3 string-set!
[(e-s e-i e-new) (go e-s e-i e-new)])
(define-inline 2 string-set!
[(e-s e-i e-new)
(bind #t (e-s e-i e-new)
`(if ,(let ([e-ref-check (build-string-set!-check e-s e-i)])
(if (constant? char? e-new)
e-ref-check
(build-and e-ref-check (%type-check mask-char type-char ,e-new))))
,(go e-s e-i e-new)
,(build-libcall #t src sexpr string-set! e-s e-i e-new)))])
(define-inline 3 $string-set-immutable!
[(e-s) ((build-set-immutable! string-type-disp string-immutable-flag) e-s)])))
(let ()
(define build-vector-ref-check (build-ref-check vector-type-disp maximum-vector-length vector-length-offset type-vector mask-vector vector-immutable-flag))
(define build-vector-set!-check (build-ref-check vector-type-disp maximum-vector-length vector-length-offset type-mutable-vector mask-mutable-vector vector-immutable-flag))
(define-inline 2 $vector-ref-check?
[(e-v e-i) (bind #t (e-v e-i) (build-vector-ref-check e-v e-i #f))])
(define-inline 2 $vector-set!-check?
[(e-v e-i) (bind #t (e-v e-i) (build-vector-set!-check e-v e-i #f))])
(let ()
(define (go e-v e-i)
(nanopass-case (L7 Expr) e-i
[(quote ,d)
(guard (target-fixnum? d))
(%mref ,e-v ,(+ (fix d) (constant vector-data-disp)))]
[else (%mref ,e-v ,e-i ,(constant vector-data-disp))]))
(define-inline 3 vector-ref
[(e-v e-i) (go e-v e-i)])
(define-inline 2 vector-ref
[(e-v e-i)
(bind #t (e-v e-i)
`(if ,(build-vector-ref-check e-v e-i #f)
,(go e-v e-i)
,(build-libcall #t src sexpr vector-ref e-v e-i)))]))
(let ()
(define (go e-v e-i e-new)
(nanopass-case (L7 Expr) e-i
[(quote ,d)
(guard (target-fixnum? d))
(build-dirty-store e-v (+ (fix d) (constant vector-data-disp)) e-new)]
[else (build-dirty-store e-v e-i (constant vector-data-disp) e-new)]))
(define-inline 3 vector-set!
[(e-v e-i e-new) (go e-v e-i e-new)])
(define-inline 2 vector-set!
[(e-v e-i e-new)
(bind #t (e-v e-i e-new)
`(if ,(build-vector-set!-check e-v e-i #f)
,(go e-v e-i e-new)
,(build-libcall #t src sexpr vector-set! e-v e-i e-new)))])
(define-inline 3 $vector-set-immutable!
[(e-fv) ((build-set-immutable! vector-type-disp vector-immutable-flag) e-fv)]))
(let ()
(define (go e-v e-i e-old e-new)
(nanopass-case (L7 Expr) e-i
[(quote ,d)
(guard (target-fixnum? d))
(build-dirty-store e-v %zero (+ (fix d) (constant vector-data-disp)) e-new (make-build-cas e-old) build-cas-seq)]
[else (build-dirty-store e-v e-i (constant vector-data-disp) e-new (make-build-cas e-old) build-cas-seq)]))
(define-inline 3 vector-cas!
[(e-v e-i e-old e-new) (go e-v e-i e-old e-new)])
(define-inline 2 vector-cas!
[(e-v e-i e-old e-new)
(bind #t (e-v e-i e-old e-new)
`(if ,(build-vector-set!-check e-v e-i #f)
,(go e-v e-i e-old e-new)
,(build-libcall #t src sexpr vector-cas! e-v e-i e-old e-new)))]))
(let ()
(define (go e-v e-i e-new)
`(set!
,(nanopass-case (L7 Expr) e-i
[(quote ,d)
(guard (target-fixnum? d))
(%mref ,e-v ,(+ (fix d) (constant vector-data-disp)))]
[else (%mref ,e-v ,e-i ,(constant vector-data-disp))])
,e-new))
(define-inline 3 vector-set-fixnum!
[(e-v e-i e-new) (go e-v e-i e-new)])
(define-inline 2 vector-set-fixnum!
[(e-v e-i e-new)
(bind #t (e-v e-i e-new)
`(if ,(build-vector-set!-check e-v e-i e-new)
,(go e-v e-i e-new)
,(build-libcall #t src sexpr vector-set-fixnum! e-v e-i e-new)))])))
(let ()
(define build-bytevector-ref-check
(lambda (e-bits e-bv e-i check-mutable?)
(nanopass-case (L7 Expr) e-bits
[(quote ,d)
(guard (and (fixnum? d) (fx> d 0) (fx= (* (fxquotient d 8) 8) d)))
(let ([bits d] [bytes (fxquotient d 8)])
(bind #t (e-bv e-i)
(build-and
(%type-check mask-typed-object type-typed-object ,e-bv)
(bind #t ([t (%mref ,e-bv ,(constant bytevector-type-disp))])
(build-and
(if check-mutable?
(%type-check mask-mutable-bytevector type-mutable-bytevector ,t)
(%type-check mask-bytevector type-bytevector ,t))
(cond
[(expr->index e-i bytes (constant maximum-bytevector-length)) =>
(lambda (index)
(%inline u<
(immediate ,(logor (ash (+ index (fx- bytes 1)) (constant bytevector-length-offset))
(constant type-bytevector) (constant bytevector-immutable-flag)))
,t))]
[else
(build-and
($type-check (fxlogor (fix (fx- bytes 1)) (constant mask-fixnum)) (constant type-fixnum) e-i)
(%inline u<
; NB. add cannot overflow or change negative to positive when
; low-order (log2 bytes) bits of fixnum value are zero, as
; guaranteed by type-check above
,(if (fx= bytes 1)
e-i
(%inline + ,e-i (immediate ,(fix (fx- bytes 1)))))
,(%inline logand
,(translate t
(constant bytevector-length-offset)
(constant fixnum-offset))
(immediate ,(- (constant fixnum-factor))))))]))))))]
[(seq (profile ,src) ,[e]) (and e `(seq (profile ,src) ,e))]
[else #f])))
(define-inline 2 $bytevector-ref-check?
[(e-bits e-bv e-i) (build-bytevector-ref-check e-bits e-bv e-i #f)])
(define-inline 2 $bytevector-set!-check?
[(e-bits e-bv e-i) (build-bytevector-ref-check e-bits e-bv e-i #t)]))
(let ()
(define build-bytevector-fill
(let ([filler (make-build-fill 1 (constant bytevector-data-disp))])
(lambda (e-bv e-bytes e-fill)
(bind #t uptr ([e-fill (build-unfix e-fill)])
(filler e-bv e-bytes e-fill)))))
(let ()
(define do-make-bytevector
(lambda (e-length maybe-e-fill)
; NB: caller must bind maybe-e-fill
(safe-assert (or (not maybe-e-fill) (no-need-to-bind? #f maybe-e-fill)))
(if (constant? (lambda (x) (and (fixnum? x) (fx<= 0 x 10000))) e-length)
(let ([n (constant-value e-length)])
(if (fx= n 0)
`(quote ,(bytevector))
(bind #t ([t (%constant-alloc type-typed-object
(fx+ (constant header-size-bytevector) n))])
`(seq
(set! ,(%mref ,t ,(constant bytevector-type-disp))
(immediate ,(fx+ (fx* n (constant bytevector-length-factor))
(constant type-bytevector))))
,(if maybe-e-fill
(build-bytevector-fill t `(immediate ,n) maybe-e-fill)
t)))))
(bind #t (e-length)
(let ([t-bytes (make-tmp 'tbytes 'uptr)] [t-vec (make-tmp 'tvec)])
`(if ,(%inline eq? ,e-length (immediate 0))
(quote ,(bytevector))
(let ([,t-bytes ,(build-unfix e-length)])
(let ([,t-vec (alloc ,(make-info-alloc (constant type-typed-object) #f #f)
,(%inline logand
,(%inline + ,t-bytes
(immediate ,(fx+ (constant header-size-bytevector)
(fx- (constant byte-alignment) 1))))
(immediate ,(- (constant byte-alignment)))))])
(seq
(set! ,(%mref ,t-vec ,(constant bytevector-type-disp))
,(build-type/length t-bytes
(constant type-bytevector)
0
(constant bytevector-length-offset)))
,(if maybe-e-fill
(build-bytevector-fill t-vec t-bytes maybe-e-fill)
t-vec))))))))))
(let ()
(define valid-length?
(lambda (e-length)
(constant?
(lambda (x)
(and (or (fixnum? x) (bignum? x))
(<= 0 x (constant maximum-bytevector-length))))
e-length)))
(define-inline 2 make-bytevector
[(e-length) (and (valid-length? e-length) (do-make-bytevector e-length #f))]
[(e-length e-fill)
(and (valid-length? e-length)
(constant? (lambda (x) (and (fixnum? x) (fx<= -128 x 255))) e-fill)
(do-make-bytevector e-length e-fill))]))
(define-inline 3 make-bytevector
[(e-length) (do-make-bytevector e-length #f)]
[(e-length e-fill) (bind #f (e-fill) (do-make-bytevector e-length e-fill))]))
(define-inline 3 bytevector-fill!
[(e-bv e-fill)
(bind #t (e-bv e-fill)
`(seq
,(build-bytevector-fill e-bv
(%inline srl
,(%mref ,e-bv ,(constant bytevector-type-disp))
,(%constant bytevector-length-offset))
e-fill)
,(%constant svoid)))]))
(let ()
(define build-bytevector
(lambda (e*)
(define (find-k n)
(let loop ([bytes (constant-case ptr-bits [(32) 4] [(64) 8])]
[type* (constant-case ptr-bits
[(32) '(unsigned-32 unsigned-16 unsigned-8)]
[(64) '(unsigned-64 unsigned-32 unsigned-16 unsigned-8)])])
(let ([bytes/2 (fxsrl bytes 1)])
(if (fx<= n bytes/2)
(loop bytes/2 (cdr type*))
(values bytes (car type*))))))
(define (build-chunk k n e*)
(define (build-shift e shift)
(if (fx= shift 0) e (%inline sll ,e (immediate ,shift))))
(let loop ([k (constant-case native-endianness
[(little) (fxmin k n)]
[(big) k])]
[e* (constant-case native-endianness
[(little) (reverse (if (fx<= n k) e* (list-head e* k)))]
[(big) e*])]
[constant-part 0]
[expression-part #f]
[expression-shift 0]
[mask? #f]) ; no need to mask the high-order byte
(if (fx= k 0)
(if expression-part
(let ([expression-part (build-shift expression-part expression-shift)])
(if (= constant-part 0)
expression-part
(%inline logor ,expression-part (immediate ,constant-part))))
`(immediate ,constant-part))
(let ([k (fx- k 1)]
[constant-part (ash constant-part 8)]
[expression-shift (fx+ expression-shift 8)])
(if (null? e*)
(loop k e* constant-part expression-part expression-shift #t)
(let ([e (car e*)] [e* (cdr e*)])
(if (fixnum-constant? e)
(loop k e* (logor constant-part (logand (constant-value e) #xff)) expression-part expression-shift #t)
(loop k e* constant-part
(let* ([e (build-unfix e)]
[e (if mask? (%inline logand ,e (immediate #xff)) e)])
(if expression-part
(%inline logor ,(build-shift expression-part expression-shift) ,e)
e))
0 #t))))))))
(let ([len (length e*)])
(if (fx= len 0)
`(quote ,(bytevector))
(list-bind #f (e*)
(bind #t ([t (%constant-alloc type-typed-object
(fx+ (constant header-size-bytevector) len))])
`(seq
(set! ,(%mref ,t ,(constant bytevector-type-disp))
(immediate ,(+ (* len (constant bytevector-length-factor))
(constant type-bytevector))))
; build and store k-octet (k = 4 on 32-bit machines, k = 8 on 64-bit
; machines) chunks, taking endianness into account. for the last
; chunk, set k = 1, 2, 4, or 8 depending on the number of octets
; remaining, padding with zeros as necessary.
,(let f ([e* e*] [n (length e*)] [offset (constant bytevector-data-disp)])
(let-values ([(k type) (find-k n)])
`(seq
(inline ,(make-info-load type #f) ,%store ,t ,%zero (immediate ,offset)
,(build-chunk k n e*))
,(if (fx<= n k)
t
(f (list-tail e* k) (fx- n k) (fx+ offset k)))))))))))))
(define-inline 2 bytevector
[e* (and (andmap
(lambda (x)
(constant?
(lambda (x) (and (fixnum? x) (fx<= -128 x 255)))
x))
e*)
(build-bytevector e*))])
(define-inline 3 bytevector
[e* (build-bytevector e*)]))
(let ()
(define byte-offset
(lambda (off)
(cond
[(nanopass-case (L7 Expr) off
[(quote ,d)
(and (and (integer? d) (exact? d))
(let ([n (+ d (constant bytevector-data-disp))])
(and (target-fixnum? n)
`(quote ,n))))]
[else #f])]
[else (%inline + ,off
(quote ,(constant bytevector-data-disp)))])))
(define-inline 3 bytevector-copy!
[(bv1 off1 bv2 off2 n)
(%primcall src sexpr $byte-copy! ,bv1 ,(byte-offset off1) ,bv2 ,(byte-offset off2) ,n)]))
(define-inline 3 bytevector-truncate!
[(bv len)
(if (fixnum-constant? len)
(let ([len (constant-value len)])
(if (fx= len 0)
`(quote ,(bytevector))
(bind #t (bv)
`(seq
(set! ,(%mref ,bv ,(constant bytevector-type-disp))
(immediate ,(fx+ (fx* len (constant bytevector-length-factor))
(constant type-bytevector))))
,bv))))
(bind #t (bv len)
`(if ,(%inline eq? ,len (immediate 0))
(quote ,(bytevector))
(seq
(set! ,(%mref ,bv ,(constant bytevector-type-disp))
,(build-type/length len
(constant type-bytevector)
(constant fixnum-offset)
(constant bytevector-length-offset)))
,bv))))])
(define-inline 3 $bytevector-set-immutable!
[(bv) ((build-set-immutable! bytevector-type-disp bytevector-immutable-flag) bv)])
(let ()
(define bv-index-offset
(lambda (offset-expr)
(if (fixnum-constant? offset-expr)
(values %zero (+ (constant bytevector-data-disp) (constant-value offset-expr)))
(values (build-unfix offset-expr) (constant bytevector-data-disp)))))
(define bv-offset-okay?
(lambda (x mask)
(constant? (lambda (x) (and (target-fixnum? x) (>= x 0) (eq? (logand x mask) 0))) x)))
(let ()
(define-syntax define-bv-8-inline
(syntax-rules ()
[(_ name type)
(define-inline 2 name
[(e-bv e-offset)
(bind #t (e-bv e-offset)
`(if ,(handle-prim #f #f 3 '$bytevector-ref-check? (list `(quote 8) e-bv e-offset))
,(let-values ([(e-index imm-offset) (bv-index-offset e-offset)])
(build-object-ref #f 'type e-bv e-index imm-offset))
,(build-libcall #t src sexpr name e-bv e-offset)))])]))
(define-bv-8-inline bytevector-s8-ref integer-8)
(define-bv-8-inline bytevector-u8-ref unsigned-8))
(let ()
(define-syntax define-bv-native-ref-inline
(lambda (x)
(syntax-case x ()
[(_ name type)
#'(define-inline 3 name
[(e-bv e-offset)
(let-values ([(e-index imm-offset) (bv-index-offset e-offset)])
(build-object-ref #f 'type e-bv e-index imm-offset))])])))
(define-bv-native-ref-inline bytevector-s8-ref integer-8)
(define-bv-native-ref-inline bytevector-u8-ref unsigned-8)
(define-bv-native-ref-inline bytevector-s16-native-ref integer-16)
(define-bv-native-ref-inline bytevector-u16-native-ref unsigned-16)
(define-bv-native-ref-inline bytevector-s32-native-ref integer-32)
(define-bv-native-ref-inline bytevector-u32-native-ref unsigned-32)
(define-bv-native-ref-inline bytevector-s64-native-ref integer-64)
(define-bv-native-ref-inline bytevector-u64-native-ref unsigned-64)
(define-bv-native-ref-inline bytevector-ieee-single-native-ref single-float)
(define-bv-native-ref-inline bytevector-ieee-double-native-ref double-float))
(let ()
(define-syntax define-bv-native-int-set!-inline
(lambda (x)
(syntax-case x ()
[(_ check-64? name type)
(with-syntax ([body #'(let-values ([(e-index imm-offset) (bv-index-offset e-offset)])
(build-object-set! 'type e-bv e-index imm-offset e-val))])
(with-syntax ([body (if (datum check-64?)
#'(and (>= (constant ptr-bits) 64) body)
#'body)])
#'(define-inline 3 name
[(e-bv e-offset e-val) body])))])))
(define-bv-native-int-set!-inline #f bytevector-s8-set! integer-8)
(define-bv-native-int-set!-inline #f bytevector-u8-set! unsigned-8)
(define-bv-native-int-set!-inline #f $bytevector-set! unsigned-8)
(define-bv-native-int-set!-inline #f bytevector-s16-native-set! integer-16)
(define-bv-native-int-set!-inline #f bytevector-u16-native-set! unsigned-16)
(define-bv-native-int-set!-inline #f bytevector-s32-native-set! integer-32)
(define-bv-native-int-set!-inline #f bytevector-u32-native-set! unsigned-32)
(define-bv-native-int-set!-inline #t bytevector-s64-native-set! integer-64)
(define-bv-native-int-set!-inline #t bytevector-u64-native-set! unsigned-64))
(let ()
(define-syntax define-bv-native-ieee-set!-inline
(lambda (x)
(syntax-case x ()
[(_ name type)
#'(define-inline 3 name
[(e-bv e-offset e-val)
(let-values ([(e-index imm-offset) (bv-index-offset e-offset)])
(bind #f (e-bv e-index)
(build-object-set! 'type e-bv e-index imm-offset
(build-$real->flonum src sexpr e-val `(quote name)))))])])))
(define-bv-native-ieee-set!-inline bytevector-ieee-single-native-set! single-float)
(define-bv-native-ieee-set!-inline bytevector-ieee-double-native-set! double-float))
(let ()
(define-syntax define-bv-int-ref-inline
(lambda (x)
(define p2?
(lambda (n)
(let f ([i 1])
(or (fx= i n)
(and (not (fx> i n)) (f (fxsll i 1)))))))
(syntax-case x ()
[(_ name type mask)
#`(define-inline 3 name
[(e-bv e-offset e-eness)
(and (or (constant unaligned-integers)
(and #,(p2? (fx+ (datum mask) 1)) (bv-offset-okay? e-offset mask)))
(constant? (lambda (x) (memq x '(big little))) e-eness)
(let-values ([(e-index imm-offset) (bv-index-offset e-offset)])
(build-object-ref (not (eq? (constant-value e-eness) (constant native-endianness)))
'type e-bv e-index imm-offset)))])])))
(define-bv-int-ref-inline bytevector-s16-ref integer-16 1)
(define-bv-int-ref-inline bytevector-u16-ref unsigned-16 1)
(define-bv-int-ref-inline bytevector-s24-ref integer-24 1)
(define-bv-int-ref-inline bytevector-u24-ref unsigned-24 1)
(define-bv-int-ref-inline bytevector-s32-ref integer-32 3)
(define-bv-int-ref-inline bytevector-u32-ref unsigned-32 3)
(define-bv-int-ref-inline bytevector-s40-ref integer-40 3)
(define-bv-int-ref-inline bytevector-u40-ref unsigned-40 3)
(define-bv-int-ref-inline bytevector-s48-ref integer-48 3)
(define-bv-int-ref-inline bytevector-u48-ref unsigned-48 3)
(define-bv-int-ref-inline bytevector-s56-ref integer-56 7)
(define-bv-int-ref-inline bytevector-u56-ref unsigned-56 7)
(define-bv-int-ref-inline bytevector-s64-ref integer-64 7)
(define-bv-int-ref-inline bytevector-u64-ref unsigned-64 7))
(let ()
(define-syntax define-bv-ieee-ref-inline
(lambda (x)
(syntax-case x ()
[(_ name type mask)
#'(define-inline 3 name
[(e-bv e-offset e-eness)
(and (or (constant unaligned-floats)
(bv-offset-okay? e-offset mask))
(constant? (lambda (x) (eq? x (constant native-endianness))) e-eness)
(let-values ([(e-index imm-offset) (bv-index-offset e-offset)])
(build-object-ref #f 'type e-bv e-index imm-offset)))])])))
(define-bv-ieee-ref-inline bytevector-ieee-single-ref single-float 3)
(define-bv-ieee-ref-inline bytevector-ieee-double-ref double-float 7))
(let ()
(define-syntax define-bv-int-set!-inline
(lambda (x)
(syntax-case x ()
[(_ check-64? name type mask)
(with-syntax ([body #'(and (or (constant unaligned-integers)
(and mask (bv-offset-okay? e-offset mask)))
(constant? (lambda (x) (memq x '(big little))) e-eness)
(let-values ([(e-index imm-offset) (bv-index-offset e-offset)])
(if (eq? (constant-value e-eness) (constant native-endianness))
(build-object-set! 'type e-bv e-index imm-offset e-value)
(build-swap-object-set! 'type e-bv e-index imm-offset e-value))))])
(with-syntax ([body (if (datum check-64?)
#'(and (>= (constant ptr-bits) 64) body)
#'body)])
#'(define-inline 3 name
[(e-bv e-offset e-value e-eness) body])))])))
(define-bv-int-set!-inline #f bytevector-s16-set! integer-16 1)
(define-bv-int-set!-inline #f bytevector-u16-set! unsigned-16 1)
(define-bv-int-set!-inline #f bytevector-s24-set! integer-24 #f)
(define-bv-int-set!-inline #f bytevector-u24-set! unsigned-24 #f)
(define-bv-int-set!-inline #f bytevector-s32-set! integer-32 3)
(define-bv-int-set!-inline #f bytevector-u32-set! unsigned-32 3)
(define-bv-int-set!-inline #t bytevector-s40-set! integer-40 #f)
(define-bv-int-set!-inline #t bytevector-u40-set! unsigned-40 #f)
(define-bv-int-set!-inline #t bytevector-s48-set! integer-48 #f)
(define-bv-int-set!-inline #t bytevector-u48-set! unsigned-48 #f)
(define-bv-int-set!-inline #t bytevector-s56-set! integer-56 #f)
(define-bv-int-set!-inline #t bytevector-u56-set! unsigned-56 #f)
(define-bv-int-set!-inline #t bytevector-s64-set! integer-64 7)
(define-bv-int-set!-inline #t bytevector-u64-set! unsigned-64 7))
(let ()
(define-syntax define-bv-ieee-set!-inline
(lambda (x)
(syntax-case x ()
[(_ name type mask)
#'(define-inline 3 name
[(e-bv e-offset e-value e-eness)
(and (or (constant unaligned-floats) (bv-offset-okay? e-offset mask))
(constant? (lambda (x) (eq? x (constant native-endianness))) e-eness)
(let-values ([(e-index imm-offset) (bv-index-offset e-offset)])
(bind #f (e-bv e-index)
(build-object-set! 'type e-bv e-index imm-offset
(build-$real->flonum src sexpr e-value
`(quote name))))))])])))
(define-bv-ieee-set!-inline bytevector-ieee-single-set! single-float 3)
(define-bv-ieee-set!-inline bytevector-ieee-double-set! double-float 7))
(let ()
(define anyint-ref-helper
(lambda (type mask e-bv e-offset e-eness)
(and (or (constant unaligned-integers) (bv-offset-okay? e-offset mask))
(constant? (lambda (x) (memq x '(big little))) e-eness)
(let-values ([(e-index imm-offset) (bv-index-offset e-offset)])
(build-object-ref (not (eq? (constant-value e-eness) (constant native-endianness)))
type e-bv e-index imm-offset)))))
(define-syntax define-bv-anyint-ref-inline
(syntax-rules ()
[(_ name type8 type16 type32 type64)
(define-inline 3 name
[(e-bv e-offset e-eness e-size)
(and (fixnum-constant? e-size)
(case (constant-value e-size)
[(1) (let-values ([(e-index imm-offset) (bv-index-offset e-offset)])
`(seq
,e-eness
,(build-object-ref #f 'type8 e-bv e-index imm-offset)))]
[(2) (anyint-ref-helper 'type16 #b1 e-bv e-offset e-eness)]
[(4) (anyint-ref-helper 'type32 #b11 e-bv e-offset e-eness)]
[(8) (anyint-ref-helper 'type64 #b111 e-bv e-offset e-eness)]
[else #f]))])]))
(define-bv-anyint-ref-inline bytevector-sint-ref
integer-8 integer-16 integer-32 integer-64)
(define-bv-anyint-ref-inline bytevector-uint-ref
unsigned-8 unsigned-16 unsigned-32 unsigned-64))
(let ()
(define anyint-set!-helper
(lambda (type mask e-bv e-offset e-value e-eness)
(and (or (constant unaligned-integers) (bv-offset-okay? e-offset mask))
(constant? (lambda (x) (memq x '(big little))) e-eness)
(let-values ([(e-index imm-offset) (bv-index-offset e-offset)])
(if (eq? (constant-value e-eness) (constant native-endianness))
(build-object-set! type e-bv e-index imm-offset e-value)
(build-swap-object-set! type e-bv e-index imm-offset e-value))))))
(define-syntax define-bv-anyint-set!-inline
(syntax-rules ()
[(_ name type8 type16 type32 type64)
(define-inline 3 name
[(e-bv e-offset e-value e-eness e-size)
(and (fixnum-constant? e-size)
(case (constant-value e-size)
[(1) (let-values ([(e-index imm-offset) (bv-index-offset e-offset)])
`(seq
,e-eness
,(build-object-set! 'type8 e-bv e-index imm-offset e-value)))]
[(2) (anyint-set!-helper 'type16 1 e-bv e-offset e-value e-eness)]
[(4) (anyint-set!-helper 'type32 3 e-bv e-offset e-value e-eness)]
[(8) (and (>= (constant ptr-bits) 64)
(anyint-set!-helper 'type64 7 e-bv e-offset e-value e-eness))]
[else #f]))])]))
(define-bv-anyint-set!-inline bytevector-sint-set!
integer-8 integer-16 integer-32 integer-64)
(define-bv-anyint-set!-inline bytevector-uint-set!
unsigned-8 unsigned-16 unsigned-32 unsigned-64)))
(let ()
(define (byte-count e-n)
(or (nanopass-case (L7 Expr) e-n
[(quote ,d)
(and (and (integer? d) (exact? d))
(let ([n (* d (constant string-char-bytes))])
(and (target-fixnum? n)
`(immediate ,(fix n)))))]
[else #f])
(%inline sll ,e-n ,(%constant string-char-offset))))
(define byte-offset
(lambda (e-off)
(or (nanopass-case (L7 Expr) e-off
[(quote ,d)
(and (and (integer? d) (exact? d))
(let ([n (+ (* d (constant string-char-bytes))
(constant string-data-disp))])
(and (target-fixnum? n)
`(immediate ,(fix n)))))]
[else #f])
(%inline +
,(%inline sll ,e-off ,(%constant string-char-offset))
(immediate ,(fix (constant string-data-disp)))))))
(define-inline 3 string-copy!
[(e-bv1 e-off1 e-bv2 e-off2 e-n)
(%primcall src sexpr $byte-copy! ,e-bv1 ,(byte-offset e-off1) ,e-bv2 ,(byte-offset e-off2) ,(byte-count e-n))]))
(define-inline 3 string-truncate!
[(e-str e-len)
(if (fixnum-constant? e-len)
(let ([len (constant-value e-len)])
(if (fx= len 0)
`(quote ,(string))
(bind #t (e-str)
`(seq
(set! ,(%mref ,e-str ,(constant string-type-disp))
(immediate ,(fx+ (fx* len (constant string-length-factor))
(constant type-string))))
,e-str))))
(bind #t (e-str e-len)
`(if ,(%inline eq? ,e-len (immediate 0))
(quote ,(string))
(seq
(set! ,(%mref ,e-str ,(constant string-type-disp))
,(build-type/length e-len
(constant type-string)
(constant fixnum-offset)
(constant string-length-offset)))
,e-str))))])
(let ()
(define build-string-fill
(make-build-fill (constant string-char-bytes) (constant string-data-disp)))
(let ()
(define do-make-string
(lambda (e-length e-fill)
; NB: caller must bind e-fill
(safe-assert (no-need-to-bind? #f e-fill))
(if (constant? (lambda (x) (and (fixnum? x) (fx<= 0 x 10000))) e-length)
(let ([n (constant-value e-length)])
(if (fx= n 0)
`(quote ,(string))
(let ([bytes (fx* n (constant string-char-bytes))])
(bind #t ([t (%constant-alloc type-typed-object
(fx+ (constant header-size-string) bytes))])
`(seq
(set! ,(%mref ,t ,(constant string-type-disp))
(immediate ,(fx+ (fx* n (constant string-length-factor))
(constant type-string))))
,(build-string-fill t `(immediate ,bytes) e-fill))))))
(bind #t (e-length)
(let ([t-bytes (make-tmp 'tsize 'uptr)] [t-str (make-tmp 'tstr)])
`(if ,(%inline eq? ,e-length (immediate 0))
(quote ,(string))
(let ([,t-bytes ,(translate e-length
(constant fixnum-offset)
(constant string-char-offset))])
(let ([,t-str (alloc ,(make-info-alloc (constant type-typed-object) #f #f)
,(%inline logand
,(%inline + ,t-bytes
(immediate ,(fx+ (constant header-size-string)
(fx- (constant byte-alignment) 1))))
(immediate ,(- (constant byte-alignment)))))])
(seq
(set! ,(%mref ,t-str ,(constant string-type-disp))
,(build-type/length t-bytes
(constant type-string)
(constant string-char-offset)
(constant string-length-offset)))
,(build-string-fill t-str t-bytes e-fill))))))))))
(define default-fill `(immediate ,(ptr->imm #\nul)))
(define-inline 3 make-string
[(e-length) (do-make-string e-length default-fill)]
[(e-length e-fill) (bind #t (e-fill) (do-make-string e-length e-fill))])
(let ()
(define (valid-length? e-length)
(constant?
(lambda (x)
(and (or (fixnum? x) (bignum? x))
(<= 0 x (constant maximum-string-length))))
e-length))
(define-inline 2 make-string
[(e-length)
(and (valid-length? e-length)
(do-make-string e-length default-fill))]
[(e-length e-fill)
(and (valid-length? e-length)
(constant? char? e-fill)
(do-make-string e-length e-fill))])))
(define-inline 3 string-fill!
[(e-str e-fill)
`(seq
,(bind #t (e-str e-fill)
(build-string-fill e-str
(translate
(%inline logxor
,(%mref ,e-str ,(constant string-type-disp))
,(%constant type-string))
(constant string-length-offset)
(constant string-char-offset))
e-fill))
,(%constant svoid))]))
(let ()
(define build-fxvector-fill
(make-build-fill (constant ptr-bytes) (constant fxvector-data-disp)))
(meta-assert (= (constant log2-ptr-bytes) (constant fixnum-offset)))
(let ()
(define do-make-fxvector
(lambda (e-length e-fill)
; NB: caller must bind e-fill
(safe-assert (no-need-to-bind? #f e-fill))
(if (constant? (lambda (x) (and (fixnum? x) (fx<= 0 x 10000))) e-length)
(let ([n (constant-value e-length)])
(if (fx= n 0)
`(quote ,(fxvector))
(let ([bytes (fx* n (constant ptr-bytes))])
(bind #t ([t (%constant-alloc type-typed-object
(fx+ (constant header-size-fxvector) bytes))])
`(seq
(set! ,(%mref ,t ,(constant fxvector-type-disp))
(immediate ,(fx+ (fx* n (constant fxvector-length-factor))
(constant type-fxvector))))
,(build-fxvector-fill t `(immediate ,bytes) e-fill))))))
(bind #t (e-length) ; fixnum length doubles as byte count
(let ([t-fxv (make-tmp 'tfxv)])
`(if ,(%inline eq? ,e-length (immediate 0))
(quote ,(fxvector))
(let ([,t-fxv (alloc ,(make-info-alloc (constant type-typed-object) #f #f)
,(%inline logand
,(%inline + ,e-length
(immediate ,(fx+ (constant header-size-fxvector)
(fx- (constant byte-alignment) 1))))
(immediate ,(- (constant byte-alignment)))))])
(seq
(set! ,(%mref ,t-fxv ,(constant fxvector-type-disp))
,(build-type/length e-length
(constant type-fxvector)
(constant fixnum-offset)
(constant fxvector-length-offset)))
,(build-fxvector-fill t-fxv e-length e-fill)))))))))
(define default-fill `(immediate ,(fix 0)))
(define-inline 3 make-fxvector
[(e-length) (do-make-fxvector e-length default-fill)]
[(e-length e-fill) (bind #t (e-fill) (do-make-fxvector e-length e-fill))])
(let ()
(define (valid-length? e-length)
(constant?
(lambda (x)
(and (or (fixnum? x) (bignum? x))
(<= 0 x (constant maximum-fxvector-length))))
e-length))
(define-inline 2 make-fxvector
[(e-length)
(and (valid-length? e-length)
(do-make-fxvector e-length default-fill))]
[(e-length e-fill)
(and (valid-length? e-length)
(constant? fixnum? e-fill)
(do-make-fxvector e-length e-fill))])))
(define-inline 3 fxvector-fill!
[(e-fxv e-fill)
`(seq
,(bind #t (e-fxv e-fill)
(build-fxvector-fill e-fxv
(translate
(%inline logxor
,(%mref ,e-fxv ,(constant fxvector-type-disp))
,(%constant type-fxvector))
(constant fxvector-length-offset)
(constant fixnum-offset))
e-fill))
,(%constant svoid))]))
(let ()
(define build-vector-fill
(make-build-fill (constant ptr-bytes) (constant vector-data-disp)))
(meta-assert (= (constant log2-ptr-bytes) (constant fixnum-offset)))
(let ()
(define do-make-vector
(lambda (e-length e-fill)
; NB: caller must bind e-fill
(safe-assert (no-need-to-bind? #f e-fill))
(if (constant? (lambda (x) (and (fixnum? x) (fx<= 0 x 10000))) e-length)
(let ([n (constant-value e-length)])
(if (fx= n 0)
`(quote ,(vector))
(let ([bytes (fx* n (constant ptr-bytes))])
(bind #t ([t (%constant-alloc type-typed-object
(fx+ (constant header-size-vector) bytes))])
`(seq
(set! ,(%mref ,t ,(constant vector-type-disp))
(immediate ,(+ (fx* n (constant vector-length-factor))
(constant type-vector))))
,(build-vector-fill t `(immediate ,bytes) e-fill))))))
(bind #t (e-length) ; fixnum length doubles as byte count
(let ([t-vec (make-tmp 'tvec)])
`(if ,(%inline eq? ,e-length (immediate 0))
(quote ,(vector))
(let ([,t-vec (alloc ,(make-info-alloc (constant type-typed-object) #f #f)
,(%inline logand
,(%inline + ,e-length
(immediate ,(fx+ (constant header-size-vector)
(fx- (constant byte-alignment) 1))))
(immediate ,(- (constant byte-alignment)))))])
(seq
(set! ,(%mref ,t-vec ,(constant vector-type-disp))
,(build-type/length e-length
(constant type-vector)
(constant fixnum-offset)
(constant vector-length-offset)))
,(build-vector-fill t-vec e-length e-fill)))))))))
(define default-fill `(immediate ,(fix 0)))
(define-inline 3 make-vector
[(e-length) (do-make-vector e-length default-fill)]
[(e-length e-fill) (bind #t (e-fill) (do-make-vector e-length e-fill))])
(let ()
(define (valid-length? e-length)
(constant?
(lambda (x) (and (target-fixnum? x) (>= x 0)))
e-length))
(define-inline 2 make-vector
[(e-length)
(and (valid-length? e-length)
(do-make-vector e-length default-fill))]
[(e-length e-fill)
(and (valid-length? e-length)
(constant? fixnum? e-fill)
(do-make-vector e-length e-fill))]))))
(let ()
(meta-assert (= (constant log2-ptr-bytes) (constant fixnum-offset)))
(define-inline 3 $make-eqhash-vector
[(e-length)
(let ([t-vec (make-tmp 'tvec)]
[t-idx (make-assigned-tmp 't-idx)]
[Ltop (make-local-label 'Ltop)])
`(let ([,t-idx ,e-length])
(if ,(%inline eq? ,t-idx (immediate 0))
(quote ,(vector))
(let ([,t-vec (alloc ,(make-info-alloc (constant type-typed-object) #f #f)
,(%inline logand
,(%inline + ,t-idx
(immediate ,(fx+ (constant header-size-vector)
(fx- (constant byte-alignment) 1))))
(immediate ,(- (constant byte-alignment)))))])
(seq
(set! ,(%mref ,t-vec ,(constant vector-type-disp))
,(build-type/length t-idx
(constant type-vector)
(constant fixnum-offset)
(constant vector-length-offset)))
(label ,Ltop
,(%seq
(set! ,t-idx ,(%inline - ,t-idx (immediate ,(fix 1))))
(set! ,(%mref ,t-vec ,t-idx ,(constant vector-data-disp)) ,t-idx)
(if ,(%inline eq? ,t-idx (immediate 0))
,t-vec
(goto ,Ltop)))))))))]))
(define-inline 2 $continuation?
[(e)
(bind #t (e)
(build-and
(%type-check mask-closure type-closure ,e)
(%type-check mask-continuation-code type-continuation-code
,(%mref
,(%inline -
,(%mref ,e ,(constant closure-code-disp))
,(%constant code-data-disp))
,(constant code-type-disp)))))])
(define-inline 3 $continuation-stack-length
[(e)
(translate (%mref ,e ,(constant continuation-stack-length-disp))
(constant fixnum-offset)
(constant log2-ptr-bytes))])
(define-inline 3 $continuation-stack-clength
[(e)
(translate (%mref ,e ,(constant continuation-stack-clength-disp))
(constant fixnum-offset)
(constant log2-ptr-bytes))])
(define-inline 3 $continuation-return-code
[(e)
(bind #t ([t (%inline +
,(%mref ,e ,(constant continuation-return-address-disp))
,(%constant return-address-toplink-disp))])
(%inline - ,t ,(%mref ,t 0)))])
(define-inline 3 $continuation-return-offset
[(e)
(build-fix
(%inline -
,(%mref
,(%mref ,e ,(constant continuation-return-address-disp))
,(constant return-address-toplink-disp))
,(%constant return-address-toplink-disp)))])
(define-inline 3 $continuation-return-livemask
[(e)
(%mref
,(%mref ,e ,(constant continuation-return-address-disp))
,(constant return-address-livemask-disp))])
(define-inline 3 $continuation-stack-ref
[(e-k e-i)
(%mref
,(%mref ,e-k ,(constant continuation-stack-disp))
,(translate e-i (constant fixnum-offset) (constant log2-ptr-bytes))
0)])
(define-inline 2 $foreign-char?
[(e)
(bind #t (e)
(build-and
(%type-check mask-char type-char ,e)
(%inline < ,e (immediate ,(ptr->imm (integer->char #x100))))))])
(define-inline 2 $foreign-wchar?
[(e)
(constant-case wchar-bits
[(16)
(bind #t (e)
(build-and
(%type-check mask-char type-char ,e)
(%inline < ,e (immediate ,(ptr->imm (integer->char #x10000))))))]
[(32) (%type-check mask-char type-char ,e)])])
(define-inline 2 $integer-8?
[(e)
(unless (fx>= (constant fixnum-bits) 8) ($oops '$integer-8? "unexpected fixnum-bits"))
(bind #t (e)
(build-and
(%type-check mask-fixnum type-fixnum ,e)
(%inline u<
,(%inline + ,e (immediate ,(fix #x80)))
(immediate ,(fix #x180)))))])
(define-inline 2 $integer-16?
[(e)
(unless (fx>= (constant fixnum-bits) 16) ($oops '$integer-16? "unexpected fixnum-bits"))
(bind #t (e)
(build-and
(%type-check mask-fixnum type-fixnum ,e)
(%inline u<
,(%inline + ,e (immediate ,(fix #x8000)))
(immediate ,(fix #x18000)))))])
(define-inline 2 $integer-24?
[(e)
(unless (fx>= (constant fixnum-bits) 24) ($oops '$integer-24? "unexpected fixnum-bits"))
(bind #t (e)
(build-and
(%type-check mask-fixnum type-fixnum ,e)
(%inline u<
,(%inline + ,e (immediate ,(fix #x800000)))
(immediate ,(fix #x1800000)))))])
(define-inline 2 $integer-32?
[(e)
(bind #t (e)
(if (fx>= (constant fixnum-bits) 32)
(build-and
(%type-check mask-fixnum type-fixnum ,e)
(%inline u<
,(%inline + ,e (immediate ,(fix #x80000000)))
(immediate ,(fix #x180000000))))
(build-simple-or
(%type-check mask-fixnum type-fixnum ,e)
(build-and
(%type-check mask-typed-object type-typed-object ,e)
(bind #t ([t (%mref ,e ,(constant bignum-type-disp))])
`(if ,(%type-check mask-signed-bignum type-positive-bignum ,t)
,(build-libcall #f #f sexpr <= e `(quote #xffffffff))
,(build-and
(%type-check mask-signed-bignum type-negative-bignum ,t)
(build-libcall #f #f sexpr >= e `(quote #x-80000000)))))))))])
(define-inline 2 $integer-40?
[(e)
(bind #t (e)
(if (fx>= (constant fixnum-bits) 32)
(build-and
(%type-check mask-fixnum type-fixnum ,e)
(%inline u<
,(%inline + ,e (immediate ,(fix #x8000000000)))
(immediate ,(fix #x18000000000))))
(build-simple-or
(%type-check mask-fixnum type-fixnum ,e)
(build-and
(%type-check mask-typed-object type-typed-object ,e)
(bind #t ([t (%mref ,e ,(constant bignum-type-disp))])
`(if ,(%type-check mask-signed-bignum type-positive-bignum ,t)
,(build-libcall #f #f sexpr <= e `(quote #xffffffffff))
,(build-and
(%type-check mask-signed-bignum type-negative-bignum ,t)
(build-libcall #f #f sexpr >= e `(quote #x-8000000000)))))))))])
(define-inline 2 $integer-48?
[(e)
(bind #t (e)
(if (fx>= (constant fixnum-bits) 32)
(build-and
(%type-check mask-fixnum type-fixnum ,e)
(%inline u<
,(%inline + ,e (immediate ,(fix #x800000000000)))
(immediate ,(fix #x1800000000000))))
(build-simple-or
(%type-check mask-fixnum type-fixnum ,e)
(build-and
(%type-check mask-typed-object type-typed-object ,e)
(bind #t ([t (%mref ,e ,(constant bignum-type-disp))])
`(if ,(%type-check mask-signed-bignum type-positive-bignum ,t)
,(build-libcall #f #f sexpr <= e `(quote #xffffffffffff))
,(build-and
(%type-check mask-signed-bignum type-negative-bignum ,t)
(build-libcall #f #f sexpr >= e `(quote #x-800000000000)))))))))])
(define-inline 2 $integer-56?
[(e)
(bind #t (e)
(if (fx>= (constant fixnum-bits) 32)
(build-and
(%type-check mask-fixnum type-fixnum ,e)
(%inline u<
,(%inline + ,e (immediate ,(fix #x80000000000000)))
(immediate ,(fix #x180000000000000))))
(build-simple-or
(%type-check mask-fixnum type-fixnum ,e)
(build-and
(%type-check mask-typed-object type-typed-object ,e)
(bind #t ([t (%mref ,e ,(constant bignum-type-disp))])
`(if ,(%type-check mask-signed-bignum type-positive-bignum ,t)
,(build-libcall #f #f sexpr <= e `(quote #xffffffffffffff))
,(build-and
(%type-check mask-signed-bignum type-negative-bignum ,t)
(build-libcall #f #f sexpr >= e `(quote #x-80000000000000)))))))))])
(define-inline 2 $integer-64?
[(e)
(when (fx>= (constant fixnum-bits) 64) ($oops '$integer-64? "unexpected fixnum-bits"))
(bind #t (e)
(build-simple-or
(%type-check mask-fixnum type-fixnum ,e)
(build-and
(%type-check mask-typed-object type-typed-object ,e)
(bind #t ([t (%mref ,e ,(constant bignum-type-disp))])
`(if ,(%type-check mask-signed-bignum type-positive-bignum ,t)
,(build-libcall #f #f sexpr <= e `(quote #xffffffffffffffff))
,(build-and
(%type-check mask-signed-bignum type-negative-bignum ,t)
(build-libcall #f #f sexpr >= e `(quote #x-8000000000000000))))))))])
(define-inline 3 char->integer
; assumes types are set up so that fixnum tag will be right after the shift
[(e-char) (build-char->integer e-char)])
(define-inline 2 char->integer
; assumes types are set up so that fixnum tag will be right after the shift
[(e-char)
(bind #t (e-char)
`(if ,(%type-check mask-char type-char ,e-char)
,(%inline srl ,e-char
(immediate ,(fx- (constant char-data-offset) (constant fixnum-offset))))
,(build-libcall #t src sexpr char->integer e-char)))])
(define-inline 3 char-
; assumes fixnum is zero
[(e1 e2)
(%inline sra
,(%inline - ,e1 ,e2)
(immediate ,(fx- (constant char-data-offset) (constant fixnum-offset))))])
(define-inline 3 integer->char
[(e-int) (build-integer->char e-int)])
(define-inline 3 boolean=?
[(e1 e2) (%inline eq? ,e1 ,e2)]
[(e1 e2 . e*) (reduce-equality src sexpr moi e1 e2 e*)])
(define-inline 3 symbol=?
[(e1 e2) (%inline eq? ,e1 ,e2)]
[(e1 e2 . e*) (reduce-equality src sexpr moi e1 e2 e*)])
(let ()
(define (go e flag)
(%inline logtest
,(%mref ,e ,(constant record-type-flags-disp))
(immediate ,(fix flag))))
(define-inline 3 record-type-opaque?
[(e) (go e (constant rtd-opaque))])
(define-inline 3 record-type-sealed?
[(e) (go e (constant rtd-sealed))])
(define-inline 3 record-type-generative?
[(e) (go e (constant rtd-generative))]))
(let ()
(define build-record?
(lambda (e)
(bind #t (e)
(build-and
(%type-check mask-typed-object type-typed-object ,e)
(bind #t ([t (%mref ,e ,(constant typed-object-type-disp))])
(build-and
(%type-check mask-record type-record ,t)
(build-not
(%inline logtest
,(%mref ,t ,(constant record-type-flags-disp))
(immediate ,(fix (constant rtd-opaque)))))))))))
(define build-sealed-isa?
(lambda (e e-rtd)
(bind #t (e)
(bind #f (e-rtd)
(build-and
(%type-check mask-typed-object type-typed-object ,e)
(%inline eq?
,(%mref ,e ,(constant typed-object-type-disp))
,e-rtd))))))
(define build-unsealed-isa?
(lambda (e e-rtd)
(let ([t (make-assigned-tmp 't)] [Ltop (make-local-label 'Ltop)])
(bind #t (e e-rtd)
(build-and
(%type-check mask-typed-object type-typed-object ,e)
`(let ([,t ,(%mref ,e ,(constant typed-object-type-disp))])
,(build-simple-or
(%inline eq? ,t ,e-rtd)
(build-and
(%type-check mask-record type-record ,t)
`(label ,Ltop
(seq
(set! ,t ,(%mref ,t ,(constant record-type-parent-disp)))
,(build-simple-or
(%inline eq? ,t ,e-rtd)
`(if ,(%inline eq? ,t ,(%constant sfalse))
,(%constant sfalse)
(goto ,Ltop)))))))))))))
(define-inline 3 record?
[(e) (build-record? e)]
[(e e-rtd)
(if (constant? (lambda (x)
(and (record-type-descriptor? x)
(record-type-sealed? x)))
e-rtd)
(build-sealed-isa? e e-rtd)
(build-unsealed-isa? e e-rtd))])
(define-inline 2 r6rs:record?
[(e) (build-record? e)])
(define-inline 2 record?
[(e) (build-record? e)]
[(e e-rtd)
(nanopass-case (L7 Expr) e-rtd
[(quote ,d)
(and (record-type-descriptor? d)
(if (record-type-sealed? d)
(build-sealed-isa? e e-rtd)
(build-unsealed-isa? e e-rtd)))]
[else #f])])
(define-inline 2 $sealed-record?
[(e e-rtd) (build-sealed-isa? e e-rtd)])
(define-inline 2 eq-hashtable?
[(e) (let ([rtd (let () (include "hashtable-types.ss") (record-type-descriptor eq-ht))])
(let ([e-rtd `(quote ,rtd)])
(if (record-type-sealed? rtd)
(build-sealed-isa? e e-rtd)
(build-unsealed-isa? e e-rtd))))]))
(define-inline 2 gensym?
[(e)
(bind #t (e)
(build-and
(%type-check mask-symbol type-symbol ,e)
(bind #t ([t (%mref ,e ,(constant symbol-name-disp))])
`(if ,t
,(%type-check mask-pair type-pair ,t)
,(%constant strue)))))])
(let ()
(define build-make-symbol
(lambda (e-name)
(bind #t ([t (%constant-alloc type-symbol (constant size-symbol))])
(%seq
(set! ,(%mref ,t ,(constant symbol-name-disp)) ,e-name)
(set! ,(%mref ,t ,(constant symbol-value-disp)) ,(%constant sunbound))
(set! ,(%mref ,t ,(constant symbol-pvalue-disp))
(literal
,(make-info-literal #f 'library
(lookup-libspec nonprocedure-code)
(constant code-data-disp))))
(set! ,(%mref ,t ,(constant symbol-plist-disp)) ,(%constant snil))
(set! ,(%mref ,t ,(constant symbol-splist-disp)) ,(%constant snil))
(set! ,(%mref ,t ,(constant symbol-hash-disp)) ,(%constant sfalse))
,t))))
(define (go e-pname)
(bind #t ([t (%constant-alloc type-pair (constant size-pair))])
(%seq
(set! ,(%mref ,t ,(constant pair-cdr-disp)) ,e-pname)
(set! ,(%mref ,t ,(constant pair-car-disp)) ,(%constant sfalse))
,(build-make-symbol t))))
(define-inline 3 gensym
[() (build-make-symbol (%constant sfalse))]
[(e-pname) (bind #f (e-pname) (go e-pname))]
[(e-pname e-uname) #f])
(define-inline 2 gensym
[() (build-make-symbol (%constant sfalse))]
[(e-pname) (and (constant? string? e-pname) (go e-pname))]
[(e-pname e-uname) #f]))
(define-inline 3 symbol->string
[(e-sym)
(bind #t (e-sym)
(bind #t ([e-name (%mref ,e-sym ,(constant symbol-name-disp))])
`(if ,e-name
(if ,(%type-check mask-pair type-pair ,e-name)
,(%mref ,e-name ,(constant pair-cdr-disp))
,e-name)
,(%primcall #f sexpr $gensym->pretty-name ,e-sym))))])
(define-inline 3 $fxaddress
[(e) (%inline logand
,(let ([n (- (log2 (constant typemod)) (constant fixnum-offset))])
(if (> n 0) (%inline sra ,e (immediate ,n)) e))
(immediate ,(- (constant fixnum-factor))))])
(define-inline 3 $set-timer
[(e) (bind #f (e)
(bind #t ([t (build-fix (ref-reg %trap))])
`(seq
(set! ,(ref-reg %trap) ,(build-unfix e))
,t)))])
(define-inline 3 directory-separator?
[(e) (if-feature windows
(bind #t (e)
(build-simple-or
(%inline eq? ,e (immediate ,(ptr->imm #\/)))
(%inline eq? ,e (immediate ,(ptr->imm #\\)))))
(%inline eq? ,e (immediate ,(ptr->imm #\/))))])
(let ()
(define add-cdrs
(lambda (n e)
(if (fx= n 0)
e
(add-cdrs (fx- n 1) (%mref ,e ,(constant pair-cdr-disp))))))
(define-inline 3 list-ref
[(e-ls e-n)
(nanopass-case (L7 Expr) e-n
[(quote ,d)
(and (and (fixnum? d) (fx< d 4))
(%mref ,(add-cdrs d e-ls) ,(constant pair-car-disp)))]
[else #f])])
(define-inline 3 list-tail
[(e-ls e-n)
(nanopass-case (L7 Expr) e-n
[(quote ,d) (and (and (fixnum? d) (fx<= d 4)) (add-cdrs d e-ls))]
[else #f])]))
(let ()
(define (go0 src sexpr subtype)
(%primcall src sexpr $make-eq-hashtable
(immediate ,(fix (constant hashtable-default-size)))
(immediate ,(fix subtype))))
(define (go1 src sexpr e-size subtype)
(nanopass-case (L7 Expr) e-size
[(quote ,d)
; d must be a fixnum? for $hashtable-size-minlen and a
; target-machine fixnum for cross compiling
(and (and (fixnum? d) (target-fixnum? d) (fx>= d 0))
(%primcall src sexpr $make-eq-hashtable
(immediate ,(fix ($hashtable-size->minlen d)))
(immediate ,(fix subtype))))]
[else #f]))
(define-inline 3 make-eq-hashtable
[() (go0 src sexpr (constant eq-hashtable-subtype-normal))]
[(e-size) (go1 src sexpr e-size (constant eq-hashtable-subtype-normal))])
(define-inline 3 make-weak-eq-hashtable
[() (go0 src sexpr (constant eq-hashtable-subtype-weak))]
[(e-size) (go1 src sexpr e-size (constant eq-hashtable-subtype-weak))])
(define-inline 3 make-ephemeron-eq-hashtable
[() (go0 src sexpr (constant eq-hashtable-subtype-ephemeron))]
[(e-size) (go1 src sexpr e-size (constant eq-hashtable-subtype-ephemeron))]))
(let ()
(define-syntax def-put-x
(syntax-rules ()
[(_ name x-length)
(define-inline 3 name
[(e-bop e-x)
(bind #t (e-x)
(build-libcall #f src sexpr name e-bop e-x `(immediate 0)
(handle-prim #f #f 3 'x-length (list e-x))))]
[(e-bop e-x e-start)
(bind #t (e-x e-start)
(build-libcall #f src sexpr name e-bop e-x e-start
(%inline -
,(handle-prim #f #f 3 'x-length (list e-x))
,e-start)))]
[(e-bop e-x e-start e-count)
(build-libcall #f src sexpr name e-bop e-x e-start e-count)])]))
(def-put-x put-bytevector bytevector-length)
(def-put-x put-bytevector-some bytevector-length)
(def-put-x put-string string-length)
(def-put-x put-string-some string-length))
(define-inline 3 $read-time-stamp-counter
[()
(constant-case architecture
[(x86)
(%seq
; returns low-order 32 bits in eax, high-order in edx
(set! ,%eax (inline ,(make-info-kill* (reg-list %edx)) ,%read-time-stamp-counter))
,(u32xu32->ptr %edx %eax))]
[(x86_64)
(%seq
; returns low-order 32 bits in rax, high-order in rdx
(set! ,%rax (inline ,(make-info-kill* (reg-list %rdx)) ,%read-time-stamp-counter))
,(unsigned->ptr
(%inline logor ,(%inline sll ,%rdx (immediate 32)) ,%rax)
64))]
[(arm32) (unsigned->ptr (%inline read-time-stamp-counter) 32)]
[(ppc32)
(let ([t-hi (make-tmp 't-hi)])
`(let ([,t-hi (inline ,(make-info-kill* (reg-list %real-zero))
,%read-time-stamp-counter)])
,(u32xu32->ptr t-hi %real-zero)))])])
(define-inline 3 $read-performance-monitoring-counter
[(e)
(constant-case architecture
[(x86)
(%seq
(set! ,%eax (inline ,(make-info-kill* (reg-list %edx)) ,%read-performance-monitoring-counter ,(build-unfix e)))
,(u32xu32->ptr %edx %eax))]
[(x86_64)
(%seq
(set! ,%rax (inline ,(make-info-kill* (reg-list %rdx)) ,%read-performance-monitoring-counter ,(build-unfix e)))
,(unsigned->ptr
(%inline logor ,(%inline sll ,%rdx (immediate 32)) ,%rax)
64))]
[(arm32 ppc32) (unsigned->ptr (%inline read-performance-monitoring-counter ,(build-unfix e)) 32)])])
)) ; expand-primitives module
(define-pass np-place-overflow-and-trap : L9 (ir) -> L9.5 ()
(definitions
(define repeat? #f)
(define update-label!
(lambda (l oc tc)
(let ([orig-oc (local-label-overflow-check l)]
[orig-tc (local-label-trap-check l)])
(unless (and (eq? oc orig-oc) (eq? tc orig-tc))
(set! repeat? #t)
(local-label-overflow-check-set! l oc)
(local-label-trap-check-set! l tc)))))
(define combine-seq
(lambda (x y)
(case x
[(no) y]
[(yes) 'yes]
[else (if (eq? y 'no) 'maybe 'yes)])))
(define-pass strip-redundant-overflow-and-trap : (L9.5 Expr) (ir) -> (L9.5 Expr) ()
(definitions
(define-record-type goto (nongenerative) (fields label oc? tc?))
(define goto* '())
(define well-behaved-goto?
(lambda (goto)
(and (or (goto-oc? goto) (not (local-label-overflow-check (goto-label goto))))
(or (goto-tc? goto) (not (local-label-trap-check (goto-label goto))))))))
(Lvalue : Lvalue (ir oc? tc?) -> Lvalue ()
[(mref ,[e0] ,[e1] ,imm) `(mref ,e0 ,e1 ,imm)])
(Expr : Expr (ir oc? tc?) -> Expr ()
[(overflow-check ,[e #t tc? -> e]) (if oc? e `(overflow-check ,e))]
[(trap-check ,ioc ,[e oc? #t -> e]) (if tc? e `(trap-check ,(if oc? #f ioc) ,e))]
[(call ,info ,mdcl (literal ,info0) ,[e*] ...)
(guard oc? (eq? (info-literal-type info0) 'library)
(libspec-does-not-expect-headroom? (info-literal-addr info0)))
`(call ,info ,mdcl
(literal ,(make-info-literal #f 'library
(libspec->headroom-libspec (info-literal-addr info0))
0))
,e* ...)]
[(loop ,x (,x* ...) ,[body oc? #f -> body]) `(loop ,x (,x* ...) ,body)]
[(label ,l ,[body])
(local-label-overflow-check-set! l (and (not (eq? (local-label-overflow-check l) 'no)) oc?))
(local-label-trap-check-set! l (and (not (eq? (local-label-trap-check l) 'no)) tc?))
`(label ,l ,body)]
[(goto ,l) (set! goto* (cons (make-goto l oc? tc?) goto*)) ir])
(let ([ir (Expr ir #f #f)])
(and (andmap well-behaved-goto? goto*) ir)))
(define-pass insert-loop-traps : (L9 Expr) (ir) -> (L9.5 Expr) ()
(Expr : Expr (ir) -> Expr ()
[(loop ,x (,x* ...) ,[body]) `(loop ,x (,x* ...) (trap-check #f ,body))]))
(define has-no-headroom-libcall?
(lambda (e?)
(and e?
(nanopass-case (L9.5 Expr) e?
[(literal ,info)
(and (eq? (info-literal-type info) 'library)
(libspec-has-does-not-expect-headroom-version? (info-literal-addr info))
info)]
[else #f]))))
(with-output-language (L9.5 Expr)
(define request-trap-check (if (generate-interrupt-trap) 'yes 'no))
(define add-trap-check
(lambda (overflow? e)
(if (eq? request-trap-check 'yes)
`(trap-check ,overflow? ,e)
e)))))
(Lvalue : Lvalue (ir) -> Lvalue ('no 'no)
[(mref ,[e0 #f -> e0 oc0 tc0] ,[e1 #f -> e1 oc1 tc1] ,imm)
(values `(mref ,e0 ,e1 ,imm) (combine-seq oc0 oc1) (combine-seq tc0 tc1))])
(Expr : Expr (ir tail?) -> Expr ('no 'no)
[(goto ,l)
(if (local-label? l)
(values `(goto ,l) (local-label-overflow-check l) (local-label-trap-check l))
(values `(goto ,l) 'no 'no))]
[(values ,info ,[e* #f -> e* oc* tc*] ...)
(values `(values ,info ,e* ...) (fold-left combine-seq 'no oc*) (fold-left combine-seq 'no tc*))]
[(call ,info ,mdcl ,x ,[e* #f -> e* oc* tc*] ...)
(guard (uvar? x) (eq? (uvar-location x) 'loop))
(values `(call ,info ,mdcl ,x ,e* ...) (fold-left combine-seq 'no oc*) request-trap-check)]
[(call ,info ,mdcl ,e? ,[e* #f -> e* oc* tc*] ...)
(let-values ([(e? oc tc) (if e? (Expr e? #f) (values e? 'no 'no))])
; to save code space, we skip trap check for error calls under assumption trap checks will
; be made by the error handler. if not, could get a uninterruptible hard loop...c'est la vie
(define wrap-tc
(lambda (overflow? call)
(if (and (info-call-error? info)
(eq? (fold-left combine-seq tc tc*) 'no))
call
(add-trap-check overflow? call))))
(let ([noc? (eq? (fold-left combine-seq oc oc*) 'no)])
(cond
[(and (or tail? (and (info-call-error? info) (fx< (debug-level) 2))) noc?)
(let ([call `(call ,info ,mdcl ,e? ,e* ...)])
(if (info-call-pariah? info)
(values (wrap-tc #t call) 'no 'no)
(values call 'no request-trap-check)))]
[(and noc? (has-no-headroom-libcall? e?)) =>
(lambda (info0)
(safe-assert (not (libspec-does-not-expect-headroom? (info-literal-addr info0))))
(let ([call `(call ,info ,mdcl
(literal ,(make-info-literal #f 'library
(libspec->does-not-expect-headroom-libspec (info-literal-addr info0))
0))
,e* ...)])
(if (info-call-pariah? info)
(values (wrap-tc #t call) 'no 'no)
(values call 'no request-trap-check))))]
[else (let ([call `(call ,info ,mdcl ,e? ,e* ...)])
(if (info-call-pariah? info)
(values `(overflow-check ,(wrap-tc #f call)) 'no 'no)
(values call 'yes request-trap-check)))])))]
[(inline ,info ,prim ,[e* #f -> e* oc* tc*] ...)
(values `(inline ,info ,prim ,e* ...) (fold-left combine-seq 'no oc*) (fold-left combine-seq 'no tc*))]
[(alloc ,info ,[e #f -> e oc tc]) (values `(alloc ,info ,e) oc tc)]
[(loop ,x (,x* ...) ,body)
(uvar-location-set! x 'loop)
(let-values ([(body oc tc) (Expr body tail?)])
(uvar-location-set! x #f)
(values
(if (eq? tc 'yes)
`(loop ,x (,x* ...) ,(add-trap-check #t body))
`(loop ,x (,x* ...) ,body))
(if (eq? oc 'no) 'no 'yes)
'no))]
[(foreign-call ,info ,[e #f -> e oc tc] ,[e* #f -> e* oc* tc*] ...)
(values `(foreign-call ,info ,e ,e* ...) (fold-left combine-seq oc oc*) (fold-left combine-seq tc tc*))]
[(label ,l ,[body oc tc]) (update-label! l oc tc) (values `(label ,l ,body) oc tc)]
[(set! ,[lvalue -> lvalue oc0 tc0] ,[e #f -> e oc1 tc1])
(values `(set! ,lvalue ,e) (combine-seq oc0 oc1) (combine-seq tc0 tc1))]
[(mvlet ,[e #f -> e oc tc] ((,x** ...) ,interface* ,[body* oc* tc*]) ...)
; claiming mvlet always makes a nontail call
(values `(mvlet ,e ((,x** ...) ,interface* ,body*) ...) 'yes request-trap-check)]
[(mvcall ,info ,[e1 #f -> e1 oc1 tc1] ,[e2 #f -> e2 oc2 tc2])
; claiming mvcall always makes a nontail call
(values `(mvcall ,info ,e1 ,e2) 'yes request-trap-check)]
[(let ([,x* ,[e* #f -> e* oc* tc*]] ...) ,[body oc tc])
(values `(let ([,x* ,e*] ...) ,body) (fold-left combine-seq oc oc*) (fold-left combine-seq tc tc*))]
[(if ,[e0 #f -> e0 oc0 tc0] ,[e1 oc1 tc1] ,[e2 oc2 tc2])
(define combine-branch
(lambda (l r)
(case l
[(yes) (if (eq? r 'yes) 'yes 'maybe)]
[(no) (if (eq? r 'no) 'no 'maybe)]
[else l])))
(let ([oc (combine-seq oc0 (combine-branch oc1 oc2))]
[tc (combine-seq tc0 (combine-branch tc1 tc2))])
(define wrap-oc
(lambda (ocx e)
(if (and (eq? ocx 'yes) (not (eq? oc 'yes)))
`(overflow-check ,e)
e)))
(define wrap-tc
(lambda (tcx e)
(if (and (eq? tcx 'yes) (not (eq? tc 'yes)))
(add-trap-check #t e)
e)))
(values
`(if ,e0 ,(wrap-oc oc1 (wrap-tc tc1 e1)) ,(wrap-oc oc2 (wrap-tc tc2 e2)))
oc tc))]
[(seq ,[e0 #f -> e0 oc0 tc0] ,[e1 oc1 tc1])
(values `(seq ,e0 ,e1) (combine-seq oc0 oc1) (combine-seq tc0 tc1))])
(CaseLambdaClause : CaseLambdaClause (ir force-overflow?) -> CaseLambdaClause ()
[(clause (,x* ...) ,mcp ,interface ,body)
(safe-assert (not repeat?)) ; should always be initialized and/or reset to #f
`(clause (,x* ...) ,mcp ,interface
,(or (let f ()
(let-values ([(body oc tc) (Expr body #t)])
(if repeat?
(begin (set! repeat? #f) (f))
(strip-redundant-overflow-and-trap
(let ([body (if (eq? tc 'yes) (add-trap-check #t body) body)])
(if (or force-overflow? (eq? oc 'yes))
`(overflow-check ,body)
body))))))
; punting badly here under assumption that we currently can't even generate
; misbehaved gotos, i.e., paths ending in a goto that don't do an overflow
; or trap check where the target label expects it to have been done. if we
; ever violate this assumption on a regular basis, might want to revisit and
; do something better.
; ... test punt case by commenting out above for all but library.ss
`(overflow-check (trap-check #f ,(insert-loop-traps body)))))])
(CaseLambdaExpr : CaseLambdaExpr (ir) -> CaseLambdaExpr ()
[(case-lambda ,info ,[cl* (let ([libspec (info-lambda-libspec info)])
(and libspec (libspec-does-not-expect-headroom? libspec))) -> cl*] ...)
`(case-lambda ,info ,cl* ...)]))
(define-pass np-rebind-on-ruined-path : L9.5 (ir) -> L9.5 ()
(definitions
(define prefix*)
(define add-prefix!
(lambda (x)
(when (uvar? x)
(unless (uvar-in-prefix? x)
(uvar-in-prefix! x #t)
(set! prefix* (cons x prefix*))))))
(define add-prefix*! (lambda (x*) (for-each add-prefix! x*)))
(define reset-prefix*!
(lambda (orig-prefix*)
(let loop ([ls prefix*] [diff* '()])
(if (eq? ls orig-prefix*)
(begin (set! prefix* ls) diff*)
(let ([x (car ls)])
(uvar-in-prefix! x #f)
(loop (cdr ls) (cons x diff*)))))))
(define-pass gather-refs : (L9.5 Expr) (e) -> (L9.5 Expr) (x*)
(definitions (define x*))
(Expr : Expr (ir) -> Expr ()
[,x (guard (uvar? x))
(cond
[(uvar-in-prefix? x)
(let ([t (make-tmp 't)])
(uvar-location-set! x t)
(uvar-in-prefix! x #f)
(set! x* (cons x x*))
t)]
[(uvar-location x)]
[else x])])
(fluid-let ([x* '()])
(let ([e (Expr e)])
(values e x*)))))
(Expr : Expr (ir) -> Expr ()
[(overflow-check (call ,info ,mdcl ,e? ,e* ...))
(guard (info-call-error? info))
`(overflow-check (call ,info ,mdcl ,e? ,e* ...))]
[(overflow-check ,e)
(if (null? prefix*)
`(overflow-check ,e)
(let-values ([(e x*) (gather-refs e)])
(let ([t* (map (lambda (x)
(let ([t (uvar-location x)])
(uvar-location-set! x #f)
t))
x*)])
`(let ([,t* ,x*] ...) (overflow-check ,e)))))]
[(set! ,x ,[e])
(guard (and (uvar? x) (not (uvar-assigned? x))))
(add-prefix! x)
`(set! ,x ,e)]
[(let ([,x* ,[e*]] ...) ,body)
(add-prefix*! x*)
`(let ([,x* ,e*] ...) ,(Expr body))]
[(if ,[e0] ,e1 ,e2)
(let ([orig-prefix* prefix*])
(let ([e1 (Expr e1)])
(let ([e1-diff-prefix* (reset-prefix*! orig-prefix*)])
(let ([e2 (Expr e2)])
(add-prefix*! e1-diff-prefix*)
`(if ,e0 ,e1 ,e2)))))]
[(seq ,[e0] ,e1) `(seq ,e0 ,(Expr e1))])
(CaseLambdaClause : CaseLambdaClause (ir) -> CaseLambdaClause ()
[(clause (,x* ...) ,mcp ,interface ,body)
(fluid-let ([prefix* x*])
`(clause (,x* ...) ,mcp ,interface ,(Expr body)))]))
(define-pass np-finalize-loops : L9.5 (ir) -> L9.75 ()
(Expr : Expr (ir) -> Expr ()
[(loop ,x (,x* ...) ,body)
(let ([Ltop (make-local-label (uvar-name x))])
(uvar-location-set! x (cons Ltop x*))
(let ([body (Expr body)])
(uvar-location-set! x #f)
`(label ,Ltop ,body)))]
[(call ,info ,mdcl ,x ,[e*] ...)
(guard (uvar-location x))
(let ([Ltop.x* (uvar-location x)])
(fold-left (lambda (body x e) `(seq (set! ,x ,e) ,body))
`(goto ,(car Ltop.x*)) (cdr Ltop.x*) e*))]))
(define-pass np-optimize-pred-in-value : L9.75 (ir) -> L9.75 ()
(definitions
(define bar
(lambda (e bool?)
(if (eq? bool? 'wrapper)
(with-output-language (L9.75 Expr)
`(if ,e ,(%constant strue) ,(%constant sfalse)))
e)))
(define dont
(lambda (e)
(with-values (Expr e #f) (lambda (e bool?) e)))))
(Value : Expr (ir) -> Expr ()
[else (with-values (Expr ir 'value) bar)])
(Lvalue : Lvalue (ir) -> Expr (#f))
(Expr : Expr (ir [value? #f]) -> Expr (#f)
[(immediate ,imm) (values ir (or (eq? imm (constant strue)) (eq? imm (constant sfalse))))]
[(set! ,lvalue ,[e]) (values `(set! ,lvalue ,e) #f)]
[(seq ,[dont : e0] ,[e1 bool?]) (values `(seq ,e0 ,e1) bool?)]
[(let ([,x* ,[e*]] ...) ,[e bool?]) (values `(let ([,x* ,e*] ...) ,e) bool?)]
[(inline ,info ,prim ,[e*] ...)
(guard (pred-primitive? prim))
(values `(inline ,info ,prim ,e* ...) #t)]
[(if ,[dont : e0] ,[e1 bool1?] ,[e2 bool2?])
(guard value?)
(if (and bool1? bool2?)
(values `(if ,e0 ,e1 ,e2) 'wrapper)
(values `(if ,e0 ,(bar e1 bool1?) ,(bar e2 bool2?)) #f))]))
(define-pass np-remove-complex-opera* : L9.75 (ir) -> L10 ()
; remove-complex-opera* cannot assume that assigned uvars and
; (mrefs at this point) are immutable. it must take this into
; account and avoid possible interleaved subexpression evaluation
; for calls and inline forms. it can do so by removing all lvalues
; as call/inline subexpressions, or it can be more selective and
; allow them to remain when doing so can't cause any problems.
; for example, (<lvalue1> <lvalue2>) can be left alone, and both
;
; ((begin e <lvalue1>) <lvalue2>) => (begin e (<lvalue1> <lvalue2>))
;
; and
;
; (<lvalue1> (begin e <lvalue2>)) => (begin e (<lvalue1> <lvalue2>))
;
; are safe transformations, but
;
; ((begin e1 <lvalue1>) (begin e2 <lvalue2>))
;
; cannot be turned into
;
; (begin e1 e2 (<lvalue1> <lvalue2>)).
;
; NB: remove-complex-opera* produces set! forms rather than let bindings
; since the former (but not the latter) can be pushed into both branches
; of an if without causing potentially exponential code growth
(definitions
(define local*)
(define make-tmp
(lambda (x)
(import (only np-languages make-tmp))
(let ([x (make-tmp x)])
(set! local* (cons x local*))
x)))
(define Ref
(lambda (ir setup*)
(if (var? ir)
(values ir setup*)
(let ([tmp (make-tmp 't)])
(values tmp (cons (Rhs ir tmp) setup*))))))
(define Lvalue?
(lambda (x)
(nanopass-case (L10 Triv) x
[,lvalue #t]
[else #f])))
(define Triv*
(lambda (e* k)
(let f ([e* e*] [lvalue-setup* '()] [rt* '()] [setup* '()])
(if (null? e*)
(build-seq* setup*
(build-seq* lvalue-setup*
(k (reverse rt*))))
(let-values ([(t t-setup*) (Triv (car e*) (null? lvalue-setup*))])
(if (and (null? lvalue-setup*)
(not (null? t-setup*))
(Lvalue? t)
; uvar's are singly assigned
(or (not (uvar? t)) (uvar-assigned? t)))
(f (cdr e*) t-setup* (cons t rt*) setup*)
(f (cdr e*) lvalue-setup* (cons t rt*) (append t-setup* setup*))))))))
(define build-seq* (lambda (x* y) (fold-right build-seq y x*)))
(with-output-language (L10 Expr)
(define build-seq (lambda (x y) `(seq ,x ,y)))
(define Rhs
(lambda (ir lvalue)
(Expr ir
(lambda (e)
(nanopass-case (L10 Expr) e
[,rhs `(set! ,lvalue ,rhs)]
[(values ,info ,t) `(set! ,lvalue ,t)]
[(values ,info ,t* ...)
; sets lvalue to void. otherwise, the lvalue we entered with (which
; might be referenced downstream) is never set and hence fails in the live
; analysis where it is live all the way out of the function.
`(seq
(call ,(make-info-call (info-call-src info) (info-call-sexpr info) #f #t #t) #f
(literal ,(make-info-literal #t 'object '$oops (constant symbol-value-disp)))
,(%constant sfalse)
(literal ,(make-info-literal #f 'object
(format "returned ~r values to single value return context"
(length t*)) 0)))
(set! ,lvalue ,(%constant svoid)))]
[else (sorry! who "unexpected Rhs expression ~s" e)])))))))
(CaseLambdaClause : CaseLambdaClause (ir) -> CaseLambdaClause ()
[(clause (,x* ...) ,mcp ,interface ,body)
(fluid-let ([local* '()])
(let ([body (Expr body values)])
(safe-assert (nodups x* local*))
`(clause (,x* ...) (,local* ...) ,mcp ,interface
,body)))])
(Triv : Expr (ir lvalue-okay?) -> Triv (setup*)
[,x
(guard (or lvalue-okay? (and (uvar? x) (not (uvar-assigned? x))) (eq? x %zero)))
(values x '())]
[(mref ,e1 ,e2 ,imm)
(guard lvalue-okay?)
(let*-values ([(x1 setup*) (Ref e1 '())] [(x2 setup*) (Ref e2 setup*)])
(values (%mref ,x1 ,x2 ,imm) setup*))]
[(literal ,info) (values `(literal ,info) '())]
[(immediate ,imm) (values `(immediate ,imm) '())]
[(label-ref ,l ,offset) (values `(label-ref ,l ,offset) '())]
[(let ([,x* ,e*] ...) ,[t setup*])
(set! local* (append x* local*))
(safe-assert (nodups local*))
(values t
(fold-right
(lambda (ir lvalue setup*) (cons (Rhs ir lvalue) setup*))
setup* e* x*))]
[(seq ,[Expr : e0 values -> e0] ,[t setup*])
(values t (cons e0 setup*))]
[(pariah) (values (%constant svoid) (list (with-output-language (L10 Expr) `(pariah))))]
[else
(let ([tmp (make-tmp 't)])
(values tmp (list (Rhs ir tmp))))])
(Expr : Expr (ir k) -> Expr ()
[(inline ,info ,prim ,e1* ...)
(Triv* e1*
(lambda (t1*)
(k `(inline ,info ,prim ,t1* ...))))]
[(alloc ,info ,e)
(let-values ([(t setup*) (Triv e #t)])
(build-seq* setup* (k `(alloc ,info ,t))))]
[(call ,info ,mdcl ,e0? ,e1* ...)
(if e0?
(Triv* (cons e0? e1*) (lambda (t*) (k `(call ,info ,mdcl ,(car t*) ,(cdr t*) ...))))
(Triv* e1* (lambda (t*) (k `(call ,info ,mdcl #f ,t* ...)))))]
[(foreign-call ,info ,e0 ,e1* ...)
(Triv* (cons e0 e1*)
(lambda (t*)
(k `(foreign-call ,info ,(car t*) ,(cdr t*) ...))))]
[(values ,info ,e* ...)
(Triv* e*
(lambda (t*)
(k `(values ,info ,t* ...))))]
[(if ,[Expr : e0 values -> e0] ,[e1] ,[e2]) `(if ,e0 ,e1 ,e2)]
[(seq ,[Expr : e0 values -> e0] ,[e1]) `(seq ,e0 ,e1)]
[(set! ,lvalue ,e)
(let-values ([(lvalue setup*) (Triv lvalue #t)])
; must put lvalue setup* first to avoid potentially interleaved argument
; evaluation in, e.g.:
;
; (let ([p1 (cons 0 1)] [p2 (cons 0 2)])
; (let ([x (cons 0 3)])
; (set-car!
; (begin (set-car! x p1) (car x))
; (begin (set-car! x p2) (car x)))
; (eq? (car p1) p2)))
; ; after expand-primitives (essentially):
; => (let ([p1 (cons 0 1)] [p2 (cons 0 2)])
; (let ([x (cons 0 3)])
; (set!
; ,(%mref (begin (set! ,(%mref x 0) p1) ,(%mref x 0)) 0)
; (begin (set! ,(%mref x 0) p2) ,(%mref x 0)))
; (eq? ,(%mref p1 0) p2)))
; ; okay:
; => (let ([p1 (cons 0 1)] [p2 (cons 0 2)])
; (let ([x (cons 0 3)])
; ; setup* for lvalue:
; (set! ,(%mref x 0) p1)
; (set! t ,(%mref x 0))
; ; setup* for e
; (set! ,(%mref x 0) p2)
; (set! ,(%mref t 0) ,(%mref x 0))
; (eq? ,(%mref p1 0) p2)))
; ; not okay:
; => (let ([p1 (cons 0 1)] [p2 (cons 0 2)])
; (let ([x (cons 0 3)])
; ; setup* for e
; (set! ,(%mref x 0) p2)
; ; setup* for lvalue:
; (set! ,(%mref x 0) p1)
; (set! t ,(%mref x 0))
; (set!
; ,(%mref t 0)
; ; wrong x[0]
; ,(%mref x 0))
; (eq? ,(%mref p1 0) p2)))
(build-seq* setup*
`(seq
,(Rhs e lvalue)
,(k (%constant svoid)))))]
[(let ([,x* ,e*] ...) ,[body])
(set! local* (append x* local*))
(safe-assert (nodups local*))
(fold-left (lambda (t x e) (build-seq (Rhs e x) t)) body x* e*)]
[(mvlet ,[Expr : e values -> e] ((,x** ...) ,interface* ,[body*]) ...)
(set! local* (append (apply append x**) local*))
(safe-assert (nodups local*))
`(mvlet ,e ((,x** ...) ,interface* ,body*) ...)]
[(mvcall ,info ,[Expr : e1 values -> e1] ,e2)
(let-values ([(t2 setup*) (Triv e2 #t)])
(build-seq* setup* (k `(mvcall ,info ,e1 ,t2))))]
[(goto ,l) `(goto ,l)]
[(label ,l ,[body]) `(label ,l ,body)]
[(trap-check ,ioc ,[body]) `(trap-check ,ioc ,body)]
[(overflow-check ,[body]) `(overflow-check ,body)]
[(pariah) `(pariah)]
[(profile ,src) `(profile ,src)]
[else
(let-values ([(t setup*) (Triv ir #t)])
(build-seq* setup* (k t)))]))
(define-pass np-push-mrvs : L10 (ir) -> L10.5 ()
(definitions
(define local*)
(define make-tmp
(lambda (x)
(import (only np-languages make-tmp))
(let ([x (make-tmp x)])
(set! local* (cons x local*))
x)))
(define Mvcall
(lambda (info e consumer k)
(with-output-language (L10.5 Expr)
(nanopass-case (L10.5 Expr) e
[,t (k `(mvcall ,(make-info-call (info-call-src info) (info-call-sexpr info) #f #f #f) #f ,consumer ,t ()))]
[(values ,info2 ,t* ...)
(k `(mvcall ,(make-info-call (info-call-src info) (info-call-sexpr info) #f #f #f) #f ,consumer ,t* ... ()))]
[(mvcall ,info ,mdcl ,t0 ,t1* ... (,t* ...))
(k `(mvcall ,info ,mdcl ,t0 ,t1* ... (,t* ... ,consumer)))]
[(if ,e0 ,[e1] ,[e2]) `(if ,e0 ,e1 ,e2)]
[(seq ,e0 ,[e1]) `(seq ,e0 ,e1)]
[(mlabel ,[e] (,l* ,[e*]) ...) `(mlabel ,e (,l* ,e*) ...)]
[(label ,l ,[body]) `(label ,l ,body)]
[(trap-check ,ioc ,[body]) `(trap-check ,ioc ,body)]
[(overflow-check ,[body]) `(overflow-check ,body)]
[(pariah) `(pariah)]
[(profile ,src) `(profile ,src)]
[(goto ,l) `(goto ,l)]
[,rhs ; alloc, inline, foreign-call
(let ([tmp (make-tmp 't)])
`(seq
(set! ,tmp ,rhs)
,(k `(mvcall ,(make-info-call (info-call-src info) (info-call-sexpr info) #f #f #f) #f ,consumer ,tmp ()))))]
[else ; set! & mvset
`(seq ,e ,(k `(mvcall ,(make-info-call (info-call-src info) (info-call-sexpr info) #f #f #f) #f ,consumer ,(%constant svoid) ())))])))))
(CaseLambdaClause : CaseLambdaClause (ir) -> CaseLambdaClause ()
[(clause (,x* ...) (,local0* ...) ,mcp ,interface ,body)
(fluid-let ([local* local0*])
(let ([body (Expr body)])
(safe-assert (nodups x* local*))
`(clause (,x* ...) (,local* ...) ,mcp ,interface
,body)))])
(Rhs : Rhs (ir) -> Rhs ()
[(call ,info ,mdcl ,[t0?] ,[t1*] ...) `(mvcall ,info ,mdcl ,t0? ,t1* ... ())])
(Expr : Expr (ir) -> Expr ()
[(mvcall ,info ,[e] ,[t]) (Mvcall info e t values)]
[(set! ,[lvalue] (mvcall ,info ,[e] ,[t]))
(Mvcall info e t (lambda (rhs) `(set! ,lvalue ,rhs)))]
[(mvlet ,[e] ((,x** ...) ,interface* ,body*) ...)
(let ([label* (map (lambda (x) (make-local-label 'mv)) body*)])
(define Pvalues
(lambda (info t*)
(define build-assignments
(lambda (x* t* body)
(fold-left
(lambda (body x t)
; okay to drop t since it's a triv
(if (uvar-referenced? x)
`(seq (set! ,x ,t) ,body)
body))
body x* t*)))
(find-matching-clause (length t*) x** interface* label*
(lambda (x* label)
; mark label referenced so it won't be discarded
(local-label-iteration-set! label #t)
(build-assignments x* t* `(goto ,label)))
(lambda (nfixed x* label)
; mark label referenced so it won't be discarded
(local-label-iteration-set! label #t)
(let ([xfixed* (list-head x* nfixed)]
[tfixed* (list-head t* nfixed)]
[xvar (list-ref x* nfixed)]
[tvar* (list-tail t* nfixed)])
; the args are all trivs, otherwise this code would not properly build the rest
; list after all of the arguments have been evaluated (and it couldn't suppress
; the list creation when xvar is unreferenced)
(build-assignments xfixed* tfixed*
(if (uvar-referenced? xvar)
`(seq
,(if (null? tvar*)
`(set! ,xvar ,(%constant snil))
(let ([t (make-tmp 't)])
`(seq
(set! ,t ,(%constant-alloc type-pair
(fx* (constant size-pair) (length tvar*))))
,(let f ([tvar* tvar*] [offset 0])
(let ([tvar (car tvar*)] [tvar* (cdr tvar*)])
`(seq
(set! ,(%mref ,t
,(fx+ (constant pair-car-disp) offset))
,tvar)
,(if (null? tvar*)
`(seq
(set! ,(%mref ,t
,(fx+ (constant pair-cdr-disp) offset))
,(%constant snil))
(set! ,xvar ,t))
(let ([next-offset (fx+ offset (constant size-pair))])
`(seq
(set! ,(%mref ,t
,(fx+ (constant pair-cdr-disp) offset))
,(%lea ,t next-offset))
,(f tvar* next-offset))))))))))
(goto ,label))
`(goto ,label)))))
(lambda ()
(let ([src (and info (info-call-src info))] [sexpr (and info (info-call-sexpr info))])
`(seq
(pariah)
(mvcall ,(make-info-call src sexpr #f #t #t) #f
(literal ,(make-info-literal #t 'object '$oops (constant symbol-value-disp)))
,(%constant sfalse)
(literal ,(make-info-literal #f 'object "incorrect number of values received in multiple value context" 0))
())))))))
(let ([e (nanopass-case (L10.5 Expr) e
[,t (Pvalues #f (list t))]
[(values ,info ,t* ...) (Pvalues info t*)]
[(mvcall ,info ,mdcl ,t0? ,t1* ... (,t* ...))
(for-each (lambda (l) (local-label-iteration-set! l #t)) label*)
`(mvset ,info (,mdcl ,t0? ,t1* ...) (,t* ...) ((,x** ...) ,interface* ,label*) ...)]
[(if ,e0 ,[e1] ,[e2]) `(if ,e0 ,e1 ,e2)]
[(seq ,e0 ,[e1]) `(seq ,e0 ,e1)]
[(label ,l ,[body]) `(label ,l ,body)]
[(profile ,src) `(profile ,src)]
[(trap-check ,ioc ,[body]) `(trap-check ,ioc ,body)]
[(overflow-check ,[body]) `(overflow-check ,body)]
[(pariah) `(pariah)]
[(mlabel ,[e] (,l* ,[e*]) ...) `(mlabel ,e (,l* ,e*) ...)]
[(goto ,l) `(goto ,l)]
[,rhs ; alloc, inline, foreign-call
(let ([tmp (make-tmp 't)])
`(seq
(set! ,tmp ,rhs)
,(Pvalues #f (list tmp))))]
[else ; set! & mvset
`(seq ,e ,(Pvalues #f (list (%constant svoid))))])])
(let-values ([(label* body*)
(let loop ([label* label*] [body* body*] [rlabel* '()] [rbody* '()])
(if (null? label*)
(values rlabel* rbody*)
(let* ([label (car label*)])
(if (local-label-iteration label)
(begin
(local-label-iteration-set! label #f)
(loop (cdr label*) (cdr body*)
(cons label rlabel*)
(cons (Expr (car body*)) rbody*)))
(loop (cdr label*) (cdr body*) rlabel* rbody*)))))])
`(mlabel ,e (,label* ,body*) ...))))]))
(define-pass np-normalize-context : L10.5 (ir) -> L11 ()
(definitions
(define local*)
(define make-tmp
(lambda (x)
(import (only np-languages make-tmp))
(let ([x (make-tmp x)])
(set! local* (cons x local*))
x)))
(define rhs-inline
(lambda (lvalue info prim t*)
(with-output-language (L11 Effect)
(cond
[(pred-primitive? prim)
`(if (inline ,info ,prim ,t* ...)
(set! ,lvalue ,(%constant strue))
(set! ,lvalue ,(%constant sfalse)))]
[(effect-primitive? prim)
`(seq
(inline ,info ,prim ,t* ...)
(set! ,lvalue ,(%constant svoid)))]
[(not (value-primitive? prim)) ($oops who "unrecognized prim ~s" prim)]
[else `(set! ,lvalue (inline ,info ,prim ,t* ...))])))))
(CaseLambdaClause : CaseLambdaClause (ir) -> CaseLambdaClause ()
[(clause (,x* ...) (,local0* ...) ,mcp ,interface ,body)
(fluid-let ([local* local0*])
(let ([tlbody (Tail body)])
(safe-assert (nodups x* local*))
`(clause (,x* ...) (,local* ...) ,mcp ,interface ,tlbody)))])
(Pred : Expr (ir) -> Pred ()
(definitions
(define-syntax predicafy-triv
(syntax-rules ()
[(_ ?t)
`(if ,(%inline eq? ?t (immediate ,(constant sfalse)))
(false)
(true))]))
(define-syntax predicafy-rhs
(syntax-rules ()
[(_ ?rhs)
(let ([t (make-tmp 't)])
`(seq
(set! ,t ?rhs)
,(predicafy-triv ,t)))])))
[,x (predicafy-triv ,x)]
[(mref ,x1 ,x2 ,imm) (predicafy-triv ,(%mref ,x1 ,x2 ,imm))]
[(literal ,info)
(if (info-literal-indirect? info)
(predicafy-triv (literal ,info))
(if (and (eq? (info-literal-type info) 'object)
(eq? (info-literal-addr info) #f)
(eqv? (info-literal-offset info) 0))
`(false)
`(true)))]
[(immediate ,imm) (if (eqv? imm (constant sfalse)) `(false) `(true))]
[(label-ref ,l ,offset) `(true)]
[(mvcall ,info ,mdcl ,[t0?] ,[t1] ... (,[t*] ...))
(if (and (info-call-error? info) (fx< (debug-level) 2))
`(seq (tail (mvcall ,info ,mdcl ,t0? ,t1 ... (,t* ...))) (true))
(predicafy-rhs (mvcall ,info ,mdcl ,t0? ,t1 ... (,t* ...))))]
[(foreign-call ,info ,[t0] ,[t1] ...) (predicafy-rhs (foreign-call ,info ,t0 ,t1 ...))]
[(label ,l ,[pbody]) `(seq (label ,l) ,pbody)]
[(trap-check ,ioc ,[pbody]) `(seq (trap-check ,ioc) ,pbody)]
[(overflow-check ,[pbody]) `(seq (overflow-check) ,pbody)]
[(profile ,src) `(seq (profile ,src) (true))]
[(pariah) `(seq (pariah) (true))]
[(alloc ,info ,t) `(true)]
[(inline ,info ,prim ,[t*] ...)
(guard (value-primitive? prim))
(predicafy-rhs (inline ,info ,prim ,t* ...))]
[(inline ,info ,prim ,[t*] ...)
(guard (effect-primitive? prim))
`(seq (inline ,info ,prim ,t* ...) (true))]
[(inline ,info ,prim ,t* ...)
(guard (not (pred-primitive? prim)))
($oops who "unrecognized prim ~s" prim)]
[(set! ,[lvalue] (inline ,info ,prim ,[t*] ...))
`(seq ,(rhs-inline lvalue info prim t*) (true))]
[(set! ,[lvalue] (mvcall ,info ,mdcl ,[t0?] ,[t1] ... (,[t*] ...)))
(guard (info-call-error? info) (fx< (debug-level) 2))
(%seq
(tail (mvcall ,info ,mdcl ,t0? ,t1 ... (,t* ...)))
(true))]
[(set! ,[lvalue] ,[rhs]) `(seq (set! ,lvalue ,rhs) (true))]
[(mvset ,info (,mdcl ,[t0?] ,[t1] ...) (,[t*] ...) ((,x** ...) ,interface* ,l*) ...)
`(seq
(mvset ,info (,mdcl ,t0? ,t1 ...) (,t* ...) ((,x** ...) ,interface* ,l*) ...)
(true))]
[(values ,info ,t) (Pred t)]
[(values ,info ,t* ...)
`(seq (mvcall ,(make-info-call (info-call-src info) (info-call-sexpr info) #f #t #t) #f
(literal ,(make-info-literal #t 'object '$oops (constant symbol-value-disp)))
,(%constant sfalse)
(literal ,(make-info-literal #f 'object
(format "returned ~r values to single value return context"
(length t*)) 0))
())
(true))])
(Effect : Expr (ir) -> Effect ()
[,x `(nop)]
[(mref ,x1 ,x2 ,imm) `(nop)]
[(literal ,info) `(nop)]
[(immediate ,imm) `(nop)]
[(label-ref ,l ,offset) `(nop)]
[(alloc ,info ,t) `(nop)]
[(inline ,info ,prim ,[t*] ...)
(cond
[(primitive-pure? prim) `(nop)] ; TODO: do we get any of these when cp0 is run?
[(value-primitive? prim)
`(set! ,(make-tmp 'waste) (inline ,info ,prim ,t* ...))]
[(pred-primitive? prim)
`(if (inline ,info ,prim ,t* ...) (nop) (nop))]
[else `(inline ,info ,prim ,t* ...)])]
[(set! ,[lvalue] (inline ,info ,prim ,[t*] ...))
(rhs-inline lvalue info prim t*)]
[(set! ,[lvalue] (mvcall ,info ,mdcl ,[t0?] ,[t1] ... (,[t*] ...)))
(guard (info-call-error? info) (fx< (debug-level) 2))
`(tail (mvcall ,info ,mdcl ,t0? ,t1 ... (,t* ...)))]
[(label ,l ,[ebody]) `(seq (label ,l) ,ebody)]
[(trap-check ,ioc ,[ebody]) `(seq (trap-check ,ioc) ,ebody)]
[(overflow-check ,[ebody]) `(seq (overflow-check) ,ebody)]
[(profile ,src) `(profile ,src)]
[(pariah) `(pariah)]
[(mvcall ,info ,mdcl ,[t0?] ,[t1] ... (,[t*] ...))
(guard (info-call-error? info) (fx< (debug-level) 2))
`(tail (mvcall ,info ,mdcl ,t0? ,t1 ... (,t* ...)))]
[(mlabel ,[e] (,l* ,[e*]) ...)
(let ([join (make-local-label 'mjoin)])
`(seq
,(let f ([e e] [l* l*] [e* e*])
(if (null? l*)
e
(%seq ,e (goto ,join)
,(f `(seq (label ,(car l*)) ,(car e*)) (cdr l*) (cdr e*)))))
(label ,join)))]
[(values ,info ,t* ...) `(nop)])
(Tail : Expr (ir) -> Tail ()
[(inline ,info ,prim ,[t*] ...)
(guard (pred-primitive? prim))
`(if (inline ,info ,prim ,t* ...)
,(%constant strue)
,(%constant sfalse))]
[(inline ,info ,prim ,[t*] ...)
(guard (effect-primitive? prim))
`(seq (inline ,info ,prim ,t* ...) ,(%constant svoid))]
[(inline ,info ,prim ,t* ...)
(guard (not (value-primitive? prim)))
($oops who "unrecognized prim ~s" prim)]
[(set! ,[lvalue] (inline ,info ,prim ,[t*] ...))
`(seq ,(rhs-inline lvalue info prim t*) ,(%constant svoid))]
[(set! ,[lvalue] (mvcall ,info ,mdcl ,[t0?] ,[t1] ... (,[t*] ...)))
(guard (info-call-error? info) (fx< (debug-level) 2))
`(mvcall ,info ,mdcl ,t0? ,t1 ... (,t* ...))]
[(set! ,[lvalue] ,[rhs]) `(seq (set! ,lvalue ,rhs) ,(%constant svoid))]
[(mvset ,info (,mdcl ,[t0?] ,[t1] ...) (,[t*] ...) ((,x** ...) ,interface* ,l*) ...)
`(seq
(mvset ,info (,mdcl ,t0? ,t1 ...) (,t* ...) ((,x** ...) ,interface* ,l*) ...)
,(%constant svoid))]
[(label ,l ,[tlbody]) `(seq (label ,l) ,tlbody)]
[(trap-check ,ioc ,[tlbody]) `(seq (trap-check ,ioc) ,tlbody)]
[(overflow-check ,[tlbody]) `(seq (overflow-check) ,tlbody)]
[(profile ,src) `(seq (profile ,src) ,(%constant svoid))]
[(pariah) `(seq (pariah) ,(%constant svoid))]
[(mlabel ,[tl] (,l* ,[tl*]) ...)
(let f ([tl tl] [l* l*] [tl* tl*])
(if (null? l*)
tl
`(seq
(tail ,tl)
,(f `(seq (label ,(car l*)) ,(car tl*)) (cdr l*) (cdr tl*)))))]))
(define-pass np-insert-trap-check : L11 (ir) -> L11.5 ()
(Effect : Effect (ir) -> Effect ()
[(trap-check ,ioc)
`(seq
(set! ,(ref-reg %trap) ,(%inline -/eq ,(ref-reg %trap) (immediate 1)))
(if (inline ,(make-info-condition-code 'eq? #f #t) ,%condition-code)
,(%seq
(pariah)
(mvcall ,(make-info-call #f #f #f #t #f) #f
(literal ,(make-info-literal #f 'library
(if ioc
(lookup-does-not-expect-headroom-libspec event)
(lookup-libspec event))
0))
()))
(nop)))]))
(define-pass np-flatten-case-lambda : L11.5 (ir) -> L12 ()
(definitions
(define Ldoargerr (make-Ldoargerr))
(define Ldomvleterr (make-Ldomvleterr))
(define flatten-clauses
(lambda (info cl* dcl*)
(let ([libspec (info-lambda-libspec info)])
(with-output-language (L12 Tail)
(when libspec
(safe-assert (equal? (info-lambda-interface* info) (list (libspec-interface libspec))))
(if (null? (info-lambda-fv* info))
(when (libspec-closure? libspec)
($oops who "libspec claims closure needed, but no free variables for ~s" (libspec-name libspec)))
(unless (libspec-closure? libspec)
($oops who "libspec claims no closure needed, but has free variables ~s for ~s" (info-lambda-fv* info) (libspec-name libspec)))))
(if (or (info-lambda-well-known? info) libspec)
(let loop ([cl* cl*] [dcl* dcl*] [local* '()] [tlbody #f])
(if (null? cl*)
(values local* (or tlbody (%constant svoid)))
(if (or libspec (direct-call-label-referenced (car dcl*)))
(nanopass-case (L11.5 CaseLambdaClause) (car cl*)
[(clause (,x* ...) (,local1* ...) ,mcp ,interface ,tlbody1)
(loop (cdr cl*) (cdr dcl*) (maybe-cons mcp (append x* local1* local*))
(let ([tlbody1 `(entry-point (,x* ...) ,(car dcl*) ,mcp ,(Tail tlbody1))])
(if tlbody
`(seq (tail ,tlbody) ,tlbody1)
tlbody1)))])
(loop (cdr cl*) (cdr dcl*) local* tlbody))))
(let f ([cl* cl*] [dcl* dcl*])
(if (null? cl*)
(values '() `(seq (pariah) (goto ,Ldoargerr)))
(nanopass-case (L11.5 CaseLambdaClause) (car cl*)
[(clause (,x* ...) (,local* ...) ,mcp ,interface ,tlbody)
(let ([tlbody `(entry-point (,x* ...) ,(car dcl*) ,mcp ,(Tail tlbody))])
(if (fx< interface 0)
(let ([fixed-args (lognot interface)])
(let ([tlbody (if (uvar-referenced? (list-ref x* fixed-args))
`(seq (do-rest ,fixed-args) ,tlbody)
tlbody)])
(if (fx= fixed-args 0)
(values (maybe-cons mcp (append x* local*)) tlbody)
(let-values ([(next-local* next-tlbody) (f (cdr cl*) (cdr dcl*))])
(values
(maybe-cons mcp (append x* local* next-local*))
`(if ,(%inline u< ,%ac0
(immediate ,fixed-args))
,next-tlbody
,tlbody))))))
(let-values ([(next-local* next-tlbody) (f (cdr cl*) (cdr dcl*))])
(values
(maybe-cons mcp (append x* local* next-local*))
`(if ,(%inline eq? ,%ac0
(immediate ,interface))
,tlbody
,next-tlbody)))))]))))))))
(define flatten-mvclauses
(lambda (x** interface* l*)
(with-output-language (L12 Effect)
(if (null? x**)
(%seq
(pariah)
;; mverror point ensures that the call's return address
;; is in sfp[0], so the caller's frame is still
;; on the stack for error reporting and debugging
(mverror-point)
(goto ,Ldomvleterr))
(let ([x* (car x**)] [interface (car interface*)] [l (car l*)])
(let ([ebody `(mventry-point (,x* ...) ,l)])
(if (fx< interface 0)
(let ([fixed-args (lognot interface)])
(let ([ebody (if (uvar-referenced? (list-ref x* fixed-args))
`(seq (do-rest ,fixed-args) ,ebody)
ebody)])
(if (fx= fixed-args 0)
ebody
(let ([next-ebody (flatten-mvclauses (cdr x**) (cdr interface*) (cdr l*))])
`(if ,(%inline u< ,%ac0
(immediate ,fixed-args))
,next-ebody
,ebody)))))
(let ([next-ebody (flatten-mvclauses (cdr x**) (cdr interface*) (cdr l*))])
`(if ,(%inline eq? ,%ac0
(immediate ,interface))
,ebody
,next-ebody))))))))))
(CaseLambdaExpr : CaseLambdaExpr (ir) -> CaseLambdaExpr ()
[(case-lambda ,info ,cl* ...)
(let-values ([(local* tlbody) (flatten-clauses info cl* (info-lambda-dcl* info))])
(safe-assert (nodups local*))
(info-lambda-dcl*-set! info (filter direct-call-label-referenced (info-lambda-dcl* info)))
`(lambda ,info (,local* ...) ,tlbody))])
(Tail : Tail (ir) -> Tail ())
(Effect : Effect (ir) -> Effect ()
[(mvset ,info (,mdcl ,[t0?] ,[t1] ...) (,[t*] ...) ((,x** ...) ,interface* ,l*) ...)
`(mvset ,info (,mdcl ,t0? ,t1 ...) (,t* ...) ((,x** ...) ...)
,(flatten-mvclauses x** interface* l*))]))
(define-pass np-impose-calling-conventions : L12 (ir) -> L13 ()
(definitions
(import (only asm-module asm-foreign-call asm-foreign-callable asm-enter))
(define newframe-info-for-mventry-point)
(define label-for-mverror-point)
(define Lcall-error (make-Lcall-error))
(define dcl*)
(define local*)
(define max-fv)
(define le-label)
(define-$type-check (L13 Pred))
(define make-tmp
(lambda (x)
(import (only np-languages make-tmp))
(let ([x (make-tmp x)])
(set! local* (cons x local*))
x)))
(define set-formal-registers!
(lambda (x*)
(let do-reg ([x* x*] [reg* arg-registers])
(if (or (null? x*) (null? reg*))
x*
(begin
(uvar-location-set! (car x*) (car reg*))
(do-reg (cdr x*) (cdr reg*)))))))
(define get-arg-regs
(lambda (t*)
(let f ([t* t*] [reg* arg-registers])
(if (or (null? t*) (null? reg*))
(values '() '() t*)
(let ([reg (car reg*)])
(let-values ([(reg* reg-t* frame-t*) (f (cdr t*) (cdr reg*))])
(values (cons reg reg*) (cons (car t*) reg-t*) frame-t*)))))))
(module (build-tail-call build-nontail-call build-mv-return)
(define symref?
(lambda (info)
(and (info-literal-indirect? info)
(eq? (info-literal-type info) 'object)
(let ([x (info-literal-addr info)])
(and (symbol? x)
(eqv? (info-literal-offset info) (constant symbol-value-disp))
x)))))
(define libref?
(lambda (info)
(and (not (info-literal-indirect? info))
(eq? (info-literal-type info) 'library)
(let ([x (info-literal-addr info)])
(and (libspec? x)
(eqv? (info-literal-offset info) 0)
x)))))
(define build-call
(with-output-language (L13 Tail)
(case-lambda
[(t rpl reg* fv* maybe-info mdcl)
(build-call t #f rpl reg* fv* maybe-info mdcl #f)]
[(t cploc rpl reg* fv* maybe-info mdcl consumer?)
(let ()
(define set-return-address
(lambda (tl)
(if rpl
(%seq (set! ,%ref-ret (label-ref ,rpl ,(constant size-rp-header))) ,tl)
(meta-cond
[(real-register? '%ret) (%seq (set! ,%ret ,(get-fv 0)) ,tl)]
[else tl]))))
(define finish-call
(lambda (argcnt? cp? t)
(safe-assert (not (eq? t (get-fv 0))))
(let ([live-reg* (reg-cons* %ret (if cp? (reg-cons* %cp reg*) reg*))]
[live-fv* (meta-cond
[(real-register? '%ret) fv*]
[else (cons (get-fv 0) fv*)])])
(if consumer?
`(jump ,t (,%ac0 ,live-reg* ... ,live-fv* ...))
(if argcnt?
`(seq
(set! ,%ac0 (immediate ,(fx+ (length reg*) (length fv*))))
(jump ,t (,%ac0 ,live-reg* ... ,live-fv* ...)))
`(jump ,t (,live-reg* ... ,live-fv* ...)))))))
(define direct-call
(lambda ()
(if rpl
`(joto ,mdcl (,fv* ...))
`(goto ,mdcl))))
(define normal-call
(lambda ()
(define cploc-is-cp?
(lambda ()
; cploc must be #f, an nfv, %cp or an mref tc[cp]
(meta-cond
[(real-register? '%cp) (eq? cploc %cp)]
[else (and cploc (not (var? cploc)))])))
(define-syntax set-cp
(syntax-rules ()
[(_ lhs rhs ?tl)
(let ([tl `?tl])
(if (cploc-is-cp?)
tl
`(seq (set! lhs rhs) ,tl)))]))
(define insert-procedure-check
(lambda (reg tlbody)
(if (and maybe-info (info-call-check? maybe-info))
`(if ,(%type-check mask-closure type-closure ,reg)
,tlbody
(seq (pariah) (goto ,Lcall-error)))
tlbody)))
(if mdcl
(set-cp ,(ref-reg %cp) ,(or cploc (Triv t))
,(set-return-address
(if (memq mdcl dcl*)
(direct-call)
(finish-call #f ; don't set the argcount, since it doesn't need to be checked
#t (in-context Triv `(label-ref ,mdcl 0))))))
(meta-cond
[(real-register? '%cp)
(set-cp ,%cp ,(or cploc (Triv t))
,(set-return-address ; must be set before potential jump to call-error
(insert-procedure-check %cp
(finish-call #t #t
(in-context Triv
(%mref ,%cp ,(constant closure-code-disp)))))))]
[else
`(seq
(set! ,%xp ,(or cploc (Triv t)))
,(set-cp ,(ref-reg %cp) ,%xp
,(set-return-address ; must be set before potential jump to call-error
(insert-procedure-check %xp
(finish-call #t #t
(in-context Triv
(%mref ,%xp ,(constant closure-code-disp))))))))]))))
(if (not t)
(set-return-address
(if (memq mdcl dcl*)
(direct-call)
(finish-call #f #f (in-context Triv `(label-ref ,mdcl 0)))))
(nanopass-case (L12 Triv) t
; if the expression in the cp position #f, and we have an mdcl, this is
; a hackish workaround for not having a good way to express maybe-Expr
[(literal ,info)
(cond
[(symref? info) =>
; okay to do pvalue call even if this is a consumer call since only primrefs
; come through as consumer symrefs
(lambda (sym)
(%seq
(set! ,%xp (literal ,(make-info-literal #f 'object sym 0)))
(set! ,(ref-reg %cp) ,(%mref ,%xp ,(constant symbol-value-disp)))
,(set-return-address
(finish-call #t #t
(in-context Triv
(%mref ,%xp ,(constant symbol-pvalue-disp)))))))]
[(libref? info) =>
(lambda (libspec)
(define set-cp
(lambda (tlbody)
(if (libspec-closure? libspec)
`(seq
(set! ,(ref-reg %cp) (literal ,info))
,tlbody)
tlbody)))
(set-cp
(set-return-address
(finish-call #f (libspec-closure? libspec)
(in-context Triv `(literal ,(make-info-literal #f 'library-code libspec (constant code-data-disp))))))))]
[else (normal-call)])]
[else (normal-call)])))])))
(define build-consumer-call
(lambda (tc cnfv rpl)
; haven't a clue which argument registers are live, so list 'em all.
; also haven't a clue which frame variables are live. really need a
; way to list all of them as well, but we count on there being enough
; other registers (e.g., ac0, xp) to get us from the producer return
; point to the consumer jump point.
(build-call tc cnfv rpl arg-registers '() #f #f #t)))
(define prepare-for-consumer-call
(lambda (mrvl)
(with-output-language (L13 Effect)
(let ([loc0 (if (null? arg-registers)
(in-context Lvalue (%mref ,%sfp 0))
(car arg-registers))])
(%seq
(set! ,loc0 ,%ac0)
(set! ,%ac0 (immediate 1))
(label ,mrvl))))))
(define store-cp?
(lambda (t)
(nanopass-case (L12 Triv) t
[(literal ,info) #f]
[else #t])))
(define build-nontail-call
(lambda (info mdcl t0 t1* tc* nfv** mrvl prepare-for-consumer? build-postlude)
(let-values ([(reg* reg-t* frame-t*) (get-arg-regs t1*)])
(let ([nfv* (fold-left (lambda (ls x) (cons (make-tmp 'nfv) ls)) '() frame-t*)]
[cnfv* (fold-right (lambda (x ls) (cons (and (store-cp? x) (make-tmp 'cnfv)) ls)) '() tc*)]
[rpl* (map (lambda (tc) (make-local-label 'rpl)) tc*)]
[rpl (make-local-label 'rpl)])
(let ([newframe-info (make-info-newframe (info-call-src info) (info-call-sexpr info) (reverse (remq #f cnfv*)) nfv* nfv**)])
(with-output-language (L13 Effect)
(define build-return-point
(lambda (rpl mrvl cnfv* call)
(%seq (tail ,call) (label ,rpl) (return-point ,newframe-info ,rpl ,mrvl (,(remq #f cnfv*) ...)))))
(define set-locs
(lambda (loc* t* ebody)
(fold-right
(lambda (loc t ebody)
(if loc
`(seq (set! ,loc ,(Triv t)) ,ebody)
ebody))
ebody loc* t*)))
((lambda (e) (if (info-call-pariah? info) (%seq (pariah) ,e) e))
(set-locs cnfv* tc*
(set-locs nfv* frame-t*
(set-locs reg* reg-t*
(%seq
(new-frame ,newframe-info ,rpl* ... ,rpl)
,((lambda (e)
(if prepare-for-consumer?
`(seq ,e ,(prepare-for-consumer-call mrvl))
e))
(if (null? tc*)
(build-return-point rpl mrvl cnfv*
(build-call t0 rpl reg* nfv* info mdcl))
(let ([this-mrvl (make-local-label 'mrvl)])
`(seq
,(let ([rpl (car rpl*)])
(build-return-point rpl this-mrvl cnfv*
(build-call t0 rpl reg* nfv* info mdcl)))
,(let f ([tc* tc*] [cnfv* cnfv*] [rpl* rpl*] [this-mrvl this-mrvl])
`(seq
,(prepare-for-consumer-call this-mrvl)
,(let ([tc (car tc*)] [tc* (cdr tc*)] [rpl* (cdr rpl*)] [cnfv (car cnfv*)] [cnfv* (cdr cnfv*)])
(if (null? tc*)
(build-return-point rpl mrvl cnfv*
(build-consumer-call tc cnfv rpl))
(let ([this-mrvl (make-local-label 'mrvl)])
`(seq
,(let ([rpl (car rpl*)])
(build-return-point rpl this-mrvl cnfv*
(build-consumer-call tc cnfv rpl)))
,(f tc* cnfv* rpl* this-mrvl)))))))))))
,(build-postlude newframe-info rpl))))))))))))
; NB: combine
(define build-nontail-call-for-tail-call-with-consumers
(lambda (info mdcl t0 t1* tc* nfv** mrvl prepare-for-consumer? build-postlude)
(let-values ([(reg* reg-t* frame-t*) (get-arg-regs t1*)])
(let ([nfv* (fold-left (lambda (ls x) (cons (make-tmp 'nfv) ls)) '() frame-t*)]
[cnfv* (fold-right (lambda (x ls) (cons (and (store-cp? x) (make-tmp 'cnfv)) ls)) '() tc*)]
[rpl* (map (lambda (tc) (make-local-label 'rpl)) (cdr tc*))]
[rpl (make-local-label 'rpl)])
(let ([newframe-info (make-info-newframe (info-call-src info) (info-call-sexpr info) (reverse (remq #f cnfv*)) nfv* nfv**)])
(with-output-language (L13 Effect)
(define build-return-point
(lambda (rpl mrvl cnfv* call)
(%seq (tail ,call) (label ,rpl) (return-point ,newframe-info ,rpl ,mrvl (,(remq #f cnfv*) ...)))))
(define set-locs
(lambda (loc* t* ebody)
(fold-right
(lambda (loc t ebody)
(if loc
`(seq (set! ,loc ,(Triv t)) ,ebody)
ebody))
ebody loc* t*)))
((lambda (e) (if (info-call-pariah? info) (%seq (pariah) ,e) e))
(set-locs cnfv* tc*
(set-locs nfv* frame-t*
(set-locs reg* reg-t*
(%seq
(new-frame ,newframe-info ,rpl* ... ,rpl)
,((lambda (e)
(if prepare-for-consumer?
`(seq ,e ,(prepare-for-consumer-call mrvl))
e))
(if (null? (cdr tc*))
(build-return-point rpl mrvl cnfv*
(build-call t0 rpl reg* nfv* info mdcl))
(let ([this-mrvl (make-local-label 'mrvl)])
`(seq
,(let ([rpl (car rpl*)])
(build-return-point rpl this-mrvl cnfv*
(build-call t0 rpl reg* nfv* info mdcl)))
,(let f ([tc* tc*] [cnfv* cnfv*] [rpl* rpl*] [this-mrvl this-mrvl])
`(seq
,(prepare-for-consumer-call this-mrvl)
,(let ([tc (car tc*)] [tc* (cdr tc*)] [rpl* (cdr rpl*)] [cnfv (car cnfv*)] [cnfv* (cdr cnfv*)])
(if (null? (cdr tc*))
(build-return-point rpl mrvl cnfv*
(build-consumer-call tc cnfv rpl))
(let ([this-mrvl (make-local-label 'mrvl)])
`(seq
,(let ([rpl (car rpl*)])
(build-return-point rpl this-mrvl cnfv*
(build-consumer-call tc cnfv rpl)))
,(f tc* cnfv* rpl* this-mrvl)))))))))))
,(build-postlude newframe-info (car (last-pair cnfv*))))))))))))))
(module (build-tail-call build-mv-return)
(with-output-language (L13 Tail)
(define set-locs
(lambda (loc* t* tlbody)
(fold-right
(lambda (loc t tlbody)
; omit set! for tail-frame optimization
(if (and (fv? loc) (uvar? t) (eq? (uvar-location t) loc))
tlbody
`(seq (set! ,loc ,(Triv t)) ,tlbody)))
tlbody loc* t*)))
(define build-shift-args
(lambda (info)
(with-output-language (L13 Effect)
(let ([Ltop (make-local-label 'Ltop)])
`(seq
(set! ,%ts ,(%inline - ,%ac0 (immediate ,(length arg-registers))))
(if ,(%inline <= ,%ts (immediate 0))
(nop)
,(%seq
(set! ,%xp ,(%inline + ,%sfp ,(%constant ptr-bytes)))
(set! ,%ts ,(%inline sll ,%ts ,(%constant log2-ptr-bytes)))
(set! ,%ts ,(%inline + ,%ts ,%xp))
(label ,Ltop)
(shift-arg ,%xp 0 ,info)
(set! ,%xp ,(%inline + ,%xp ,(%constant ptr-bytes)))
(if ,(%inline eq? ,%xp ,%ts)
(nop)
(goto ,Ltop)))))))))
(define build-tail-call
(lambda (info mdcl t0 t1* tc*)
(if (null? tc*)
(let-values ([(reg* reg-t* frame-t*) (get-arg-regs t1*)])
(let ([fv* (let f ([frame-t* frame-t*] [i 0])
(if (null? frame-t*)
(begin (set! max-fv (fxmax max-fv i)) '())
(let ([i (fx+ i 1)])
(cons (get-fv i) (f (cdr frame-t*) i)))))])
(set-locs fv* frame-t*
(set-locs reg* reg-t*
(build-call t0 #f reg* fv* info mdcl)))))
(let ([tc (car (last-pair tc*))]
[mrvl (make-local-label 'mrvl)])
(if (store-cp? tc)
(%seq
,(build-nontail-call-for-tail-call-with-consumers info mdcl t0 t1* tc* '() mrvl #t
(lambda (newframe-info cnfv)
(safe-assert cnfv)
(%seq
(remove-frame ,newframe-info)
(restore-local-saves ,newframe-info)
(set! ,(ref-reg %cp) ,cnfv)
,(build-shift-args newframe-info))))
,(build-consumer-call tc (in-context Triv (ref-reg %cp)) #f))
(let ([tc* (list-head tc* (fx- (length tc*) 1))])
`(seq
,(build-nontail-call info mdcl t0 t1* tc* '() mrvl #t
(lambda (newframe-info rpl)
(%seq
(remove-frame ,newframe-info)
(restore-local-saves ,newframe-info)
,(build-shift-args newframe-info))))
,(build-consumer-call tc #f #f))))))))
(define build-mv-return
(lambda (t*)
(let-values ([(reg* reg-t* frame-t*) (get-arg-regs t*)])
(let ([fv* (let f ([frame-t* frame-t*] [i 0])
(if (null? frame-t*)
(begin (set! max-fv (fxmax max-fv i)) '())
(let ([i (fx+ i 1)])
(cons (get-fv i) (f (cdr frame-t*) i)))))])
(set-locs fv* frame-t*
(set-locs reg* reg-t*
`(seq
(set! ,%ac0 (immediate ,(length t*)))
,(meta-cond
[(real-register? '%ret)
(%seq
; must leave RA in %ret for values-error
(set! ,%ret ,(get-fv 0))
(jump ,(%mref ,%ret ,(constant return-address-mv-return-address-disp))
(,%ac0 ,%ret ,reg* ... ,fv* ...)))]
[else
(%seq
(set! ,%xp ,(get-fv 0))
(jump ,(%mref ,%xp ,(constant return-address-mv-return-address-disp))
(,%ac0 ,reg* ... ,(get-fv 0) ,fv* ...)))])))))))))))
(define-syntax do-return
(lambda (x)
(syntax-case x ()
[(k retval)
(with-implicit (k quasiquote)
#'`(seq
(set! ,%ac0 retval)
(jump ,(get-fv 0) (,%ac0))))])))
(define Ref
(lambda (x)
(when (uvar? x) (uvar-referenced! x #t))
x))
(module (build-foreign-call build-fcallable)
(with-output-language (L13 Effect)
(define build-unfix
(lambda (t)
(in-context Rhs
(%inline sra ,t ,(%constant fixnum-offset)))))
(define build-fix
(lambda (t)
(in-context Rhs
(%inline sll ,t ,(%constant fixnum-offset)))))
(define Scheme->C
; ASSUMPTIONS: ac0, ac1, and xp are not C argument registers
(lambda (type toC t)
(define ptr->integer
(lambda (width t k)
(if (fx>= (constant fixnum-bits) width)
(k (build-unfix t))
`(seq
(set! ,%ac0 ,t)
(if ,(%type-check mask-fixnum type-fixnum ,%ac0)
,(if (fx> width (constant ptr-bits))
(%seq
(set! ,%ac0 ,(build-unfix %ac0))
(if ,(%inline < ,%ac0 (immediate 0))
,(k %ac0 (in-context Rhs `(immediate -1)))
,(k %ac0 (in-context Rhs `(immediate 0)))))
(k (build-unfix %ac0)))
(seq
(set! ,%ac0
(inline
,(case width
[(32) (intrinsic-info-asmlib dofargint32 #f)]
[(64) (intrinsic-info-asmlib dofargint64 #f)]
[else ($oops who "can't handle width ~s" width)])
,%asmlibcall))
,(if (fx> width (constant ptr-bits))
(k %ac0 (in-context Rhs (ref-reg %ac1)))
(k %ac0))))))))
(define build-u*
(lambda ()
(let ([x (make-tmp 't)])
`(seq
(set! ,x ,t)
(if ,(%inline eq? ,x ,(%constant sfalse))
,(toC (in-context Rhs `(immediate 0)))
,(toC (in-context Rhs (%lea ,x (constant bytevector-data-disp)))))))))
(define build-float
(lambda ()
(let ([x (make-tmp 't)])
`(seq
(set! ,x ,t)
,(toC x)))))
(nanopass-case (Ltype Type) type
[(fp-scheme-object) (toC t)]
[(fp-fixnum) (toC (build-unfix t))]
[(fp-u8*) (build-u*)]
[(fp-u16*) (build-u*)]
[(fp-u32*) (build-u*)]
[(fp-integer ,bits) (ptr->integer bits t toC)]
[(fp-unsigned ,bits) (ptr->integer bits t toC)]
[(fp-double-float) (build-float)]
[(fp-single-float) (build-float)]
[(fp-ftd ,ftd)
(let ([x (make-tmp 't)])
`(seq
(set! ,x ,t)
,(toC (in-context Rhs
(%mref ,x ,(constant record-data-disp))))))]
[(fp-ftd& ,ftd)
(let ([x (make-tmp 't)])
(%seq
(set! ,x ,t)
(set! ,x ,(%mref ,x ,(constant record-data-disp)))
,(toC x)))]
[else ($oops who "invalid parameter type specifier ~s" type)])))
(define Scheme->C-for-result
(lambda (type toC t)
(nanopass-case (Ltype Type) type
[(fp-void) (toC)]
[(fp-ftd& ,ftd)
;; pointer isn't received as a result, but instead passed
;; to the function as its first argument (or simulated as such)
(toC)]
[else
(Scheme->C type toC t)])))
(define C->Scheme
; ASSUMPTIONS: ac0, ac1, and xp are not C argument registers
(lambda (type fromC lvalue for-return?)
(define integer->ptr
; ac0 holds low 32-bits, ac1 holds high 32 bits, if needed
(lambda (width lvalue)
(if (fx>= (constant fixnum-bits) width)
`(set! ,lvalue ,(build-fix %ac0))
(let ([e1 (lambda (big)
(let ([x (make-tmp 't)])
(%seq
(set! ,x ,(build-fix %ac0))
(set! ,x ,(build-unfix x))
(if ,(%inline eq? ,x ,%ac0)
(set! ,lvalue ,(build-fix %ac0))
,big))))]
[e2 `(seq
(set! ,%ac0
(inline
,(case width
[(32) (intrinsic-info-asmlib dofretint32 #f)]
[(64) (intrinsic-info-asmlib dofretint64 #f)]
[else ($oops who "can't handle width ~s" width)])
,%asmlibcall))
(set! ,lvalue ,%ac0))])
(if (fx> width (constant ptr-bits))
(let ([Lbig (make-local-label 'Lbig)] [t-ac1 (make-tmp 't-ac1)])
(let ([t-ac1 (make-tmp 't-ac1)])
`(seq
; TODO: unnecessary if ac1 is not a pseudo register
(set! ,t-ac1 ,(ref-reg %ac1))
(if (if ,(%inline < ,%ac0 (immediate 0))
,(%inline eq? ,t-ac1 (immediate -1))
,(%inline eq? ,t-ac1 (immediate 0)))
,(e1 `(goto ,Lbig))
(seq (label ,Lbig) ,e2)))))
(e1 e2))))))
(define unsigned->ptr
; ac0 holds low 32-bits, ac1 holds high 32 bits, if needed
(lambda (width lvalue)
(if (fx>= (constant fixnum-bits) width)
`(set! ,lvalue ,(build-fix %ac0))
(let ([e1 (lambda (big)
`(if ,(%inline u<
,(%constant most-positive-fixnum)
,%ac0)
,big
(set! ,lvalue ,(build-fix %ac0))))]
[e2 `(seq
(set! ,%ac0
(inline
,(case width
[(32) (intrinsic-info-asmlib dofretuns32 #f)]
[(64) (intrinsic-info-asmlib dofretuns64 #f)]
[else ($oops who "can't handle width ~s" width)])
,%asmlibcall))
(set! ,lvalue ,%ac0))])
(if (fx> width (constant ptr-bits))
(let ([Lbig (make-local-label 'Lbig)] [t-ac1 (make-tmp 't-ac1)])
(let ([t-ac1 (make-tmp 't-ac1)])
`(seq
; TODO: unnecessary if ac1 is not a pseudo register
(set! ,t-ac1 ,(ref-reg %ac1))
(if ,(%inline eq? ,t-ac1 (immediate 0))
,(e1 `(goto ,Lbig))
(seq (label ,Lbig) ,e2)))))
(e1 e2))))))
(define (alloc-fptr ftd)
(%seq
(set! ,%xp
,(%constant-alloc type-typed-object (fx* (constant ptr-bytes) 2) #f))
(set!
,(%mref ,%xp ,(constant record-type-disp))
(literal ,(make-info-literal #f 'object ftd 0)))
(set! ,(%mref ,%xp ,(constant record-data-disp)) ,%ac0)
(set! ,lvalue ,%xp)))
(nanopass-case (Ltype Type) type
[(fp-void) `(set! ,lvalue ,(%constant svoid))]
[(fp-scheme-object) (fromC lvalue)]
[(fp-fixnum)
(%seq
,(fromC %ac0)
(set! ,%ac0 ,(build-fix %ac0))
(set! ,lvalue ,%ac0))]
[(fp-u8*)
(%seq
,(fromC %ac0)
(set! ,%xp (inline ,(intrinsic-info-asmlib dofretu8* #f) ,%asmlibcall))
(set! ,lvalue ,%xp))]
[(fp-u16*)
(%seq
,(fromC %ac0)
(set! ,%xp (inline ,(intrinsic-info-asmlib dofretu16* #f) ,%asmlibcall))
(set! ,lvalue ,%xp))]
[(fp-u32*)
(%seq
,(fromC %ac0)
(set! ,%xp (inline ,(intrinsic-info-asmlib dofretu32* #f) ,%asmlibcall))
(set! ,lvalue ,%xp))]
[(fp-integer ,bits)
`(seq
,(if (fx> bits (constant ptr-bits))
(fromC %ac0 (in-context Lvalue (ref-reg %ac1)))
(fromC %ac0))
,(integer->ptr bits lvalue))]
[(fp-unsigned ,bits)
`(seq
,(if (fx> bits (constant ptr-bits))
(fromC %ac0 (in-context Lvalue (ref-reg %ac1)))
(fromC %ac0))
,(unsigned->ptr bits lvalue))]
[(fp-double-float)
(%seq
(set! ,%xp ,(%constant-alloc type-flonum (constant size-flonum) for-return?))
,(fromC %xp)
(set! ,lvalue ,%xp))]
[(fp-single-float)
(%seq
(set! ,%xp ,(%constant-alloc type-flonum (constant size-flonum) for-return?))
,(fromC %xp)
(set! ,lvalue ,%xp))]
[(fp-ftd ,ftd)
(%seq
,(fromC %ac0) ; C integer return might be wiped out by alloc
,(alloc-fptr ftd))]
[(fp-ftd& ,ftd)
(%seq
,(fromC %ac0)
,(alloc-fptr ftd))]
[else ($oops who "invalid result type specifier ~s" type)]))))
(define (pick-Scall result-type)
(nanopass-case (Ltype Type) result-type
[(fp-void) (lookup-c-entry Scall-any-results)]
[else (lookup-c-entry Scall-one-result)]))
(define build-foreign-call
(with-output-language (L13 Effect)
(lambda (info t0 t1* maybe-lvalue new-frame?)
(let ([arg-type* (info-foreign-arg-type* info)]
[result-type (info-foreign-result-type info)])
(let ([e (let-values ([(allocate c-args ccall c-res deallocate) (asm-foreign-call info)])
; NB. allocate must save tc if not callee-save, and ccall
; (not deallocate) must restore tc if not callee-save
(%seq
,(allocate)
; cp must hold our closure or our code object. we choose code object
(set! ,(%tc-ref cp) (label-ref ,le-label 0))
,(with-saved-scheme-state
(in) ; save just the required registers, e.g., %sfp
(out %ac0 %ac1 %cp %xp %yp %ts %td scheme-args extra-regs)
(fold-left (lambda (e t1 arg-type c-arg) `(seq ,(Scheme->C arg-type c-arg t1) ,e))
(ccall t0) t1* arg-type* c-args))
,(let ([e (deallocate)])
(if maybe-lvalue
(nanopass-case (Ltype Type) result-type
[(fp-ftd& ,ftd)
;; Don't actually return a value, because the result
;; was instead installed in the first argument.
`(seq (set! ,maybe-lvalue ,(%constant svoid)) ,e)]
[else
`(seq ,(C->Scheme result-type c-res maybe-lvalue #t) ,e)])
e))))])
(if new-frame?
(sorry! who "can't handle nontail foreign calls")
e))))))
(define build-fcallable
(with-output-language (L13 Tail)
(lambda (info self-label)
(define set-locs
(lambda (loc* t* ebody)
(fold-right
(lambda (loc t ebody)
(if loc (in-context Effect `(seq (set! ,loc ,t) ,ebody)) ebody))
ebody loc* t*)))
(let ([arg-type* (info-foreign-arg-type* info)]
[result-type (info-foreign-result-type info)])
(let ([x* (map (lambda (x) (make-tmp 't)) arg-type*)])
(let-values ([(reg* reg-x* frame-x*) (get-arg-regs x*)])
(let ([fv* (let f ([frame-x* frame-x*] [i 0])
(if (null? frame-x*)
(begin (set! max-fv (fxmax max-fv i)) '())
(let ([i (fx+ i 1)])
(cons (get-fv i) (f (cdr frame-x*) i)))))]
[cp-save (meta-cond
[(real-register? '%cp) (make-tmp 'cp)]
[else #f])])
; add 2 for the old RA and cchain
(set! max-fv (fx+ max-fv 2))
(let-values ([(c-init c-args c-result c-return) (asm-foreign-callable info)])
; c-init saves C callee-save registers and restores tc
; each of c-args sets a variable to one of the C arguments
; c-result converts C results to Scheme values
; c-return restores callee-save registers and returns to C
(%seq
,(c-init)
,(restore-scheme-state
(in %cp) ; to save and then restore just before S_call_help
(out %ac0 %ac1 %xp %yp %ts %td scheme-args extra-regs))
; need overflow check since we're effectively retroactively turning
; what was a foreign call into a Scheme non-tail call
(fcallable-overflow-check)
; leave room for the RA & c-chain
(set! ,%sfp ,(%inline + ,%sfp (immediate ,(fx* (constant ptr-bytes) 2))))
; stash %cp and restore later to make sure it's intact by the time
; that we get to S_call_help
,(meta-cond
[(real-register? '%cp) `(set! ,cp-save ,%cp)]
[else `(nop)])
; convert arguments
,(fold-left (lambda (e x arg-type c-arg) `(seq ,(C->Scheme arg-type c-arg x #f) ,e))
(set-locs fv* frame-x*
(set-locs (map (lambda (reg) (in-context Lvalue (%mref ,%tc ,(reg-tc-disp reg)))) reg*) reg-x*
`(set! ,%ac0 (immediate ,(length arg-type*)))))
x* arg-type* c-args)
; cookie (0) will be replaced by the procedure, so this
; needs to be a quote, not an immediate
(set! ,(ref-reg %ac1) (literal ,(make-info-literal #f 'object 0 0)))
(set! ,(ref-reg %ts) (label-ref ,self-label 0)) ; for locking
,(meta-cond
[(real-register? '%cp) `(set! ,%cp ,cp-save)]
[else `(nop)])
,(save-scheme-state
(in %ac0 %ac1 %ts %cp)
(out %xp %yp %td scheme-args extra-regs))
; Scall-{any,one}-results calls the Scheme implementation of the
; callable, locking this callable wrapper (as communicated in %ts)
; until just before returning
(inline ,(make-info-c-simple-call fv* #f (pick-Scall result-type)) ,%c-simple-call)
,(restore-scheme-state
(in %ac0)
(out %ac1 %cp %xp %yp %ts %td scheme-args extra-regs))
; assuming no use of %cp from here on that could get saved into `(%tc-ref cp)`:
,(Scheme->C-for-result result-type c-result %ac0)
,(c-return)))))))))))
(define handle-do-rest
(lambda (fixed-args offset save-asm-ra?)
(with-output-language (L13 Effect)
(let-values ([(arg reg* fv-start)
; not using interface
(let f ([arg-number fixed-args] [rl arg-registers])
(cond
[(null? rl)
(let ([fv-offset (fx+ (fx* arg-number (constant ptr-bytes)) offset)])
(values
(in-context Lvalue (%mref ,%sfp ,fv-offset))
'()
(fx+ fv-offset (constant ptr-bytes))))]
[(= arg-number 0) (values (car rl) (cdr rl) offset)]
[else (f (fx- arg-number 1) (cdr rl))]))])
; TODO: try to avoid using ts by starting at the end and coming back until ac0
; reaches k(sfp), so we can use ts and/or td as an argument register. (need one
; available for the memory-memory moves)
(let* ([Lstart (make-local-label 'Lstart)]
[Ldone (make-local-label 'Ldone)]
[bump-xp-and-store-cdr
`(seq
(set! ,%xp ,(%inline + ,%xp ,(%constant size-pair)))
(if ,(%inline eq? ,%xp ,%ac0)
(goto ,Ldone)
(set! ,(%mref ,%xp
,(fx- (constant pair-cdr-disp) (constant size-pair)))
,%xp)))])
(%seq
; set ac0 to number of rest elements
(set! ,%ac0 ,(%inline - ,%ac0 (immediate ,fixed-args)))
(if ,(%inline eq? ,%ac0 (immediate 0))
(set! ,arg ,(%constant snil))
,(%seq
; adjust & scale ac0 to size of rest list in bytes
(set! ,%ac0 ,(%inline sll ,%ac0 ,(%constant pair-shift)))
; allocate the space
(set! ,%xp (alloc ,(make-info-alloc (constant type-pair) #f save-asm-ra?) ,%ac0))
; point ac0 past end of list
(set! ,%ac0 ,(%inline + ,%ac0 ,%xp))
; store the first element
(set! ,(%mref ,%xp ,(constant pair-car-disp)) ,arg)
; store the list in the first-element's old home
(set! ,arg ,%xp)
; store remaining reg elements, then loop through frame elements
,(let f ([reg* reg*])
(%seq
,bump-xp-and-store-cdr
,(if (null? reg*)
(%seq
; set ts to start of the fram arguments
(set! ,%ts ,(%inline + ,%sfp (immediate ,fv-start)))
(label ,Lstart)
; copy next element from stack to list
(set! ,(%mref ,%xp ,(constant pair-car-disp))
,(%mref ,%ts 0))
,bump-xp-and-store-cdr
(set! ,%ts ,(%inline + ,%ts ,(%constant ptr-bytes)))
(goto ,Lstart))
(%seq
(set! ,(%mref ,%xp ,(constant pair-car-disp))
,(car reg*))
,(f (cdr reg*))))))
(label ,Ldone)
; store nil in the last cdr
(set! ,(%mref ,%xp
,(fx- (constant pair-cdr-disp) (constant size-pair)))
,(%constant snil))))))))))
(define make-named-info-lambda
(lambda (name interface)
(make-info-lambda #f #f #f interface name)))
(define make-do-rest
(lambda (fixed-args offset)
(with-output-language (L13 CaseLambdaExpr)
`(lambda ,(make-named-info-lambda 'dorest '()) 0 ()
,(asm-enter
(%seq
(check-live ,(intrinsic-entry-live* (vector-ref dorest-intrinsics fixed-args)) ...)
,(handle-do-rest fixed-args offset #t)
(asm-return ,(intrinsic-return-live* (vector-ref dorest-intrinsics fixed-args)) ...)))))))
(define frame-args-offset (constant ptr-bytes))
; TODO: commonize these procedures (as macros) outside of
; np-expand-hand-coded/np-impose-calling-conventions?
(define make-arg-opnd
(lambda (n)
(let ([regnum (length arg-registers)])
(if (fx<= n regnum)
(list-ref arg-registers (fx- n 1))
(with-output-language (L13 Lvalue)
(%mref ,%sfp
,(fx* (constant ptr-bytes) (fx- n regnum))))))))
(define do-call
(lambda (interface)
(with-output-language (L13 Tail)
(%seq
(set! ,%ac0 (immediate ,interface))
,(meta-cond
[(real-register? '%cp)
`(jump ,(%mref ,%cp ,(constant closure-code-disp))
(,%ac0 ,%cp ,(reg-cons* %ret arg-registers) ...))]
[else
(%seq
(set! ,%td ,(ref-reg %cp))
(jump ,(%mref ,%td ,(constant closure-code-disp))
(,%ac0 ,(reg-cons* %ret arg-registers) ...)))])))))
(with-output-language (L13 Effect)
(meta-cond
[(real-register? '%cp)
(define xp/cp %cp)
(define load-xp/cp `(nop))]
[else
(define xp/cp %xp)
(define load-xp/cp `(set! ,%xp ,(ref-reg %cp)))]))
(define-syntax %set-esp
(lambda (x)
(syntax-case x ()
[(k e)
(with-implicit (k quasiquote %mref ref-reg)
(if (real-register? '%esp)
; write-through to tc so %esp need not be saved when going to C
#'`(seq
(set! ,(ref-reg %esp) e)
(set! ,(%mref ,%tc ,(tc-disp %esp)) ,(ref-reg %esp)))
#'`(set! ,(ref-reg %esp) e)))])))
(define nuate-help
(lambda ()
; Since cp is not always a real register, and the mref form requires us to put a var of some sort
; in for its base, we need to move cp to to a real register. Unfortunately, there do not seem to be
; enough real registers available, since ac0 is in use through out, xp and td serve as temopraries, and
; we'd like to keep ts free to serve for memory to memory moves.
; Since this is the case, we need a temporary to put cp into when we are working with it and
; xp is the natural choice (or td or ts if we switched amongst their roles)
(with-output-language (L13 Tail)
; cont. in cp and xp/cp, arg count in ac0, stack base in sfp, old frame base in yp
(let ([Lmultishot (make-local-label 'Lmultishot)]
[Lcopy-values (make-local-label 'Lcopy-values)]
[Lcopyup-values (make-local-label 'Lcopyup-values)]
[Lcopydown-values (make-local-label 'Lcopydown-values)]
[Lcopy-stack (make-local-label 'Lcopy-stack)]
[Lreturn (make-local-label 'Lreturn)])
(%seq
(set! ,%td ,(%mref ,xp/cp ,(constant continuation-stack-clength-disp)))
(if ,(%inline eq?
,(%mref ,xp/cp ,(constant continuation-stack-length-disp))
,%td)
; length and clength match, so it is either mutlishot or shot1shot
(if ,(%inline eq? ,%td ,(%constant scaled-shot-1-shot-flag))
; shot 1-shot
,(%seq
(set! ,(ref-reg %cp) (literal ,(make-info-literal #t 'object '$oops
(constant symbol-value-disp))))
(set! ,(make-arg-opnd 1) ,(%constant sfalse))
(set! ,(make-arg-opnd 2)
(literal ,(make-info-literal #f 'object
"attempt to invoke shot one-shot continuation" 0)))
,(do-call 2))
; multishot
,(%seq
(label ,Lmultishot)
; split if clength > underflow-limit
(if (if ,(%inline > ,%td ,(%constant underflow-limit))
(true)
; resize unless stack-base + clength + size(values) <= esp
; this is conservative to save a few instructions: really need
; stack-base + clength <= esp and clength + size(values) < stack-size;
; also, size may include argument register values
; Carefully using ts again
,(%seq
(set! ,%ts ,(%inline sll ,%ac0 ,(%constant log2-ptr-bytes)))
(set! ,%ts ,(%inline + ,%ts ,%sfp))
(set! ,%ts ,(%inline + ,%ts ,%td))
,(%inline < ,(ref-reg %esp) ,%ts)))
,(%seq
,(with-saved-scheme-state
(in %ac0 %cp %xp %yp scheme-args)
(out %ac1 %ts %td extra-regs)
`(inline ,(make-info-c-simple-call #f (lookup-c-entry split-and-resize)) ,%c-simple-call))
(set! ,%td ,(%mref ,xp/cp ,(constant continuation-stack-clength-disp))))
(nop))
; (new) stack base in sfp, clength in ac1, old frame base in yp
; set up return address and stack link
(set! ,(%tc-ref stack-link) ,(%mref ,xp/cp ,(constant continuation-link-disp)))
; set %td to end of the destination area / base of stack values dest
(set! ,%td ,(%inline + ,%td ,%sfp))
; don't shift if no stack values
(if ,(%inline <= ,%ac0 (immediate ,(length arg-registers)))
(nop)
,(%seq
; set xp to old frame base
(set! ,%xp ,(ref-reg %yp))
; set sfp to stack values bytes
(set! ,%sfp ,(%inline - ,%ac0 (immediate ,(length arg-registers))))
(set! ,%sfp ,(%inline sll ,%sfp ,(%constant log2-ptr-bytes)))
; shift stack return values up or down
(if ,(%inline < ,%xp ,%td)
,(%seq
(label ,Lcopyup-values)
(set! ,%sfp ,(%inline - ,%sfp ,(%constant ptr-bytes)))
(set! ,(%mref ,%td ,%sfp ,frame-args-offset) ,(%mref ,%xp ,%sfp ,frame-args-offset))
(if ,(%inline eq? ,%sfp (immediate 0))
,(%seq
; restore for invariants below; td is already okay
,load-xp/cp
(set! ,%sfp ,(%tc-ref scheme-stack)))
(goto ,Lcopyup-values)))
,(%seq
(set! ,%sfp ,(%inline + ,%sfp ,%td))
(label ,Lcopydown-values)
(set! ,(%mref ,%td ,frame-args-offset) ,(%mref ,%xp ,frame-args-offset))
(set! ,%td ,(%inline + ,%td ,(%constant ptr-bytes)))
(set! ,%xp ,(%inline + ,%xp ,(%constant ptr-bytes)))
(if ,(%inline eq? ,%td ,%sfp)
,(%seq
; restore for invariants below
,load-xp/cp
(set! ,%sfp ,(%tc-ref scheme-stack))
(set! ,%td ,(%inline + ,%sfp ,(%mref ,xp/cp ,(constant continuation-stack-clength-disp)))))
(goto ,Lcopydown-values))))))
; invariants: xp/cp = continuation, sfp = stack base, td = end of destination area
; set %xp to saved stack base
(set! ,%xp ,(%mref ,xp/cp ,(constant continuation-stack-disp)))
(label ,Lcopy-stack)
(if ,(%inline eq? ,%sfp ,%td)
(nop)
,(%seq
(set! ,(%mref ,%sfp 0) ,(%mref ,%xp 0))
(set! ,%sfp ,(%inline + ,%sfp ,(%constant ptr-bytes)))
(set! ,%xp ,(%inline + ,%xp ,(%constant ptr-bytes)))
(goto ,Lcopy-stack)))
,load-xp/cp
(goto ,Lreturn)))
; 1 shot
,(%seq
; treat as multishot if clength + size(values) > length
; conservative: some values may be in argument registers
; AWK - very carefully using ts here as we are out of other registers
(set! ,%ts ,(%inline sll ,%ac0 ,(%constant log2-ptr-bytes)))
(set! ,%ts ,(%inline + ,%ts ,%td))
(if ,(%inline < ,(%mref ,xp/cp ,(constant continuation-stack-length-disp)) ,%ts)
(goto ,Lmultishot)
,(%seq
; set up stack link
(set! ,(%tc-ref stack-link) ,(%mref ,xp/cp ,(constant continuation-link-disp)))
; place old stack in ac1 for now to cache him later (after we've removed
; the values, so that we have a place to store the length and link)
(set! ,(ref-reg %ac1) ,%sfp)
; grab saved stack
(set! ,%sfp ,(%mref ,xp/cp ,(constant continuation-stack-disp)))
; set up tc's scheme-stack variable
(set! ,(%tc-ref scheme-stack) ,%sfp)
; set up esp as stack-base + length - slop
(set! ,%ts ,(%inline - ,%sfp ,(%constant stack-slop)))
,(%set-esp ,(%inline + ,%ts ,(%mref ,xp/cp ,(constant continuation-stack-length-disp))))
; set up frame pointer to stack-base + current length
(set! ,%sfp ,(%inline + ,%sfp ,%td))
; bypass copy loop if no stack values
(if ,(%inline <= ,%ac0 (immediate ,(length arg-registers)))
(nop)
,(%seq
; set td to stack values bytes
(set! ,%td ,(%inline - ,%ac0 (immediate ,(length arg-registers))))
(set! ,%td ,(%inline sll ,%td ,(%constant log2-ptr-bytes)))
; set xp, td to top of stack values src, dest
(set! ,%xp ,(ref-reg %yp))
; move stack return values to top of saved stack segment
(label ,Lcopy-values)
(set! ,%td ,(%inline - ,%td ,(%constant ptr-bytes)))
(set! ,(%mref ,%sfp ,%td ,frame-args-offset) ,(%mref ,%xp ,%td ,frame-args-offset))
(if ,(%inline eq? ,%td (immediate 0))
,load-xp/cp ; need to load cp-reg, since xp is wiped out
(goto ,Lcopy-values))))
; place old stack in stack cache
(set! ,%td ,(ref-reg %ac1))
(set! ,(%mref ,%td 0) ,(%tc-ref scheme-stack-size))
(set! ,(%mref ,%td ,(constant ptr-bytes)) ,(%tc-ref stack-cache))
(set! ,(%tc-ref stack-cache) ,%td)
; set up tc's stack-size variable
(set! ,(%tc-ref scheme-stack-size) ,(%mref ,xp/cp ,(constant continuation-stack-length-disp)))
; mark continuation shot
(set! ,(%mref ,xp/cp ,(constant continuation-stack-length-disp)) ,(%constant scaled-shot-1-shot-flag))
(set! ,(%mref ,xp/cp ,(constant continuation-stack-clength-disp)) ,(%constant scaled-shot-1-shot-flag))
; return with 1 or multiple values
(label ,Lreturn)
(if ,(%inline eq? ,%ac0 (immediate 1))
,(%seq
(set! ,%ac0 ,(make-arg-opnd 1))
(jump ,(%mref ,xp/cp ,(constant continuation-return-address-disp)) (,%ac0)))
,(meta-cond
[(real-register? '%ret)
(%seq
(set! ,%ret ,(%mref ,xp/cp ,(constant continuation-return-address-disp)))
(jump ,(%mref ,%ret ,(constant return-address-mv-return-address-disp))
(,%ac0 ,%ret ,arg-registers ...)))]
[else
(let ([fv0 (get-fv 0)])
(%seq
(set! ,%xp ,(%mref ,xp/cp ,(constant continuation-return-address-disp)))
(set! ,fv0 ,%xp)
(jump ,(%mref ,%xp ,(constant return-address-mv-return-address-disp))
(,%ac0 ,arg-registers ... ,fv0))))]))))))))))))
(Program : Program (ir) -> Program ()
[(labels ([,l* ,le*] ...) ,l)
`(labels ([,l* ,(map CaseLambdaExpr le* l*)] ...) ,l)])
(CaseLambdaExpr : CaseLambdaExpr (ir l) -> CaseLambdaExpr ()
[(lambda ,info (,local0* ...) ,tlbody)
(fluid-let ([dcl* (info-lambda-dcl* info)] [max-fv 0] [local* local0*] [le-label l])
(let ([tlbody (Tail tlbody)])
(let ([local* (filter uvar-referenced? local*)])
(safe-assert (nodups local*))
(for-each (lambda (local) (uvar-location-set! local #f)) local*)
`(lambda ,info ,max-fv (,local* ...) ,tlbody))))]
[(fcallable ,info ,l)
(let ([lambda-info (make-info-lambda #f #f #f (list (length (info-foreign-arg-type* info)))
(info-foreign-name info) (constant code-flag-template))])
(fluid-let ([max-fv 0] [local* '()])
(let ([tlbody (build-fcallable info l)])
`(lambda ,lambda-info ,max-fv (,local* ...) ,tlbody))))]
[(hand-coded ,sym)
(case sym
[(dorest0) (make-do-rest 0 frame-args-offset)]
[(dorest1) (make-do-rest 1 frame-args-offset)]
[(dorest2) (make-do-rest 2 frame-args-offset)]
[(dorest3) (make-do-rest 3 frame-args-offset)]
[(dorest4) (make-do-rest 4 frame-args-offset)]
[(dorest5) (make-do-rest 5 frame-args-offset)]
[(callcc)
(let ([Ltop (make-local-label 'Ltop)])
`(lambda ,(make-named-info-lambda 'callcc '(1)) 0 ()
,(%seq
(set! ,(ref-reg %cp) ,(make-arg-opnd 1))
(set! ,%td ,(%tc-ref stack-link))
(set! ,%xp ,%td)
(label ,Ltop)
(set! ,%ac0 ,(%mref ,%xp ,(constant continuation-stack-clength-disp)))
(if ,(%inline eq?
,(%mref ,%xp ,(constant continuation-stack-length-disp))
,%ac0)
,(%seq
(set! ,%ac0
(literal ,(make-info-literal #f 'library-code
(lookup-libspec dounderflow)
(fx+ (constant code-data-disp) (constant size-rp-header)))))
(if (if ,(%inline eq? ,%ref-ret ,%ac0)
,(%inline eq?
,(%mref ,%td ,(constant continuation-winders-disp))
,(%tc-ref winders))
(false))
,(%seq
(set! ,(make-arg-opnd 1) ,%td)
,(do-call 1))
,(%seq
(set! ,%xp ,(%constant-alloc type-closure (constant size-continuation)))
; TODO: remove next line once get-room preserves %td
(set! ,%td ,(%tc-ref stack-link))
(set! ,(%mref ,%xp ,(constant continuation-code-disp))
(literal ,(make-info-literal #f 'library (lookup-libspec nuate) (constant code-data-disp))))
(set! ,(%mref ,%xp ,(constant continuation-return-address-disp)) ,%ref-ret)
(set! ,(%mref ,%xp ,(constant continuation-winders-disp)) ,(%tc-ref winders))
(set! ,%ref-ret ,%ac0)
(set! ,(%mref ,%xp ,(constant continuation-link-disp)) ,%td)
(set! ,(%tc-ref stack-link) ,%xp)
(set! ,%ac0 ,(%tc-ref scheme-stack))
(set! ,(%tc-ref scheme-stack) ,%sfp)
(set! ,(%mref ,%xp ,(constant continuation-stack-disp)) ,%ac0)
(set! ,%ac0 ,(%inline - ,%sfp ,%ac0))
(set! ,(%mref ,%xp ,(constant continuation-stack-length-disp)) ,%ac0)
(set! ,(%mref ,%xp ,(constant continuation-stack-clength-disp)) ,%ac0)
(set! ,(%tc-ref scheme-stack-size) ,(%inline - ,(%tc-ref scheme-stack-size) ,%ac0))
(set! ,(make-arg-opnd 1) ,%xp)
,(do-call 1))))
,(%seq
(set! ,(%mref ,%xp ,(constant continuation-stack-length-disp)) ,%ac0)
(set! ,%xp ,(%mref ,%xp ,(constant continuation-link-disp)))
(goto ,Ltop))))))]
[(call1cc)
`(lambda ,(make-named-info-lambda 'call1cc '(1)) 0 ()
,(%seq
(set! ,(ref-reg %cp) ,(make-arg-opnd 1))
(set! ,%td ,(%tc-ref stack-link))
(set! ,%ac0
(literal ,(make-info-literal #f 'library-code
(lookup-libspec dounderflow)
(fx+ (constant code-data-disp) (constant size-rp-header)))))
(if (if ,(%inline eq? ,%ref-ret ,%ac0)
,(%inline eq?
,(%mref ,%td ,(constant continuation-winders-disp))
,(%tc-ref winders))
(false))
,(%seq
(set! ,(make-arg-opnd 1) ,%td)
,(do-call 1))
,(%seq
(set! ,%xp ,(%constant-alloc type-closure (constant size-continuation)))
; TODO: remove next line once get-room preserves %td
(set! ,%td ,(%tc-ref stack-link))
(set! ,(%mref ,%xp ,(constant continuation-code-disp))
(literal ,(make-info-literal #f 'library (lookup-libspec nuate) (constant code-data-disp))))
(set! ,(%mref ,%xp ,(constant continuation-return-address-disp)) ,%ref-ret)
(set! ,(%mref ,%xp ,(constant continuation-winders-disp))
,(%tc-ref winders))
,(meta-cond
[(real-register? '%ret) `(set! ,%ret ,%ac0)]
[else `(nop)])
(set! ,(%mref ,%xp ,(constant continuation-link-disp)) ,%td)
(set! ,(%tc-ref stack-link) ,%xp)
(set! ,%ac0 ,(%tc-ref scheme-stack))
(set! ,(%mref ,%xp ,(constant continuation-stack-disp)) ,%ac0)
(set! ,(%mref ,%xp ,(constant continuation-stack-clength-disp))
,(%inline - ,%sfp ,%ac0))
; we need to get ourselves a new stack. we carve it out of the old
; one if the old one is large enough. if not, we look for one in
; the cache. if the cache is empty, we allocate a new stack.
(set! ,%sfp ,(%inline + ,%sfp (immediate ,(fx* (constant one-shot-headroom) 2))))
(if ,(%inline <= ,%sfp ,(ref-reg %esp))
,(%seq
(set! ,%sfp ,(%inline - ,%sfp ,(%constant one-shot-headroom)))
(set! ,%ac0 ,(%inline - ,%sfp ,%ac0))
(set! ,(%mref ,%xp ,(constant continuation-stack-length-disp)) ,%ac0)
(set! ,(%tc-ref scheme-stack) ,%sfp)
(set! ,(%tc-ref scheme-stack-size) ,(%inline - ,(%tc-ref scheme-stack-size) ,%ac0))
(set! ,(make-arg-opnd 1) ,%xp)
,(meta-cond
[(real-register? '%ret) `(nop)]
[else `(set! ,%ref-ret
(literal ,(make-info-literal #f 'library-code
(lookup-libspec dounderflow)
(fx+ (constant code-data-disp) (constant size-rp-header)))))])
,(do-call 1))
,(%seq
; set continuation length to entire stack size
(set! ,(%mref ,%xp ,(constant continuation-stack-length-disp))
,(%tc-ref scheme-stack-size))
(set! ,%sfp ,(%tc-ref stack-cache))
(if ,(%inline eq? ,%sfp ,(%constant snil))
,(%seq
(set! ,%ac0 ,%xp)
(set! ,%xp ,(%constant-alloc typemod (constant default-stack-size)))
(set! ,%sfp ,%xp)
(set! ,(%tc-ref scheme-stack) ,%sfp)
(set! ,(%tc-ref scheme-stack-size) ,(%constant default-stack-size))
,(%set-esp ,(%inline + ,%sfp
(immediate ,(fx- (constant default-stack-size) (constant stack-slop)))))
(set! ,(make-arg-opnd 1) ,%ac0)
,(meta-cond
[(real-register? '%ret) `(nop)]
[else `(set! ,%ref-ret
(literal ,(make-info-literal #f 'library-code
(lookup-libspec dounderflow)
(fx+ (constant code-data-disp) (constant size-rp-header)))))])
,(do-call 1))
,(%seq
(set! ,(%tc-ref stack-cache) ,(%mref ,%sfp ,(constant ptr-bytes))) ; next stack-segment
(set! ,%ac0 ,(%mref ,%sfp 0)) ; stack-segment size
(set! ,(%tc-ref scheme-stack) ,%sfp)
(set! ,(%tc-ref scheme-stack-size) ,%ac0)
,(%set-esp ,(%lea ,%ac0 ,%sfp (fx- (constant stack-slop))))
(set! ,(make-arg-opnd 1) ,%xp)
,(meta-cond
[(real-register? '%ret) `(nop)]
[else `(set! ,%ref-ret
(literal ,(make-info-literal #f 'library-code
(lookup-libspec dounderflow)
(fx+ (constant code-data-disp) (constant size-rp-header)))))])
,(do-call 1)))))))))]
[(dounderflow)
(let ([Lret (make-local-label 'Lret)] [Lmvreturn (make-local-label 'Lmvreturn)])
`(lambda ,(make-named-info-lambda 'winder-dummy '()) 0 ()
,(%seq
; (asm align)
(label ,Lret)
(rp-header ,Lmvreturn 0 0)
(set! ,(make-arg-opnd 1) ,%ac0)
(set! ,%ac0 (immediate 1))
(label ,Lmvreturn)
(set! ,xp/cp ,(%tc-ref stack-link))
,(meta-cond
[(real-register? '%cp) `(nop)]
[else `(set! ,(ref-reg %cp) ,xp/cp)])
(set! ,(ref-reg %yp) ,%sfp)
,(nuate-help))))]
[(nuate)
(let ([info (make-named-info-lambda 'continuation '(-1))])
(info-lambda-flags-set! info (fxlogor (constant code-flag-continuation) (constant code-flag-system)))
`(lambda ,info 0 ()
,(%seq
,load-xp/cp
(if ,(%inline eq? ,(%tc-ref winders)
,(%mref ,xp/cp ,(constant continuation-winders-disp)))
,(%seq
(set! ,(ref-reg %yp) ,%sfp)
(set! ,%sfp ,(%tc-ref scheme-stack))
,(nuate-help))
,(%seq
(if ,(%inline eq? ,%ac0 (immediate 0))
(set! ,%xp ,(%constant snil))
,(%seq
,(handle-do-rest 0 frame-args-offset #f)
(set! ,%xp ,(make-arg-opnd 1))))
(set! ,%sfp ,(%tc-ref scheme-stack))
(set! ,(make-arg-opnd 2) ,%xp)
(set! ,(make-arg-opnd 1) ,(ref-reg %cp))
(jump (literal ,(make-info-literal #f 'library-code
(lookup-libspec dounderflow*)
(constant code-data-disp)))
(,(reg-cons* %cp arg-registers) ...)))))))]
[else `(hand-coded ,sym)])])
(Lvalue : Lvalue (ir) -> Lvalue ()
[,x (Ref x)]
[(mref ,x1 ,x2 ,imm) (%mref ,(Ref x1) ,(Ref x2) ,imm)])
(Triv : Triv (ir) -> Triv ()
[,x (Ref x)] ; TODO: cannot call ref in cata, as we don't allow top-level cata
[(mref ,x1 ,x2 ,imm) (%mref ,(Ref x1) ,(Ref x2) ,imm)])
(Rhs : Rhs (ir) -> Rhs ()
[(mvcall ,info ,mdcl ,t0? ,t1* ... (,t* ...))
($oops who "Effect is responsible for handling mvcalls")])
(Effect : Effect (ir) -> Effect ()
[(do-rest ,fixed-args)
(if (fx<= fixed-args dorest-intrinsic-max)
`(inline ,(intrinsic-info-asmlib (vector-ref dorest-intrinsics fixed-args) #f) ,%asmlibcall!)
(handle-do-rest fixed-args frame-args-offset #f))]
; TODO: get internal error when , is missing from ,l
[(mventry-point (,x* ...) ,l)
(%seq
(remove-frame ,newframe-info-for-mventry-point)
,(let f ([x* x*])
(if (null? x*)
(%seq
(restore-local-saves ,newframe-info-for-mventry-point)
(goto ,l))
(let ([x (car x*)])
(if (uvar-referenced? x)
`(seq (set! ,x ,(uvar-location x)) ,(f (cdr x*)))
(f (cdr x*)))))))]
[(mverror-point)
`(set! ,%ref-ret (label-ref ,label-for-mverror-point ,(constant size-rp-header)))]
[(mvcall ,info ,mdcl ,t0? ,t1* ... (,t* ...))
(let ([mrvl (make-local-label 'mrvl)])
(build-nontail-call info mdcl t0? t1* t* '() mrvl #f
(lambda (newframe-info rpl)
(%seq (label ,mrvl) (remove-frame ,newframe-info) (restore-local-saves ,newframe-info)))))]
[(mvset ,info (,mdcl ,t0? ,t1* ...) (,t* ...) ((,x** ...) ...) ,ebody)
(let* ([frame-x** (map (lambda (x*) (set-formal-registers! x*)) x**)]
[nfv** (map (lambda (x*) (map (lambda (x)
(let ([nfv (make-tmp 'mvset-nfv)])
(uvar-location-set! x nfv)
nfv))
x*))
frame-x**)])
(let ([mrvl (make-local-label 'mrvl)])
(build-nontail-call info mdcl t0? t1* t* nfv** mrvl #t
(lambda (newframe-info rpl)
(fluid-let ([newframe-info-for-mventry-point newframe-info]
[label-for-mverror-point rpl])
(Effect ebody))))))]
[(set! ,[lvalue] (mvcall ,info ,mdcl ,t0? ,t1* ... (,t* ...)))
(build-nontail-call info mdcl t0? t1* t* '() #f #f
(lambda (newframe-info rpl)
(let ([retval (make-tmp 'retval)])
(%seq
(remove-frame ,newframe-info)
(set! ,retval ,%ac0)
(restore-local-saves ,newframe-info)
(set! ,lvalue ,retval)))))]
[(foreign-call ,info ,[t0] ,[t1*] ...)
(build-foreign-call info t0 t1* #f #t)]
[(set! ,[lvalue] (foreign-call ,info ,[t0] ,[t1*] ...))
(build-foreign-call info t0 t1* lvalue #t)])
(Tail : Tail (ir) -> Tail ()
[(entry-point (,x* ...) ,dcl ,mcp ,tlbody)
(unless (andmap (lambda (x) (eq? (uvar-type x) 'ptr)) x*)
($oops who "can't handle anything but plain vanilla types yet"))
; clear and recompute referenced flags on entry-point formals in case tail-frame
; optimization eliminates all of the references
(when mcp (uvar-referenced! mcp #f))
(for-each (lambda (x) (uvar-referenced! x #f)) x*)
(let do-frame ([x* (set-formal-registers! x*)] [fv-idx 1])
(unless (null? x*)
(let ([x (car x*)] [fv (get-fv fv-idx)])
(uvar-location-set! x fv)
(do-frame (cdr x*) (fx+ fv-idx 1)))))
(let ()
(define bind-formals
(lambda (mcp x* tlbody)
(define add-cpset
(lambda (mcp tlbody)
(if (and mcp (uvar-referenced? mcp)) `(seq (set! ,mcp ,(ref-reg %cp)) ,tlbody) tlbody)))
; we set cp after registers and before frame vars, since it might
; or might not be a register
(let f ([x* x*] [mcp mcp])
(if (null? x*)
(add-cpset mcp tlbody)
(let ([x (car x*)])
(if (uvar-referenced? x)
(let ([loc (uvar-location x)])
(if (fv? loc)
(begin
(set! max-fv (fxmax max-fv (fv-offset loc)))
(add-cpset mcp `(seq (set! ,x ,loc) ,(f (cdr x*) #f))))
`(seq (set! ,x ,loc) ,(f (cdr x*) mcp))))
(f (cdr x*) mcp)))))))
(let ([tlbody (Tail tlbody)])
(%seq
(label ,dcl)
; TODO: don't want to save ret for leaf routines
; TODO: don't necessarily want to position ret save here
,(meta-cond
[(real-register? '%ret) `(set! ,(get-fv 0) ,%ret)]
[else `(nop)])
(overflood-check)
,(bind-formals mcp x* tlbody))))]
[(mvcall ,info ,mdcl ,t0? ,t1* ... (,t* ...))
(build-tail-call info mdcl t0? t1* t*)]
[(foreign-call ,info ,[t0] ,[t1*] ...)
`(seq
; CAUTION: fv0 must hold return address when we call into C
,(build-foreign-call info t0 t1* %ac0 #f)
(jump ,(get-fv 0) (,%ac0)))]
[,rhs (do-return ,(Rhs ir))]
[(values ,info ,[t]) (do-return ,t)]
[(values ,info ,t* ...) (build-mv-return t*)]))
(define-pass np-expand-hand-coded : L13 (ir) -> L13.5 ()
(definitions
(import (only asm-module asm-enter))
(define Ldoargerr (make-Ldoargerr))
(define-$type-check (L13.5 Pred))
(define make-info
(lambda (name interface*)
(make-info-lambda #f #f #f interface* name)))
(define make-arg-opnd
(lambda (n)
(let ([regnum (length arg-registers)])
(if (fx<= n regnum)
(list-ref arg-registers (fx- n 1))
(with-output-language (L13.5 Lvalue)
(%mref ,%sfp
,(fx* (constant ptr-bytes) (fx- n regnum))))))))
(define do-call
(lambda ()
(with-output-language (L13.5 Tail)
(meta-cond
[(real-register? '%cp)
`(jump ,(%mref ,%cp ,(constant closure-code-disp))
(,%ac0 ,%cp ,(reg-cons* %ret arg-registers) ...))]
[else
(%seq
(set! ,%td ,(ref-reg %cp))
(jump ,(%mref ,%td ,(constant closure-code-disp))
(,%ac0 ,(reg-cons* %ret arg-registers) ...)))]))))
(define (make-list*-procedure name)
(with-output-language (L13.5 CaseLambdaExpr)
(let ([Ltop (make-local-label 'ltop)])
`(lambda ,(make-info name '(-2)) 0 ()
(seq
(set! ,%ac0 ,(%inline - ,%ac0 (immediate 1)))
; TODO: would be nice to avoid cmpl here
(if ,(%inline eq? ,%ac0 (immediate 0))
(seq
(set! ,%ac0 ,(make-arg-opnd 1))
(jump ,%ref-ret (,%ac0)))
; TODO: would be nice to avoid second cmpl here
(if ,(%inline < ,%ac0 (immediate 0))
(seq (pariah) (goto ,Ldoargerr))
,(%seq
(set! ,%ac0 ,(%inline sll ,%ac0 ,(%constant pair-shift)))
(set! ,%xp (alloc ,(make-info-alloc (constant type-pair) #f #f) ,%ac0))
,(let f ([reg* arg-registers] [i 0])
(if (null? reg*)
; filled in first i pairs
; have at least two stack arguments
; ac0 is at least (i+1) * pair-size; also amount allocated
(%seq
; point xp to last pair of list
(set! ,%xp
,(%lea ,%xp ,%ac0
(fx- (constant size-pair))))
; adjust from two ptrs per pair to one ptr per stack element
(set! ,%ac0
,(%inline srl ,%ac0 (immediate 1)))
; point ac0 to second-to-last stack argument
(set! ,%ac0
,(%lea ,%sfp ,%ac0
(fx* i (fx- (constant ptr-bytes)))))
(set! ,(%mref ,%xp ,(constant pair-cdr-disp))
,(%mref ,%ac0 ,(constant ptr-bytes)))
(label ,Ltop)
(set! ,(%mref ,%xp ,(constant pair-car-disp))
,(%mref ,%ac0 0))
(set! ,%ac0 ,(%inline - ,%ac0 ,(%constant ptr-bytes)))
(if ,(%inline eq? ,%ac0 ,%sfp)
,(%seq
(set! ,%ac0 ,(%inline - ,%xp (immediate ,(fx* i (constant size-pair)))))
(jump ,%ref-ret (,%ac0)))
,(%seq
(set! ,(%mref ,%xp ,(fx- (constant pair-cdr-disp) (constant size-pair)))
,%xp)
(set! ,%xp ,(%inline - ,%xp ,(%constant size-pair)))
(goto ,Ltop))))
(%seq
(set! ,(%mref ,%xp
,(fx+ (fx* i (constant size-pair)) (constant pair-car-disp)))
,(car reg*))
(if ,(%inline eq? ,%ac0 (immediate ,(fx* (fx+ i 1) (constant size-pair))))
,(%seq
(set! ,(%mref ,%xp
,(fx+ (fx* i (constant size-pair)) (constant pair-cdr-disp)))
,(make-arg-opnd (fx+ i 2)))
(set! ,%ac0 ,%xp)
(jump ,%ref-ret (,%ac0)))
,(%seq
(set! ,(%mref ,%xp
,(fx+ (fx* i (constant size-pair)) (constant pair-cdr-disp)))
,(%inline + ,%xp
(immediate ,(fx* (fx+ i 1) (constant size-pair)))))
,(f (cdr reg*) (fx+ i 1)))))))))))))))
(module (make-do/call make-do/ret)
(define make-do
(lambda (enter e)
; ret-loc is relevant only on machines with %ret reg:
; #f => ret is known to be at sfp[0]---no need to save or restore
; non-#f => save and restore to/from ret-loc
; if C needs to know about or might change the return address, ret-loc
; must be either #f or sfp[0]. otherwise, it can be (%tc-ref ret), which
; is useful if we don't know if %ret holds the return address. in that case,
; saving %ret to (%tc-ref ret) does no harm, nor does restoring it
; from there, but it might be harmful to save %ret to sfp[0], since %ret's
; contents are unknown.
(lambda (ret-loc name entry)
(with-output-language (L13.5 CaseLambdaExpr)
`(lambda ,(make-info name '()) 0 ()
,(enter
(%seq
,(meta-cond
[(real-register? '%ret) (if ret-loc `(set! ,ret-loc ,%ret) `(nop))]
[else `(nop)])
,(with-saved-scheme-state
(in %ac0 %ac1 %cp %xp %yp %ts %td scheme-args extra-regs)
(out)
`(inline ,(make-info-c-simple-call #t entry) ,%c-simple-call))
,(meta-cond
[(real-register? '%ret) (if ret-loc `(set! ,%ret ,ret-loc) `(nop))]
[else `(nop)])
,e)))))))
(define make-do/call (make-do (lambda (e) e) (do-call)))
(define (make-do/ret entry-live* return-live*)
(with-output-language (L13.5 Tail)
(make-do (lambda (e) (asm-enter (%seq (check-live ,entry-live* ...) ,e)))
`(asm-return ,return-live* ...)))))
(define make-dofargint
(lambda (name size entry-live* return-live*)
(with-output-language (L13.5 CaseLambdaExpr)
`(lambda ,(make-info name '()) 0 ()
,(asm-enter
(%seq
(check-live ,entry-live* ...)
,(cond
[(= (constant bigit-bits) size)
(%seq
(set! ,%td ,(%mref ,%ac0 ,(constant bignum-type-disp)))
(set! ,%ac0
(inline ,(make-info-load (bigit-type) #f) ,%load
,%ac0 ,%zero
,(%constant bignum-data-disp)))
(if ,(%inline eq? ,%td
(immediate ,(fx+ (fxsll 1 (constant bignum-length-offset))
(constant type-positive-bignum))))
(nop)
(set! ,%ac0 ,(%inline - (immediate 0) ,%ac0))))]
[(= (* (constant bigit-bits) 2) (* (constant ptr-bits) 2) size)
(let ([ac1 (in-context Lvalue (ref-reg %ac1))])
(let ([Lnegative (make-local-label 'Lnegative)] [Lreturn (make-local-label 'Lreturn)])
(%seq
(set! ,%xp ,%ac0)
(set! ,%td ,(%mref ,%xp ,(constant bignum-type-disp)))
(if ,(%inline eq? ,%td
(immediate ,(fx+ (fxsll 1 (constant bignum-length-offset))
(constant type-positive-bignum))))
,(%seq
(set! ,%ac0
(inline ,(make-info-load (bigit-type) #f) ,%load
,%xp ,%zero
,(%constant bignum-data-disp)))
(set! ,ac1 (immediate 0))
(goto ,Lreturn))
(if ,(%inline eq? ,%td
(immediate ,(fx+ (fxsll 1 (constant bignum-length-offset))
(constant type-negative-bignum))))
,(%seq
(set! ,%ac0
(inline ,(make-info-load (bigit-type) #f) ,%load
,%xp ,%zero
,(%constant bignum-data-disp)))
(set! ,ac1 (immediate 0))
(goto ,Lnegative))
,(%seq
(set! ,ac1
(inline ,(make-info-load (bigit-type) #f) ,%load
,%xp ,%zero
,(%constant bignum-data-disp)))
(set! ,%ac0
(inline ,(make-info-load (bigit-type) #f) ,%load
,%xp ,%zero
(immediate ,(fx+ (constant bignum-data-disp) (constant bigit-bytes)))))
(if ,(%inline eq? ,%td
(immediate ,(fx+ (fxsll 2 (constant bignum-length-offset))
(constant type-positive-bignum))))
(goto ,Lreturn)
(goto ,Lnegative)))))
(label ,Lnegative)
(set! ,%ac0 ,(%inline -/eq (immediate 0) ,%ac0))
(if (inline ,(make-info-condition-code 'eq? #f #t) ,%condition-code)
(set! ,ac1 ,(%inline - (immediate 0) ,ac1))
(set! ,ac1 ,(%inline lognot ,ac1)))
(label ,Lreturn))))]
[(= (* (constant bigit-bits) 2) (constant ptr-bits) size)
(let ([Lnegative (make-local-label 'Lnegative)] [Lreturn (make-local-label 'Lreturn)])
(%seq
(set! ,%xp ,%ac0)
(set! ,%td ,(%mref ,%xp ,(constant bignum-type-disp)))
(set! ,%ac0
(inline ,(make-info-load (bigit-type) #f) ,%load
,%xp ,%zero
,(%constant bignum-data-disp)))
(if ,(%inline eq? ,%td
(immediate ,(fx+ (fxsll 1 (constant bignum-length-offset))
(constant type-positive-bignum))))
(goto ,Lreturn)
(if ,(%inline eq? ,%td
(immediate ,(fx+ (fxsll 1 (constant bignum-length-offset))
(constant type-negative-bignum))))
(goto ,Lnegative)
,(%seq
(set! ,%xp
(inline ,(make-info-load (bigit-type) #f) ,%load
,%xp ,%zero
(immediate ,(fx+ (constant bignum-data-disp) (constant bigit-bytes)))))
(set! ,%ac0
,(%inline sll ,%ac0 ,(%constant bigit-bits)))
(set! ,%ac0 ,(%inline logor ,%ac0 ,%xp))
(if ,(%inline eq? ,%td
(immediate ,(fx+ (fxsll 2 (constant bignum-length-offset))
(constant type-positive-bignum))))
(goto ,Lreturn)
(goto ,Lnegative)))))
(label ,Lnegative)
(set! ,%ac0 ,(%inline - (immediate 0) ,%ac0))
(label ,Lreturn)))]
[else (sorry! name "cannot handle size ~s" size)])
(asm-return ,return-live* ...)))))))
(define make-dofretint
(lambda (name size entry-live* return-live*)
(with-output-language (L13.5 CaseLambdaExpr)
`(lambda ,(make-info name '()) 0 ()
,(asm-enter
(%seq
(check-live ,entry-live* ...)
,(cond
[(= (constant bigit-bits) size)
(%seq
(set! ,%xp
,(%constant-alloc type-typed-object
(fx+ (constant header-size-bignum) (constant bigit-bytes))
#f #t))
(if ,(%inline < ,%ac0 (immediate 0))
,(%seq
(set! ,(%mref ,%xp ,(constant bignum-type-disp))
(immediate ,(fx+ (fxsll 1 (constant bignum-length-offset))
(constant type-negative-bignum))))
(set! ,%ac0 ,(%inline - (immediate 0) ,%ac0)))
(set! ,(%mref ,%xp ,(constant bignum-type-disp))
(immediate ,(fx+ (fxsll 1 (constant bignum-length-offset))
(constant type-positive-bignum)))))
(inline ,(make-info-load (bigit-type) #f) ,%store ,%xp ,%zero
,(%constant bignum-data-disp) ,%ac0)
(set! ,%ac0 ,%xp))]
[(= (* (constant bigit-bits) 2) (* (constant ptr-bits) 2) size)
(let ([ac1 (in-context Lvalue (ref-reg %ac1))]
[Lstore1 (make-local-label 'Lstore1)]
[Lstore2 (make-local-label 'Lstore2)])
(%seq
(if ,(%inline < ,ac1 (immediate 0))
,(%seq
(set! ,ac1 ,(%inline lognot ,ac1))
(set! ,%ac0 ,(%inline - (immediate 0) ,%ac0))
; TODO: use condition code here
(if (if ,(%inline eq? ,%ac0 (immediate 0))
,(%seq
(set! ,ac1 ,(%inline + ,ac1 (immediate 1)))
(false))
,(%inline eq? ,ac1 (immediate 0)))
,(%seq
(set! ,%xp
,(%constant-alloc type-typed-object
(fx+ (constant header-size-bignum) (constant bigit-bytes))
#f #t))
(set! ,(%mref ,%xp ,(constant bignum-type-disp))
(immediate ,(fx+ (fxsll 1 (constant bignum-length-offset))
(constant type-negative-bignum))))
(goto ,Lstore1))
,(%seq
(set! ,%xp
,(%constant-alloc type-typed-object
(fx+ (constant header-size-bignum) (fx* (constant bigit-bytes) 2))
#f #t))
(set! ,(%mref ,%xp ,(constant bignum-type-disp))
(immediate ,(fx+ (fxsll 2 (constant bignum-length-offset))
(constant type-negative-bignum))))
(goto ,Lstore2))))
; TODO: use condition code here
(if ,(%inline eq? ,ac1 (immediate 0))
,(%seq
(set! ,%xp
,(%constant-alloc type-typed-object
(fx+ (constant header-size-bignum) (constant bigit-bytes))
#f #t))
(set! ,(%mref ,%xp ,(constant bignum-type-disp))
(immediate ,(fx+ (fxsll 1 (constant bignum-length-offset))
(constant type-positive-bignum))))
(label ,Lstore1)
(inline ,(make-info-load (bigit-type) #f) ,%store ,%xp ,%zero
,(%constant bignum-data-disp) ,%ac0))
,(%seq
(set! ,%xp
,(%constant-alloc type-typed-object
(fx+ (constant header-size-bignum) (fx* (constant bigit-bytes) 2))
#f #t))
(set! ,(%mref ,%xp ,(constant bignum-type-disp))
(immediate ,(fx+ (fxsll 2 (constant bignum-length-offset))
(constant type-positive-bignum))))
(label ,Lstore2)
(inline ,(make-info-load (bigit-type) #f) ,%store ,%xp ,%zero
,(%constant bignum-data-disp)
,ac1)
(inline ,(make-info-load (bigit-type) #f) ,%store ,%xp ,%zero
(immediate ,(fx+ (constant bignum-data-disp) (constant bigit-bytes)))
,%ac0))))
(set! ,%ac0 ,%xp)))]
[(= (* (constant bigit-bits) 2) (constant ptr-bits) size)
(let ([Lstore1 (make-local-label 'Lstore1)] [Lstore2 (make-local-label 'Lstore2)])
(%seq
(if ,(%inline < ,%ac0 (immediate 0))
,(%seq
(set! ,%ac0 ,(%inline - (immediate 0) ,%ac0))
(set! ,%td ,(%inline srl ,%ac0
,(%constant bigit-bits)))
(if ,(%inline eq? ,%td (immediate 0))
,(%seq
(set! ,%xp
,(%constant-alloc type-typed-object
(fx+ (constant header-size-bignum) (constant bigit-bytes))
#f #t))
(set! ,(%mref ,%xp ,(constant bignum-type-disp))
(immediate ,(fx+ (fxsll 1 (constant bignum-length-offset))
(constant type-negative-bignum))))
(goto ,Lstore1))
,(%seq
(set! ,%xp
,(%constant-alloc type-typed-object
(fx+ (constant header-size-bignum) (fx* (constant bigit-bytes) 2))
#f #t))
(set! ,(%mref ,%xp ,(constant bignum-type-disp))
(immediate ,(fx+ (fxsll 2 (constant bignum-length-offset))
(constant type-negative-bignum))))
(goto ,Lstore2))))
,(%seq
(set! ,%td ,(%inline srl ,%ac0
,(%constant bigit-bits)))
(if ,(%inline eq? ,%td (immediate 0))
,(%seq
(set! ,%xp
,(%constant-alloc type-typed-object
(fx+ (constant header-size-bignum) (constant bigit-bytes))
#f #t))
(set! ,(%mref ,%xp ,(constant bignum-type-disp))
(immediate ,(fx+ (fxsll 1 (constant bignum-length-offset))
(constant type-positive-bignum))))
(label ,Lstore1)
(inline ,(make-info-load (bigit-type) #f) ,%store ,%xp ,%zero
,(%constant bignum-data-disp) ,%ac0))
,(%seq
(set! ,%xp
,(%constant-alloc type-typed-object
(fx+ (constant header-size-bignum) (fx* (constant bigit-bytes) 2))
#f #t))
(set! ,(%mref ,%xp ,(constant bignum-type-disp))
(immediate ,(fx+ (fxsll 2 (constant bignum-length-offset))
(constant type-positive-bignum))))
(label ,Lstore2)
(inline ,(make-info-load (bigit-type) #f) ,%store ,%xp ,%zero
,(%constant bignum-data-disp)
,%td)
(inline ,(make-info-load (bigit-type) #f) ,%store ,%xp ,%zero
(immediate ,(fx+ (constant bignum-data-disp) (constant bigit-bytes)))
,%ac0)))))
(set! ,%ac0 ,%xp)))]
[else (sorry! name "cannot handle size ~s" size)])
(asm-return ,return-live* ...)))))))
(define make-dofretuns
(lambda (name size entry-live* return-live*)
(with-output-language (L13.5 CaseLambdaExpr)
`(lambda ,(make-info name '()) 0 ()
,(asm-enter
(%seq
(check-live ,entry-live* ...)
,(cond
[(= (constant bigit-bits) size)
(%seq
(set! ,%xp
,(%constant-alloc type-typed-object
(fx+ (constant header-size-bignum) (constant bigit-bytes))
#f #t))
(set! ,(%mref ,%xp ,(constant bignum-type-disp))
(immediate ,(fx+ (fxsll 1 (constant bignum-length-offset))
(constant type-positive-bignum))))
(inline ,(make-info-load (bigit-type) #f) ,%store ,%xp ,%zero
,(%constant bignum-data-disp) ,%ac0)
(set! ,%ac0 ,%xp))]
[(= (* (constant bigit-bits) 2) (* (constant ptr-bits) 2) size)
(let ([ac1 (in-context Lvalue (ref-reg %ac1))])
(%seq
(if ,(%inline eq? ,ac1 (immediate 0))
,(%seq
(set! ,%xp
,(%constant-alloc type-typed-object
(fx+ (constant header-size-bignum) (constant bigit-bytes))
#f #t))
(set! ,(%mref ,%xp ,(constant bignum-type-disp))
(immediate ,(fx+ (fxsll 1 (constant bignum-length-offset))
(constant type-positive-bignum))))
(inline ,(make-info-load (bigit-type) #f) ,%store ,%xp ,%zero
,(%constant bignum-data-disp) ,%ac0))
,(%seq
(set! ,%xp
,(%constant-alloc type-typed-object
(fx+ (constant header-size-bignum) (fx* (constant bigit-bytes) 2))
#f #t))
(set! ,(%mref ,%xp ,(constant bignum-type-disp))
(immediate ,(fx+ (fxsll 2 (constant bignum-length-offset))
(constant type-positive-bignum))))
(inline ,(make-info-load (bigit-type) #f) ,%store ,%xp ,%zero
,(%constant bignum-data-disp)
,ac1)
(inline ,(make-info-load (bigit-type) #f) ,%store ,%xp ,%zero
(immediate ,(fx+ (constant bignum-data-disp) (constant bigit-bytes)))
,%ac0)))
(set! ,%ac0 ,%xp)))]
[(= (* (constant bigit-bits) 2) (constant ptr-bits) size)
(%seq
(set! ,%td ,(%inline srl ,%ac0
,(%constant bigit-bits)))
(if ,(%inline eq? ,%td (immediate 0))
,(%seq
(set! ,%xp
,(%constant-alloc type-typed-object
(fx+ (constant header-size-bignum) (constant bigit-bytes))
#f #t))
(set! ,(%mref ,%xp ,(constant bignum-type-disp))
(immediate ,(fx+ (fxsll 1 (constant bignum-length-offset))
(constant type-positive-bignum))))
(inline ,(make-info-load (bigit-type) #f) ,%store ,%xp ,%zero
,(%constant bignum-data-disp) ,%ac0))
,(%seq
(set! ,%xp
,(%constant-alloc type-typed-object
(fx+ (constant header-size-bignum) (fx* (constant bigit-bytes) 2))
#f #t))
(set! ,(%mref ,%xp ,(constant bignum-type-disp))
(immediate ,(fx+ (fxsll 2 (constant bignum-length-offset))
(constant type-positive-bignum))))
(inline ,(make-info-load (bigit-type) #f) ,%store ,%xp ,%zero
,(%constant bignum-data-disp)
,%td)
(inline ,(make-info-load (bigit-type) #f) ,%store ,%xp ,%zero
(immediate ,(fx+ (constant bignum-data-disp) (constant bigit-bytes)))
,%ac0)))
(set! ,%ac0 ,%xp))]
[else (sorry! name "cannot handle size ~s" size)])
(asm-return ,return-live* ...)))))))
(define make-dofretu*
(lambda (name type size entry-live* return-live*)
(with-output-language (L13.5 CaseLambdaExpr)
(let ([Ltop1 (make-local-label 'Ltop1)] [Ltop2 (make-local-label 'Ltop2)])
`(lambda ,(make-info name '()) 0 ()
,(asm-enter
(%seq
(check-live ,entry-live* ...)
; argument in ac0, return value in xp
(if ,(%inline eq? ,%ac0 (immediate 0))
,(%seq
(set! ,%xp ,(%constant sfalse))
(asm-return ,return-live* ...))
,(%seq
(set! ,%td (immediate 0))
(label ,Ltop1)
(set! ,%ts
(inline ,(make-info-load type #f) ,%load ,%ac0 ,%td
(immediate 0)))
(if ,(%inline eq? ,%ts (immediate 0))
(if ,(%inline eq? ,%td (immediate 0))
,(%seq
(set! ,%xp (literal ,(make-info-literal #f 'object #vu8() 0)))
(asm-return ,return-live* ...))
,(%seq
(set! ,(ref-reg %ac1) ,%td)
(set! ,%td ,(%inline + ,%td
(immediate
,(fx+ (constant header-size-bytevector)
(fx- (constant byte-alignment) 1)))))
(set! ,%td ,(%inline logand ,%td
(immediate ,(fx- (constant byte-alignment)))))
(set! ,%xp (alloc ,(make-info-alloc (constant type-typed-object) #f #t) ,%td))
(set! ,%td ,(ref-reg %ac1))
(set! ,%td ,(%inline sll ,%td
,(%constant bytevector-length-offset)))
(set! ,%td ,(%inline logor ,%td
,(%constant type-bytevector)))
(set! ,(%mref ,%xp ,(constant bytevector-type-disp))
,%td)
(set! ,%td ,(ref-reg %ac1))
(label ,Ltop2)
(if ,(%inline eq? ,%td (immediate 0))
(asm-return ,return-live* ...)
,(%seq
(set! ,%td ,(%inline - ,%td (immediate ,size)))
(set! ,%ts
(inline ,(make-info-load type #f) ,%load ,%ac0 ,%td
(immediate 0)))
(inline ,(make-info-load type #f) ,%store ,%xp ,%td
,(%constant bytevector-data-disp)
,%ts)
(goto ,Ltop2)))))
,(%seq
(set! ,%td ,(%inline + ,%td (immediate ,size)))
(goto ,Ltop1)))))))))))))
(CaseLambdaExpr : CaseLambdaExpr (ir) -> CaseLambdaExpr ()
[(hand-coded ,sym)
(case sym
[(values-procedure)
(let ([regnum (length arg-registers)]
[Ltop (make-local-label 'top)])
`(lambda ,(make-info "values" '(-1)) 0 ()
(if ,(%inline eq? ,%ac0 (immediate 1))
(seq
(set! ,%ac0 ,(make-arg-opnd 1))
(jump ,%ref-ret (,%ac0)))
,(meta-cond
[(real-register? '%ret)
`(jump ,(%mref ,%ret ,(constant return-address-mv-return-address-disp))
(,%ac0 ,%ret ,arg-registers ...))]
[else
(%seq
(set! ,%xp ,%ref-ret)
(jump ,(%mref ,%xp
,(constant return-address-mv-return-address-disp))
(,%ac0 ,arg-registers ... ,(get-fv 0))))]))))]
[($apply-procedure)
(let ([Lloop (make-local-label 'loop)]
[Ldone (make-local-label 'done)])
`(lambda ,(make-info "$apply" '(3)) 0 ()
,(%seq
(set! ,(ref-reg %cp) ,(make-arg-opnd 1))
(set! ,%ac0 ,(make-arg-opnd 2))
(set! ,%xp ,(make-arg-opnd 3))
;; TODO: when fixnum-offset = log2-ptr-bytes, we can avoid an sll by saving
;; %ac0 before we shift it right.
(set! ,%ac0 ,(%inline sra ,%ac0 ,(%constant fixnum-offset)))
(if ,(%inline eq? ,%ac0 (immediate 0))
(goto ,Ldone)
,(%seq
(set! ,%td ,(%inline sll ,%ac0 ,(%constant log2-ptr-bytes)))
(set! ,%td ,(%inline + ,%td ,%sfp))
(if ,(%inline > ,%td ,(ref-reg %esp))
(seq (pariah)
,(with-saved-ret-reg
(with-saved-scheme-state
(in %cp %xp %ac0)
(out %ac1 %yp %ts %td scheme-args extra-regs)
`(inline ,(make-info-c-simple-call #f (lookup-c-entry handle-apply-overflood)) ,%c-simple-call))))
(nop))
,(let load-regs ([regs arg-registers])
(if (null? regs)
(%seq
(set! ,%td ,%sfp)
(label ,Lloop)
(set! ,(%mref ,%td ,(constant ptr-bytes))
,(%mref ,%xp ,(constant pair-car-disp)))
(set! ,%xp
,(%mref ,%xp ,(constant pair-cdr-disp)))
(if ,(%type-check mask-nil snil ,%xp)
,(%seq (label ,Ldone) ,(do-call))
,(%seq
(set! ,%td ,(%inline + ,%td ,(%constant ptr-bytes)))
(goto ,Lloop))))
(%seq
(set! ,(car regs) ,(%mref ,%xp ,(constant pair-car-disp)))
(set! ,%xp ,(%mref ,%xp ,(constant pair-cdr-disp)))
(if ,(%type-check mask-nil snil ,%xp)
(goto ,Ldone)
,(load-regs (cdr regs)))))))))))]
[(list*-procedure) (make-list*-procedure "list*")]
[(cons*-procedure) (make-list*-procedure "cons*")]
[($record-procedure)
(let ([Ltop (make-local-label 'ltop)])
`(lambda ,(make-info "$record" '(-2)) 0 ()
(if ,(%inline eq? ,%ac0 (immediate 0))
(seq (pariah) (goto ,Ldoargerr))
,(%seq
(set! ,%ac0 ,(%inline sll ,%ac0 ,(%constant log2-ptr-bytes)))
(set! ,%td ,(%inline + ,%ac0 (immediate ,(fx- (constant byte-alignment) 1))))
(set! ,%td ,(%inline logand ,%td (immediate ,(- (constant byte-alignment)))))
(set! ,%xp (alloc ,(make-info-alloc (constant type-typed-object) #f #f) ,%td))
,(let f ([reg* arg-registers] [i 0])
(if (null? reg*)
(%seq
; point xp to last element of record
(set! ,%xp
,(%lea ,%xp ,%ac0 (fx- (constant ptr-bytes))))
; point ac0 to last stack argument
(set! ,%ac0
,(%lea ,%sfp ,%ac0
(fx* i (fx- (constant ptr-bytes)))))
(label ,Ltop)
(set! ,(%mref ,%xp ,(constant record-type-disp))
,(%mref ,%ac0 0))
(set! ,%ac0 ,(%inline - ,%ac0 ,(%constant ptr-bytes)))
(if ,(%inline eq? ,%ac0 ,%sfp)
,(%seq
(set! ,%ac0 ,(%inline - ,%xp (immediate ,(fx* i (constant ptr-bytes)))))
(jump ,%ref-ret (,%ac0)))
,(%seq
(set! ,%xp ,(%inline - ,%xp ,(%constant ptr-bytes)))
(goto ,Ltop))))
(%seq
(set! ,(%mref ,%xp
,(fx+ (fx* i (constant ptr-bytes)) (constant record-type-disp)))
,(car reg*))
(if ,(%inline eq? ,%ac0 (immediate ,(fx* (fx+ i 1) (constant ptr-bytes))))
,(%seq
(set! ,%ac0 ,%xp)
(jump ,%ref-ret (,%ac0)))
,(f (cdr reg*) (fx+ i 1))))))))))]
[(vector-procedure)
(let ([Ltop (make-local-label 'ltop)])
`(lambda ,(make-info "vector" '(-1)) 0 ()
(if ,(%inline eq? ,%ac0 (immediate 0))
,(%seq
(set! ,%ac0 (literal ,(make-info-literal #f 'object '#() 0)))
(jump ,%ref-ret (,%ac0)))
,(%seq
(set! ,%ac0 ,(%inline sll ,%ac0 ,(%constant log2-ptr-bytes)))
(set! ,%td ,(%inline + ,%ac0 (immediate ,(fx+ (constant ptr-bytes) (fx- (constant byte-alignment) 1)))))
(set! ,%td ,(%inline logand ,%td (immediate ,(- (constant byte-alignment)))))
(set! ,%xp (alloc ,(make-info-alloc (constant type-typed-object) #f #f) ,%td))
,(let ([delta (fx- (constant vector-length-offset) (constant log2-ptr-bytes))])
(safe-assert (fx>= delta 0))
(if (fx= delta 0)
(if (fx= (constant type-vector) 0)
`(set! ,(%mref ,%xp ,(constant vector-type-disp)) ,%ac0)
(%seq
(set! ,%td ,(%inline logor ,%ac0 (immediate ,(constant type-vector))))
(set! ,(%mref ,%xp ,(constant vector-type-disp)) ,%td)))
(%seq
(set! ,%td ,(%inline sll ,%ac0 (immediate ,delta)))
,(if (fx= (constant type-vector) 0)
`(set! ,(%mref ,%xp ,(constant vector-type-disp)) ,%td)
(%seq
(set! ,%td ,(%inline logor ,%td (immediate ,(constant type-vector))))
(set! ,(%mref ,%xp ,(constant vector-type-disp)) ,%td))))))
,(let f ([reg* arg-registers] [i 0])
(if (null? reg*)
(%seq
; point xp to last element of vector
(set! ,%xp ,(%inline + ,%xp ,%ac0))
; point ac0 to last stack argument
(set! ,%ac0
,(%lea ,%sfp ,%ac0
(fx* i (fx- (constant ptr-bytes)))))
(label ,Ltop)
(set! ,(%mref ,%xp ,(fx- (constant vector-data-disp) (constant ptr-bytes)))
,(%mref ,%ac0 0))
(set! ,%ac0 ,(%inline - ,%ac0 ,(%constant ptr-bytes)))
(if ,(%inline eq? ,%ac0 ,%sfp)
,(%seq
(set! ,%ac0 ,(%inline - ,%xp (immediate ,(fx* (fx+ i 1) (constant ptr-bytes)))))
(jump ,%ref-ret (,%ac0)))
,(%seq
(set! ,%xp ,(%inline - ,%xp ,(%constant ptr-bytes)))
(goto ,Ltop))))
(%seq
(set! ,(%mref ,%xp
,(fx+ (fx* i (constant ptr-bytes)) (constant vector-data-disp)))
,(car reg*))
(if ,(%inline eq? ,%ac0 (immediate ,(fx* (fx+ i 1) (constant ptr-bytes))))
,(%seq
(set! ,%ac0 ,%xp)
(jump ,%ref-ret (,%ac0)))
,(f (cdr reg*) (fx+ i 1))))))))))]
[(list-procedure)
(let ([Ltop (make-local-label 'ltop)])
`(lambda ,(make-info "list" '(-1)) 0 ()
(if ,(%inline eq? ,%ac0 (immediate 0))
(seq
(set! ,%ac0 ,(%constant snil))
(jump ,%ref-ret (,%ac0)))
,(%seq
(set! ,%ac0 ,(%inline sll ,%ac0 ,(%constant pair-shift)))
(set! ,%xp (alloc ,(make-info-alloc (constant type-pair) #f #f) ,%ac0))
,(let f ([reg* arg-registers] [i 0])
(if (null? reg*)
; filled in first i pairs
; have at least one stack argument
; ac0 is amount allocated, or size-pair * # elements
(%seq
; point xp to last pair of list
(set! ,%xp
,(%lea ,%xp ,%ac0 (fx- (constant size-pair))))
; adjust from two ptrs per pair to one ptr per stack element
(set! ,%ac0
,(%inline srl ,%ac0 (immediate 1)))
; point ac0 to last stack argument
(set! ,%ac0
,(%lea ,%sfp ,%ac0
(fx* i (fx- (constant ptr-bytes)))))
(set! ,(%mref ,%xp ,(constant pair-cdr-disp))
,(%constant snil))
(label ,Ltop)
(set! ,(%mref ,%xp ,(constant pair-car-disp))
,(%mref ,%ac0 0))
(set! ,%ac0 ,(%inline - ,%ac0 ,(%constant ptr-bytes)))
(if ,(%inline eq? ,%ac0 ,%sfp)
,(%seq
(set! ,%ac0 ,(%inline - ,%xp (immediate ,(fx* i (constant size-pair)))))
(jump ,%ref-ret (,%ac0)))
,(%seq
(set! ,(%mref ,%xp ,(fx- (constant pair-cdr-disp) (constant size-pair)))
,%xp)
(set! ,%xp ,(%inline - ,%xp ,(%constant size-pair)))
(goto ,Ltop))))
(%seq
(set! ,(%mref ,%xp
,(fx+ (fx* i (constant size-pair)) (constant pair-car-disp)))
,(car reg*))
(if ,(%inline eq? ,%ac0 (immediate ,(fx* (fx+ i 1) (constant size-pair))))
,(%seq
(set! ,(%mref ,%xp
,(fx+ (fx* i (constant size-pair)) (constant pair-cdr-disp)))
,(%constant snil))
(set! ,%ac0 ,%xp)
(jump ,%ref-ret (,%ac0)))
,(%seq
(set! ,(%mref ,%xp
,(fx+ (fx* i (constant size-pair)) (constant pair-cdr-disp)))
,(%inline + ,%xp
(immediate ,(fx* (fx+ i 1) (constant size-pair)))))
,(f (cdr reg*) (fx+ i 1)))))))))))]
[($instantiate-code-object)
`(lambda ,(make-info "$instantiate-code-object" '(3)) 0 ()
,(%seq
,(with-saved-ret-reg
(%seq
,(save-scheme-state
(in scheme-args)
(out %ac0 %ac1 %cp %xp %yp %ts %td extra-regs))
(inline ,(make-info-c-simple-call #f (lookup-c-entry instantiate-code-object))
,%c-simple-call)
,(restore-scheme-state
(in %ac0)
(out %ac1 %cp %xp %yp %ts %td scheme-args extra-regs))))
(jump ,%ref-ret (,%ac0))))]
[(values-error) (make-do/call (in-context Lvalue (%tc-ref ret)) "values-error" (lookup-c-entry handle-values-error))]
[(domvleterr) (make-do/call (in-context Lvalue (%tc-ref ret)) "domvleterr" (lookup-c-entry handle-mvlet-error))]
[(doargerr) (make-do/call (in-context Lvalue (%tc-ref ret)) "doargerr" (lookup-c-entry handle-arg-error))]
[(call-error) (make-do/call (in-context Lvalue (%tc-ref ret)) "call-error" (lookup-c-entry handle-docall-error))]
[(dooverflow) ((make-do/ret (intrinsic-entry-live* dooverflow) (intrinsic-return-live* dooverflow)) #f "dooverflow" (lookup-c-entry handle-overflow))]
[(dooverflood) ((make-do/ret (intrinsic-entry-live* dooverflood) (intrinsic-return-live* dooverflood)) #f "dooverflood" (lookup-c-entry handle-overflood))]
[(scan-remembered-set) ((make-do/ret (intrinsic-entry-live* scan-remembered-set) (intrinsic-return-live* scan-remembered-set)) (in-context Lvalue (%tc-ref ret)) "scan-remembered-set" (lookup-c-entry scan-remembered-set))]
[(get-room) ((make-do/ret (intrinsic-entry-live* get-room) (intrinsic-return-live* get-room)) (in-context Lvalue (%tc-ref ret)) "get-room" (lookup-c-entry get-more-room))]
[(nonprocedure-code)
`(lambda ,(make-info "nonprocedure-code" '()) 0 ()
,(%seq
(set! ,%td ,(%mref ,%xp ,(constant symbol-value-disp)))
(if ,(%type-check mask-closure type-closure ,%td)
(seq
(set! ,(ref-reg %cp) ,%td)
(set! ,(%mref ,%xp ,(constant symbol-pvalue-disp))
,(%mref ,%td ,(constant closure-code-disp))))
,(with-saved-ret-reg
(with-saved-scheme-state
(in %ac0 %ac1 %cp %xp %yp scheme-args)
(out %ts %td extra-regs)
`(inline ,(make-info-c-simple-call #f (lookup-c-entry handle-nonprocedure-symbol))
,%c-simple-call))))
,(do-call)))]
[($foreign-entry-procedure)
`(lambda ,(make-info "$foreign-entry" '(1)) 0 ()
,(%seq
(set! ,%ac0 ,(make-arg-opnd 1))
,(with-saved-ret-reg
(with-saved-scheme-state
(in %ac0)
(out %cp %xp %yp %ac1 %ts %td scheme-args extra-regs)
`(inline ,(make-info-c-simple-call #f (lookup-c-entry foreign-entry))
,%c-simple-call)))
(jump ,%ref-ret (,%ac0))))]
[($install-library-entry-procedure)
`(lambda ,(make-info "$install-library-entry" '(2)) 0 ()
,(%seq
,(with-saved-ret-reg
(%seq
,(save-scheme-state
(in scheme-args)
(out %ac0 %ac1 %cp %xp %yp %ts %td extra-regs))
(inline ,(make-info-c-simple-call #f (lookup-c-entry install-library-entry))
,%c-simple-call)
,(restore-scheme-state
(in)
(out %ac0 %ac1 %cp %xp %yp %ts %td scheme-args extra-regs))))
(set! ,%ac0 ,(%constant svoid))
(jump ,%ref-ret (,%ac0))))]
[(bytevector=?)
(let ([bv1 (make-tmp 'bv1)] [bv2 (make-tmp 'bv2)] [idx (make-tmp 'idx)] [len2 (make-tmp 'len2)])
(define (argcnt->max-fv n) (max (- n (length arg-registers)) 0))
(let ([Ltop (make-local-label 'Ltop)] [Ltrue (make-local-label 'Ltrue)] [Lfail (make-local-label 'Lfail)])
(define iptr-bytes (in-context Triv (%constant ptr-bytes)))
`(lambda ,(make-info "bytevector=?" '(2)) ,(argcnt->max-fv 2) (,bv1 ,bv2 ,idx ,len2)
,(%seq
(set! ,bv1 ,(make-arg-opnd 1))
(set! ,bv2 ,(make-arg-opnd 2))
(if ,(%inline eq? ,bv1 ,bv2)
(goto ,Ltrue)
,(%seq
(set! ,idx ,(%inline srl
,(%mref ,bv1 ,(constant bytevector-type-disp))
,(%constant bytevector-length-offset)))
(set! ,len2 ,(%inline srl
,(%mref ,bv2 ,(constant bytevector-type-disp))
,(%constant bytevector-length-offset)))
(if ,(%inline eq? ,len2 ,idx)
,(%seq
(label ,Ltop)
(if ,(%inline >= ,idx ,iptr-bytes)
(if ,(%inline eq?
,(%mref ,bv1 ,(constant bytevector-data-disp))
,(%mref ,bv2 ,(constant bytevector-data-disp)))
,(%seq
(set! ,idx ,(%inline - ,idx ,iptr-bytes))
(set! ,bv1 ,(%inline + ,bv1 ,iptr-bytes))
(set! ,bv2 ,(%inline + ,bv2 ,iptr-bytes))
(goto ,Ltop))
(goto ,Lfail))
(if (if ,(%inline eq? ,idx (immediate 0))
(true)
,(%seq
(set! ,bv1 ,(%mref ,bv1 ,(constant bytevector-data-disp)))
(set! ,bv2 ,(%mref ,bv2 ,(constant bytevector-data-disp)))
(set! ,idx ,(%inline - ,iptr-bytes ,idx))
(set! ,idx ,(%inline sll ,idx (immediate 3)))
,(constant-case native-endianness
[(little)
(%seq
(set! ,bv1 ,(%inline sll ,bv1 ,idx))
(set! ,bv2 ,(%inline sll ,bv2 ,idx)))]
[(big)
(%seq
(set! ,bv1 ,(%inline srl ,bv1 ,idx))
(set! ,bv2 ,(%inline srl ,bv2 ,idx)))])
,(%inline eq? ,bv1 ,bv2)))
,(%seq
(label ,Ltrue)
(set! ,%ac0 ,(%constant strue))
(jump ,%ref-ret (,%ac0)))
(goto ,Lfail))))
,(%seq
(label ,Lfail)
(set! ,%ac0 ,(%constant sfalse))
(jump ,%ref-ret (,%ac0))))))))))]
[(dofargint32) (make-dofargint "dofargint32" 32 (intrinsic-entry-live* dofargint32) (intrinsic-return-live* dofargint32))]
[(dofargint64) (make-dofargint "dofargint64" 64 (intrinsic-entry-live* dofargint64) (intrinsic-return-live* dofargint64))]
[(dofretint32) (make-dofretint "doretint32" 32 (intrinsic-entry-live* dofretint32) (intrinsic-return-live* dofretint32))]
[(dofretint64) (make-dofretint "doretint64" 64 (intrinsic-entry-live* dofretint64) (intrinsic-return-live* dofretint64))]
[(dofretuns32) (make-dofretuns "doretuns32" 32 (intrinsic-entry-live* dofretuns32) (intrinsic-return-live* dofretuns32))]
[(dofretuns64) (make-dofretuns "doretuns64" 64 (intrinsic-entry-live* dofretuns64) (intrinsic-return-live* dofretuns64))]
[(dofretu8*) (make-dofretu* "dofretu8*" 'unsigned-8 1 (intrinsic-entry-live* dofretu8*) (intrinsic-return-live* dofretu8*))]
[(dofretu16*) (make-dofretu* "dofretu16*" 'unsigned-16 2 (intrinsic-entry-live* dofretu16*) (intrinsic-return-live* dofretu16*))]
[(dofretu32*) (make-dofretu* "dofretu32*" 'unsigned-32 4 (intrinsic-entry-live* dofretu32*) (intrinsic-return-live* dofretu32*))]
[(error-invoke) ; more generally "tail-reentry"
`(lambda ,(make-info "error-invoke" '()) 0 ()
,(%seq
,(%inline invoke-prelude)
,(restore-scheme-state
(in %ac0 %ac1 %cp %xp %yp scheme-args)
(out %ts %td extra-regs))
,(meta-cond
[(real-register? '%ret) `(set! ,%ret ,(%mref ,%sfp 0))]
[else `(nop)])
,(do-call)))]
[(invoke)
(let ([Lret (make-local-label 'Lret)]
[Lexit (make-local-label 'Lexit)]
[Lmvreturn (make-local-label 'Lmvreturn)])
`(lambda ,(make-info "invoke" '()) 0 ()
,(%seq
; TODO: add alignment
#;(asm align) ; must start aligned or align below may fail
,(%inline invoke-prelude)
,(restore-scheme-state
(in %ac0 %cp scheme-args)
(out %ac1 %xp %yp %ts %td extra-regs))
(new-frame ,(make-info-newframe #f #f '() '() '()) ,'() ... ,Lret)
; NB: hack!!!
(set! ,%sfp ,(%inline - ,%sfp (immediate ,(constant ptr-bytes))))
(set! ,%ref-ret (label-ref ,Lret ,(constant size-rp-header)))
(tail ,(do-call)) ; argcnt already in ac0
#;(asm align)
(label ,Lret)
(rp-header ,Lmvreturn ,(* 2 (constant ptr-bytes)) 1) ; cchain is live at sfp[ptr-bytes]
(set! ,(ref-reg %ac1) (immediate 1)) ; single-value as expected
,(save-scheme-state
(in %ac0 %ac1)
(out %cp %xp %yp %ts %td scheme-args extra-regs))
(label ,Lexit)
(inline ,(make-info-c-simple-call #f (lookup-c-entry Sreturn)) ,%c-simple-call)
(label ,Lmvreturn)
(set! ,(ref-reg %ac1) ,%ac0)
,(save-scheme-state
(in %ac0 %ac1 scheme-args)
(out %cp %xp %yp %ts %td extra-regs))
(goto ,Lexit))))]
[else ($oops who "unrecognized hand-coded name ~s" sym)])]))
(define-pass np-expose-allocation-pointer : L13.5 (ir) -> L14 ()
; NB: uses %ts when %ap is not a real register
; NB: should use an unspillable, but we don't have unspillables yet
(definitions
(define local*)
(define make-tmp
(lambda (x)
(import (only np-languages make-tmp))
(let ([x (make-tmp x)])
(set! local* (cons x local*))
x)))
(define refap (with-output-language (L14 Triv) (ref-reg %ap)))
(define refeap (with-output-language (L14 Triv) (ref-reg %eap)))
(with-output-language (L14 Effect)
(define build-alloc
(lambda (info lvalue t)
(let ([Lget-room (make-local-label 'Lget-room)])
((lambda (p)
(meta-cond
[(real-register? '%ap) (p %ap values)]
[else `(seq (set! ,%ts ,refap) ,(p %ts (lambda (e) `(seq ,e (set! ,refap ,%ts)))))]))
(lambda (ap store-ap)
(%seq
(set! ,%xp ,(%inline + ,ap (immediate ,(- (info-alloc-tag info) (constant typemod)))))
,(nanopass-case (L14 Triv) t
[(immediate ,imm)
(guard (fixnum? imm) (fx< imm (constant bytes-per-segment)))
; reset_allocation_pointer never uses the last segment of the address
; space, so we can allocate less than bytes-per-segment w/o carry check
(store-ap `(set! ,ap ,(%inline + ,ap ,t)))]
[else
(%seq
,(store-ap `(set! ,ap ,(%inline +/carry ,ap ,t)))
(if (inline ,(make-info-condition-code 'carry #f #t) ,%condition-code)
(goto ,Lget-room)
(nop)))])
(if ,(%inline u< ,refeap ,ap)
,(%seq
(label ,Lget-room)
(pariah)
,((lambda (e)
(if (info-alloc-save-flrv? info)
(%seq ,(%inline save-flrv) ,e ,(%inline restore-flrv))
e))
`(set! ,%xp (inline ,(intrinsic-info-asmlib get-room (info-alloc-save-ra? info)) ,%asmlibcall))))
(nop))
(set! ,lvalue ,%xp)))))))
(define (build-inc-cc-counter arg)
(%inline inc-cc-counter ,%tc ,(%constant tc-alloc-counter-disp) ,arg))
(define (build-shift-and-inc-cc-counter t)
(let ([tcnt (make-tmp 'tcnt)])
(%seq
(set! ,tcnt ,(%inline sra ,t ,(%constant log2-ptr-bytes)))
,(build-inc-cc-counter tcnt))))
(define alloc-helper
(lambda (info lvalue t)
(if (generate-allocation-counts)
(nanopass-case (L14 Triv) t
[(immediate ,imm)
(%seq
,(build-inc-cc-counter
(in-context Triv
`(immediate ,(fxsra imm (constant log2-ptr-bytes)))))
,(build-alloc info lvalue t))]
[else
(if (var? t)
(%seq ,(build-shift-and-inc-cc-counter t) ,(build-alloc info lvalue t))
(let ([talloc (make-tmp 'talloc)])
(%seq
(set! ,talloc ,t)
,(build-shift-and-inc-cc-counter talloc)
,(build-alloc info lvalue talloc))))])
(build-alloc info lvalue t))))))
(Effect : Effect (ir) -> Effect ()
[(inline ,info ,effect-prim ,t)
(guard (eq? effect-prim %remember))
(if (real-register? '%eap)
(%seq
(if ,(%inline u< ,refap ,refeap)
(nop)
(seq
(pariah)
(inline ,(intrinsic-info-asmlib scan-remembered-set #f) ,%asmlibcall!)))
(set! ,refeap ,(%inline - ,refeap ,(%constant ptr-bytes)))
; write through to tc so dirty-list bounds are always known in case of an
; invalid memory reference or illegal instruction
(set! (mref ,%tc ,%zero ,(tc-disp %eap)) ,refeap)
(set! ,(%mref ,refeap 0) ,t))
(%seq
(set! ,%td ,refeap)
(if ,(%inline u< ,refap ,%td)
(nop)
,(%seq
(pariah)
(inline ,(intrinsic-info-asmlib scan-remembered-set #f) ,%asmlibcall!)
(set! ,%td ,refeap)))
(set! ,%td ,(%inline - ,%td ,(%constant ptr-bytes)))
(set! ,refeap ,%td)
(set! ,(%mref ,%td 0) ,t)))]
[(set! ,lvalue (alloc ,info ,[t])) (alloc-helper info lvalue t)])
(Tail : Tail (ir) -> Tail ())
(CaseLambdaExpr : CaseLambdaExpr (ir) -> CaseLambdaExpr ()
[(lambda ,info ,max-fv (,local0* ...) ,tlbody)
(fluid-let ([local* local0*])
(let ([tlbody (Tail tlbody)])
`(lambda ,info ,max-fv (,local* ...) ,tlbody)))]))
(define-record-type goto-block
(parent block)
(fields (mutable next))
(nongenerative)
(sealed #t)
(protocol
(lambda (pargs->new)
(rec make-goto-block
(case-lambda
[() (make-goto-block #f)]
[(next) ((pargs->new) next)])))))
(define-record-type if-block
(parent block)
(fields
(mutable pred)
(mutable true)
(mutable false)
(mutable live-out))
(nongenerative)
(sealed #t)
(protocol
(lambda (pargs->new)
(lambda (true false)
((pargs->new) #f true false 'uninitialized)))))
(define-record-type newframe-block
(parent block)
(fields
info
(mutable next)
(mutable rp*)
(mutable rp)
(mutable live-rp)
(mutable live-call)
(mutable live-out))
(nongenerative)
(sealed #t)
(protocol
(lambda (pargs->new)
(lambda (info next)
((pargs->new) info next #f #f 'uninitialized 'uninitialized 'uninitialized)))))
(define-record-type joto-block
(parent block)
(fields nfv* (mutable next))
(nongenerative)
(sealed #t)
(protocol
(lambda (pargs->new)
(lambda (nfv*)
((pargs->new) nfv* #f)))))
(define-record-type tail-block
(parent block)
(fields (mutable tail) (mutable exit))
(nongenerative)
(sealed #t)
(protocol
(lambda (pargs->new)
(lambda ()
((pargs->new) #f #f)))))
(define-record-type bcache
(fields effect*)
(nongenerative)
(protocol
(lambda (new)
(lambda (block)
(new (block-effect* block))))))
(define-record-type if-bcache
(parent bcache)
(fields pred)
(nongenerative)
(sealed #t)
(protocol
(lambda (pargs->new)
(lambda (block)
((pargs->new block) (if-block-pred block))))))
(define-record-type tail-bcache
(parent bcache)
(fields tail)
(nongenerative)
(sealed #t)
(protocol
(lambda (pargs->new)
(lambda (block)
((pargs->new block) (tail-block-tail block))))))
(define-who cache-block-info
(lambda (block)
(cond
[(or (goto-block? block) (joto-block? block) (newframe-block? block)) (make-bcache block)]
[(if-block? block) (make-if-bcache block)]
[(tail-block? block) (make-tail-bcache block)]
[else (sorry! who "unrecognized block ~s" block)])))
(define-who restore-block-info!
(lambda (block bcache)
(block-effect*-set! block (bcache-effect* bcache))
(cond
[(or (goto-block? block) (joto-block? block) (newframe-block? block)) (void)]
[(if-block? block) (if-block-pred-set! block (if-bcache-pred bcache))]
[(tail-block? block) (tail-block-tail-set! block (tail-bcache-tail bcache))]
[else (sorry! who "unrecognized block ~s" block)])))
(define-pass np-expose-basic-blocks : L14 (ir) -> L15a ()
(definitions
(define add-instr!
(lambda (block ir)
(block-effect*-set! block (cons ir (block-effect* block)))))
(define add-label-link!
(lambda (from l setter)
(let ([x (local-label-block l)])
(if (block? x)
(setter from x)
(local-label-block-set! l (cons (lambda (to) (setter from to)) (or x '())))))))
(define resolve-waiting-links!
(lambda (l to)
(let ([x (local-label-block l)])
(safe-assert (not (block? x)))
(when x (for-each (lambda (add-link!) (add-link! to)) x))
(local-label-block-set! l to))))
(define-pass build-graph : (L14 Tail) (ir) -> * (block block*)
(definitions
(define add-goto-block
(lambda (l block*)
(if (local-label? l)
(let ([block (make-goto-block)])
(add-label-link! block l goto-block-next-set!)
(values block (cons block block*)))
(let ([block (make-tail-block)])
(tail-block-tail-set! block (with-output-language (L15a Tail) `(goto ,l)))
(values block (cons block block*))))))
(define add-true/false-block
(lambda (target block* label-name)
(let ([block (make-goto-block target)])
(unless (block-label target)
(block-label-set! target (make-local-label label-name)))
(values block (cons block block*))))))
(Lvalue : Lvalue (ir target) -> * (ir)
[,x x]
[(mref ,x1 ,x2 ,imm) (with-output-language (L15a Lvalue) `(mref ,x1 ,x2 ,imm))])
(Triv : Triv (ir target) -> * (ir)
[(literal ,info) (with-output-language (L15a Triv) `(literal ,info))]
[(immediate ,imm) (with-output-language (L15a Triv) `(immediate ,imm))]
[,lvalue (Lvalue lvalue target)]
[(label-ref ,l ,offset) (with-output-language (L15a Triv) `(label-ref ,l ,offset))])
;; TODO: framework should come up with some way of handling or complaining about a
;; (maybe foo) when returning from a multiple value case.
(Rhs : Rhs (ir target) -> * (ir)
[(inline ,info ,value-prim ,[Triv : t target -> t] ...)
(with-output-language (L15a Rhs) `(inline ,info ,value-prim ,t ...))]
[,t (Triv t target)])
(Tail : Tail (ir block*) -> * (block block*)
[(goto ,l) (add-goto-block l block*)]
[(seq ,e0 ,[block block*]) (Effect e0 block block*)]
[(if ,p0 ,tl1 ,[f-block block*])
(let-values ([(t-block block*) (Tail tl1 block*)])
(Pred p0 t-block f-block block*))]
[(jump ,t (,var* ...))
(let ([block (make-tail-block)])
(tail-block-tail-set! block
(with-output-language (L15a Tail)
`(jump ,(make-live-info) ,(Triv t block) (,var* ...))))
(values block (cons block block*)))]
[(joto ,l (,nfv* ...))
(let ([block (make-joto-block nfv*)])
(add-label-link! block l joto-block-next-set!)
(values block (cons block block*)))]
[(asm-return ,reg* ...)
(let ([block (make-tail-block)])
(tail-block-tail-set! block (with-output-language (L15a Tail) `(asm-return ,reg* ...)))
(values block (cons block block*)))]
[(asm-c-return ,info ,reg* ...)
(let ([block (make-tail-block)])
(tail-block-tail-set! block (with-output-language (L15a Tail) `(asm-c-return ,info ,reg* ...)))
(values block (cons block block*)))]
[else ($oops who "unexpected Tail ~s" ir)])
(Effect : Effect (ir target block*) -> * (target block*)
[(nop) (values target block*)]
[(inline ,info ,effect-prim ,[Triv : t target -> t] ...)
(add-instr! target (with-output-language (L15a Effect) `(inline ,(make-live-info) ,info ,effect-prim ,t ...)))
(values target block*)]
[(overflow-check)
(add-instr! target (with-output-language (L15a Effect) `(overflow-check ,(make-live-info))))
(values target block*)]
[(overflood-check)
(add-instr! target (with-output-language (L15a Effect) `(overflood-check ,(make-live-info))))
(values target block*)]
[(fcallable-overflow-check)
(add-instr! target (with-output-language (L15a Effect) `(fcallable-overflow-check ,(make-live-info))))
(values target block*)]
[(new-frame ,info ,rpl* ... ,rpl)
(let ([block (make-newframe-block info target)] [l (make-local-label 'docall)])
(block-label-set! target l)
(let ([rp* (fold-left (lambda (ls rp) (cons #f ls)) '() rpl*)])
(newframe-block-rp*-set! block rp*)
(let loop ([rpl* rpl*] [rp* rp*])
(unless (null? rpl*)
(add-label-link! rp* (car rpl*) set-car!)
(loop (cdr rpl*) (cdr rp*)))))
(add-label-link! block rpl newframe-block-rp-set!)
(values block (cons block block*)))]
[(remove-frame ,info)
(add-instr! target (with-output-language (L15a Effect) `(remove-frame ,(make-live-info) ,info)))
(values target block*)]
[(restore-local-saves ,info)
(add-instr! target (with-output-language (L15a Effect) `(restore-local-saves ,(make-live-info) ,info)))
(values target block*)]
[(return-point ,info ,rpl ,mrvl (,cnfv* ...))
(add-instr! target (with-output-language (L15a Effect) `(return-point ,info ,rpl ,mrvl (,cnfv* ...))))
(block-return-point! target #t)
(values target block*)]
[(rp-header ,mrvl ,fs ,lpm)
(add-instr! target (with-output-language (L15a Effect) `(rp-header ,mrvl ,fs ,lpm)))
(block-return-point! target #t)
(values target block*)]
[(shift-arg ,reg ,imm ,info)
(add-instr! target (with-output-language (L15a Effect) `(shift-arg ,(make-live-info) ,reg ,imm ,info)))
(values target block*)]
[(pariah)
(block-pariah! target #t)
(values target block*)]
[(profile ,src)
(block-src*-set! target (cons src (block-src* target)))
(values target block*)]
[(tail ,tl) (Tail tl block*)]
[(label ,l)
(block-label-set! target l)
(resolve-waiting-links! l target)
(let ([block (make-goto-block target)])
(values block (cons block block*)))]
[(goto ,l) (add-goto-block l block*)]
[(seq ,e0 ,[block block*]) (Effect e0 block block*)]
[(set! ,[Lvalue : lvalue target -> lvalue] ,[Rhs : rhs target -> rhs])
(add-instr! target (with-output-language (L15a Effect) `(set! ,(make-live-info) ,lvalue ,rhs)))
(values target block*)]
[(if ,p0 ,e1 ,e2)
(let ([t-block (make-goto-block target)] [f-block (make-goto-block target)] [l (make-local-label 'ej)])
(let ([block* (cons* t-block f-block block*)])
(block-label-set! target l)
(let-values ([(f-block block*) (Effect e2 f-block block*)])
(let-values ([(t-block block*) (Effect e1 t-block block*)])
(Pred p0 t-block f-block block*)))))]
[(check-live ,reg* ...)
(add-instr! target (with-output-language (L15a Effect) `(check-live ,(make-live-info) ,reg* ...)))
(values target block*)]
[else ($oops who "unexpected Effect ~s" ir)])
(Pred : Pred (ir t-target f-target block*) -> * (block block*)
[(true) (add-true/false-block t-target block* 'lt)]
[(false) (add-true/false-block f-target block* 'lf)]
[(inline ,info ,pred-prim ,t* ...)
(let ([block (make-if-block t-target f-target)])
(unless (block-label t-target) (block-label-set! t-target (make-local-label 'lt)))
(unless (block-label f-target) (block-label-set! f-target (make-local-label 'lf)))
(if-block-pred-set! block
(with-output-language (L15a Pred)
`(inline ,(make-live-info) ,info ,pred-prim ,(map (lambda (t) (Triv t block)) t*) ...)))
(values block (cons block block*)))]
[(seq ,e0 ,[block block*]) (Effect e0 block block*)]
[(goto ,l) (add-goto-block l block*)]
[(if ,p0 ,p1 ,[f-block block*])
(let-values ([(t-block block*) (Pred p1 t-target f-target block*)])
(Pred p0 t-block f-block block*))]
[(mlabel ,p (,l* ,p*) ...)
(let loop ([l* l*] [p* p*] [block* block*])
(if (null? l*)
(Pred p t-target f-target block*)
(let-values ([(block block*) (Pred (car p*) t-target f-target block*)])
(let ([l (car l*)])
(resolve-waiting-links! l block)
(block-label-set! block l)
(loop (cdr l*) (cdr p*) block*)))))]
[else ($oops who "unexpected Pred ~s" ir)])
(Tail ir '())))
(CaseLambdaExpr : CaseLambdaExpr (ir) -> CaseLambdaExpr ()
[(lambda ,info ,max-fv (,local* ...) ,tlbody)
(let-values ([(entry-block block*) (build-graph tlbody)])
(unless (block-label entry-block)
(let ([label (make-local-label 'entry)])
(local-label-block-set! label entry-block)
(block-label-set! entry-block label)))
; NB: if entry-block is not a dcl block, it must appear first in entry-block*,
; NB: as it is the generic entry point for the procedure
(let ([entry-block* (let ([block* (fold-left
(lambda (block* dcl)
(let ([block (local-label-block dcl)])
(if (block? block) (cons block block*) block*)))
'() (info-lambda-dcl* info))])
(if (memq entry-block block*) block* (cons entry-block block*)))])
; mark reachable blocks
(for-each
(rec mark!
(lambda (from)
(unless (block-seen? from)
(block-seen! from #t)
(cond
[(goto-block? from) (mark! (goto-block-next from))]
[(joto-block? from) (mark! (joto-block-next from))]
[(if-block? from) (mark! (if-block-true from)) (mark! (if-block-false from))]
[(newframe-block? from)
(mark! (newframe-block-next from))
(for-each mark! (newframe-block-rp* from))
(mark! (newframe-block-rp from))]
[(tail-block? from) (void)]
[else (sorry! who "unrecognized from ~s" from)]))))
entry-block*)
; discard unreachable blocks, some of of which build-graph stupidly produces
(let ([block* (filter block-seen? block*)])
(for-each (lambda (block) (block-seen! block #f)) block*)
(safe-assert (andmap block-label (append entry-block* block*)))
(safe-assert (lambda (b) (eq? (local-label-block (block-label b)) b)) (append entry-block* block*))
`(lambda ,info ,max-fv (,local* ...) (,entry-block* ...) (,block* ...)))))]))
(define-pass np-add-block-source! : L15a (ir) -> L15a ()
(definitions
(define block-checksum
(lambda (block)
(fxlogor
(fxsll (fxlogand (length (block-effect* block)) (fxsrl (most-positive-fixnum) 3)) 3)
(cond
[(goto-block? block) #x001]
[(joto-block? block) #x010]
[(if-block? block) #x011]
[(newframe-block? block) #x100]
[(tail-block? block) #x101]
[else (sorry! who "unrecognized block ~s" block)])))))
(CaseLambdaExpr : CaseLambdaExpr (ir) -> CaseLambdaExpr ()
[(lambda ,info ,max-fv (,local* ...) (,entry-block* ...) (,block* ...))
(for-each
(lambda (block)
(include "types.ss")
(let ([n (fx- ($block-counter) 1)])
($block-counter n)
(block-pseudo-src-set! block
(make-source ($sfd) n (block-checksum block)))))
block*)
ir]))
(define-pass np-remove-repeater-blocks! : L15a (ir) -> L15a ()
(definitions
(define path-compress!
(lambda (b)
(cond
[(block-repeater? b) (goto-block-next b)]
[(and (goto-block? b) (null? (block-effect* b)) (null? (block-src* b)))
(block-repeater! b #t)
(let ([end (path-compress! (goto-block-next b))])
(goto-block-next-set! b end)
end)]
[else b])))
(define resolve
(lambda (b)
(if (block-repeater? b)
(goto-block-next b)
b))))
(CaseLambdaExpr : CaseLambdaExpr (ir) -> CaseLambdaExpr ()
[(lambda ,info ,max-fv (,local* ...) (,entry-block* ...) (,block* ...))
(for-each path-compress! block*)
(for-each
(lambda (from)
(define resolve!
(lambda (get put!)
(let ([to (get from)])
(when (block-repeater? to)
(put! from (goto-block-next to))))))
(cond
[(goto-block? from)
(unless (block-repeater? from)
(resolve! goto-block-next goto-block-next-set!))]
[(joto-block? from)
(resolve! joto-block-next joto-block-next-set!)]
[(if-block? from)
(resolve! if-block-true if-block-true-set!)
(resolve! if-block-false if-block-false-set!)]
[(newframe-block? from)
(resolve! newframe-block-next newframe-block-next-set!)
(newframe-block-rp*-set! from (map resolve (newframe-block-rp* from)))
(resolve! newframe-block-rp newframe-block-rp-set!)]
[(tail-block? from) (void)]
[else (sorry! who "unrecognized block ~s" from)]))
block*)
(for-each (lambda (dcl)
(let* ([b0 (local-label-block dcl)] [b (and b0 (resolve b0))])
(unless (eq? b b0)
(local-label-block-set! dcl b)
(block-label-set! b dcl))))
(info-lambda-dcl* info))
`(lambda ,info ,max-fv (,local* ...)
(,(map resolve entry-block*) ...)
(,(filter (lambda (b) (or (not (block-repeater? b)) (eq? (goto-block-next b) b))) block*) ...))]))
(define-pass np-propagate-pariahty! : L15a (ir) -> L15a ()
(definitions
(define propagate!
(lambda (b)
(unless (block-seen? b)
(block-seen! b #t)
(block-pariah! b #f)
(cond
[(goto-block? b) (propagate! (goto-block-next b))]
[(joto-block? b) (propagate! (joto-block-next b))]
[(if-block? b)
; could set likely branch direction before marking targets as pariahs,
; but these are all pariah blocks anyway
(propagate! (if-block-true b))
(propagate! (if-block-false b))]
[(newframe-block? b)
(propagate! (newframe-block-next b))
(for-each propagate! (newframe-block-rp* b))
(propagate! (newframe-block-rp b))]
[(tail-block? b) (void)]
[else (sorry! who "unrecognized block ~s" b)])))))
(CaseLambdaExpr : CaseLambdaExpr (ir) -> CaseLambdaExpr ()
[(lambda ,info ,max-fv (,local* ...) (,entry-block* ...) (,block* ...))
(safe-assert (not (ormap block-seen? block*)))
; optimistically assume all blocks are pariahs, then un-pariah anything reachable from
; the entry block without going through a known pariah block
(for-each (lambda (b) (if (block-pariah? b) (block-seen! b #t) (block-pariah! b #t))) block*)
(for-each propagate! entry-block*)
(for-each (lambda (b) (block-seen! b #f)) block*)
ir]))
(module (np-insert-profiling)
(include "types.ss")
(define-record-type start-block
(parent block)
(fields
(mutable link*))
(nongenerative)
(sealed #t)
(protocol
(lambda (pargs->new)
(lambda ()
((pargs->new) '())))))
(define-record-type link
(fields
from
(mutable to)
(mutable weight)
(mutable mst)
(mutable counter)
(mutable op))
(nongenerative)
(sealed #t)
(protocol
(lambda (new)
(lambda (from to)
(new from to 0 #f #f #f)))))
(define-who add-link-records!
; also adds exit-block links
(lambda (start-block exit-block entry-block* block*)
(define do-link
(lambda (from to)
(let ([link (make-link from to)])
(block-in-link*-set! to (cons link (block-in-link* to)))
(unless (block-seen? to)
(block-seen! to #t)
(cond
[(goto-block? to) (goto-block-next-set! to (do-link to (goto-block-next to)))]
[(joto-block? to) (joto-block-next-set! to (do-link to (joto-block-next to)))]
[(if-block? to)
(if-block-true-set! to (do-link to (if-block-true to)))
(if-block-false-set! to (do-link to (if-block-false to)))]
[(tail-block? to) (tail-block-exit-set! to (do-link to exit-block))]
[(newframe-block? to)
(newframe-block-next-set! to (do-link to (newframe-block-next to)))
; link start-block to rp blocks since they are, in reality, extra entry points that
; need to be measured separately due to the potential for control operations
(let ([rplink* (map (lambda (rp) (do-link start-block rp)) (newframe-block-rp* to))]
[rplink (do-link start-block (newframe-block-rp to))])
(start-block-link*-set! start-block (append rplink* (cons rplink (start-block-link* start-block))))
; and also record links in newframe-block for remove-link-records!
(newframe-block-rp*-set! to rplink*)
(newframe-block-rp-set! to rplink))]
[else (sorry! who "unrecognized block ~s" to)]))
link)))
(let ([all-block* (cons* start-block exit-block block*)])
(for-each (lambda (block) (block-in-link*-set! block '())) all-block*)
(block-seen! start-block #t)
(let ([entry-link* (map (lambda (to) (do-link start-block to)) entry-block*)])
(start-block-link*-set! start-block (append entry-link* (start-block-link* start-block)))
(for-each (lambda (block) (block-seen! block #f)) all-block*)
entry-link*))))
(define-who remove-link-records!
(lambda (block*)
(for-each
(lambda (block)
(cond
[(goto-block? block) (goto-block-next-set! block (link-to (goto-block-next block)))]
[(joto-block? block) (joto-block-next-set! block (link-to (joto-block-next block)))]
[(if-block? block)
(if-block-true-set! block (link-to (if-block-true block)))
(if-block-false-set! block (link-to (if-block-false block)))]
[(tail-block? block) (tail-block-exit-set! block #f)]
[(newframe-block? block)
(newframe-block-next-set! block (link-to (newframe-block-next block)))
(newframe-block-rp*-set! block (map link-to (newframe-block-rp* block)))
(newframe-block-rp-set! block (link-to (newframe-block-rp block)))]
[else (sorry! who "unrecognized block ~s" block)])
(block-in-link*-set! block '()))
block*)))
(define weight-graph!
(lambda (start-block exit-block block*)
(define sum-link-weights
(lambda (links)
; using #3$fx+ to ensure that we wrap when we go over the fixnum range
(fold-left (lambda (n link) (#3%fx+ (link-weight link) n)) 0 links)))
(define-who process-link
(lambda (ls link)
(let ([block (link-to link)])
(cond
[(block-finished? block) ls]
[(block-seen? block) ; cycle?
(link-weight-set! link 500)
ls]
[else
(block-seen! block #t)
(let ([ls (cond
[(goto-block? block) (process-link ls (goto-block-next block))]
[(joto-block? block) (process-link ls (joto-block-next block))]
[(if-block? block) (process-link (process-link ls (if-block-false block)) (if-block-true block))]
[(tail-block? block) ls]
[(newframe-block? block) (process-link ls (newframe-block-next block))]
[else (sorry! who "unrecognized block ~s" block)])])
(block-finished! block #t)
(cons block ls))]))))
(define-who propagate-flow
(lambda (block)
(let ([sum (sum-link-weights (block-in-link* block))]
[links (cond
[(goto-block? block) (list (goto-block-next block))]
[(joto-block? block) (list (joto-block-next block))]
[(if-block? block) (list (if-block-true block) (if-block-false block))]
[(tail-block? block) (list (tail-block-exit block))]
[(newframe-block? block) (list (newframe-block-next block))]
[else (sorry! who "unrecognized block ~s" block)])])
(safe-assert (not (null? links)))
; AWK: we are missing the notion of those instructions that usually
; succeed (dooverflow, dooverflood, call-error, fx+? and fx-? in
; the original blocks.ss code)
(let-values ([(pariah* non-pariah*)
(partition (lambda (link) (block-pariah? (link-to link))) links)])
(if (null? non-pariah*)
(divide-flow sum (length pariah*) pariah*)
(divide-flow sum (length non-pariah*) non-pariah*))))))
(define divide-flow
(lambda (flow n ls)
(safe-assert (fx> n 0))
(if (fx= n 1)
(link-weight-set! (car ls) flow)
(let ([x (fxquotient flow n)])
(link-weight-set! (car ls) x)
(divide-flow (fx- flow x) (fx- n 1) (cdr ls))))))
(let ([exit->start (goto-block-next exit-block)])
(block-finished! start-block #t)
(block-finished! exit-block #t)
; DFS to find cycles & determine order to propagate flow
(link-weight-set! exit->start 1000)
(for-each propagate-flow (fold-left process-link '() (start-block-link* start-block)))
(for-each (lambda (block) (block-seen! block #f)) (cons* start-block exit-block block*)))))
(module (mst-top)
(define-who mst-top
(lambda (start-block exit-block block*)
(block-seen! start-block #t)
(block-seen! exit-block #t)
(let ([pq (pqinitialize (length block*))])
(define (mst-in-link link) (pqupdate link (link-from link) pq))
(define (mst-out-link link) (pqupdate link (link-to link) pq))
; add the exit->start link to the mst
(link-mst-set! (goto-block-next exit-block) exit-block)
(for-each mst-out-link (start-block-link* start-block))
(let mst ()
(unless (pqempty? pq)
(let ([r (pqremove pq)])
(let ([block (cdr r)] [link (car r)])
(link-mst-set! link block)
(for-each mst-in-link (block-in-link* block))
(cond
[(goto-block? block) (mst-out-link (goto-block-next block))]
[(joto-block? block) (mst-out-link (joto-block-next block))]
[(if-block? block) (mst-out-link (if-block-true block)) (mst-out-link (if-block-false block))]
[(tail-block? block) (mst-out-link (tail-block-exit block))]
[(newframe-block? block) (mst-out-link (newframe-block-next block))]
[else (sorry! who "unrecognized block ~s" block)])
(mst))))))))
(define pqinitialize
(let ([b (make-block)]) ;; add dummy first block in the priority-queue
(let ([l (make-link #f b)])
(link-weight-set! l (most-positive-fixnum))
(let ([pqfirst (cons l b)])
(lambda (size)
(cons 0 (make-vector (fx+ size 1) pqfirst)))))))
(define pqupheap
(lambda (heap k w)
(let ([y (vector-ref heap (fx/ k 2))])
(if (fx> w (link-weight (car y)))
(begin
(vector-set! heap k y)
(block-seen! (cdr y) k)
(pqupheap heap (fx/ k 2) w))
k))))
(define pqdownheap
(lambda (heap n k w)
(if (fx< (fx/ n 2) k)
k
(let ([j (fx* k 2)])
(let ([y1 (vector-ref heap j)]
[y2 (and (fx< j n) (vector-ref heap (fx+ j 1)))])
(let ([w1 (link-weight (car y1))]
[w2 (if y2 (link-weight (car y2)) (most-negative-fixnum))])
(if (fx>= w1 w2)
(if (fx>= w w1)
k
(begin
(vector-set! heap k y1)
(block-seen! (cdr y1) k)
(pqdownheap heap n j w)))
(if (fx>= w w2)
k
(begin
(vector-set! heap k y2)
(block-seen! (cdr y2) k)
(pqdownheap heap n (fx+ j 1) w))))))))))
(define pqempty?
(lambda (pq)
(fx= (car pq) 0)))
(define pqremove
(lambda (pq)
(let ([n (fx- (car pq) 1)]
[heap (cdr pq)])
(set-car! pq n)
(let ([r (vector-ref heap 1)]
[x (vector-ref heap (fx+ n 1))])
(let ([k (pqdownheap heap n 1 (link-weight (car x)))])
(vector-set! heap k x)
(block-seen! (cdr x) k))
(block-seen! (cdr r) #t)
r))))
(define pqupdate
(lambda (link block pq)
(let ([k (block-seen? block)])
(cond
[(eq? k #t) (void)]
[(eq? k #f)
(let ([n (fx+ (car pq) 1)] [heap (cdr pq)])
(set-car! pq n)
(let ([k (pqupheap heap n (link-weight link))])
(vector-set! heap k (cons link block))
(block-seen! block k)))]
[else
(let ([heap (cdr pq)])
(let ([x (vector-ref heap k)]
[w (link-weight link)])
(when (fx> w (link-weight (car x)))
(let ([k (pqupheap heap k w)])
(vector-set! heap k (cons link block))
(block-seen! block k)))))])))))
(define-who instrument
(lambda (start-block exit-block block*)
(define checks-cc?
(lambda (block)
(and (if-block? block)
(null? (block-effect* block))
(nanopass-case (L15a Pred) (if-block-pred block)
[(inline ,live-info ,info ,pred-prim ,t* ...) (eq? pred-prim %condition-code)]
[else #f]))))
(define add-counter!
(lambda (block counter)
(define add-instr!
(lambda (block ir)
(let ([effect* (block-effect* block)])
(block-effect*-set! block
(if (block-return-point? block)
; rp-header / return-point form must be first
(cons* (car effect*) ir (cdr effect*))
(cons ir effect*))))))
(with-output-language (L15a Effect)
(add-instr! block
`(inline ,(make-live-info) ,null-info ,%inc-profile-counter
(literal ,(make-info-literal #t 'object counter (constant record-data-disp)))
(immediate 1))))))
(define maybe-add-counter
(lambda (new* link)
(cond
[(link-counter link) =>
(lambda (counter)
(let ([from (link-from link)] [to (link-to link)])
(cond
[(and (fx= (length (block-in-link* to)) 1) (not (eq? to exit-block)))
(assert (not (checks-cc? to)))
(add-counter! to counter)
new*]
[(or (goto-block? from) (tail-block? from))
(assert (not (checks-cc? from)))
(add-counter! from counter)
new*]
[else
(safe-assert (not (eq? to exit-block)))
(assert (not (checks-cc? to)))
(let* ([block (make-goto-block)] [l (make-link block to)])
(let ([label (block-label to)])
(if (and (eq? from start-block) (and (direct-call-label? label) (direct-call-label-referenced label)))
(begin
; we're adding the new block between the (virtual) start block and one
; of our (referenced) dcls. we need to move the dcl label to the new
; block so the counter is incremented when we come in from the outside
(block-label-set! block label)
(local-label-block-set! label block)
(let ([label (make-local-label 'exdcl)])
(block-label-set! to label)
(local-label-block-set! label to)))
(let ([label (make-local-label 'profile)])
(block-label-set! block label)
(local-label-block-set! label block))))
(link-to-set! link block)
; set link mst for p-dot-graph/profiling's benefit
(link-mst-set! l block)
(block-in-link*-set! block (list link))
(goto-block-next-set! block l)
(block-in-link*-set! to (cons l (remq link (block-in-link* to))))
(add-counter! block counter)
(cons block new*))])))]
[else new*])))
(fold-left
(lambda (new* block)
(fold-left maybe-add-counter
new* (block-in-link* block)))
block*
(cons exit-block block*))))
(define build-pinfo
(lambda (exit-block block*)
; op -> counter | (plus-counter* . minus-counter*)
; plus-counter* -> (op ...)
; minus-counter* -> (op ...)
(define make-op
(lambda (plus minus)
; optimize ((op) . ()) => op
(if (and (null? minus) (fx= (length plus) 1))
(car plus)
(cons plus minus))))
(define-who exit-ops
(lambda (block l)
(define maybe-build-op
(lambda (link ls)
(if (eq? link l)
ls
(cons (build-op link) ls))))
(cond
[(goto-block? block) (maybe-build-op (goto-block-next block) '())]
[(joto-block? block) (maybe-build-op (joto-block-next block) '())]
[(if-block? block) (maybe-build-op (if-block-true block) (maybe-build-op (if-block-false block) '()))]
[(tail-block? block) (maybe-build-op (tail-block-exit block) '())]
[(newframe-block? block) (maybe-build-op (newframe-block-next block) '())]
[else (sorry! who "unrecognized block ~s" block)])))
(define enter-ops
(lambda (n l)
(let ([ls (block-in-link* n)])
(map build-op (if (not l) ls (remq l ls))))))
(define build-op
(lambda (l)
(cond
[(link-mst l) =>
(lambda (n)
(let ([op (if (eq? (link-to l) n)
(make-op (exit-ops n #f) (enter-ops n l))
(make-op (enter-ops n #f) (exit-ops n l)))])
(link-op-set! l op)
op))]
[else
(or (link-counter l)
(let ([counter (make-profile-counter 0)])
(link-counter-set! l counter)
(link-op-set! l counter)
counter))])))
(define (filter-src* block)
(cond
[(eq? ($compile-profile) 'source) (block-src* block)]
[(block-pseudo-src block) => list]
[else '()]))
(fold-left
(lambda (ls block)
(let ([src* (filter-src* block)])
(if (null? src*)
ls
(cons (make-rblock src* (make-op (map build-op (block-in-link* block)) '())) ls))))
'() block*)))
(module (p-graph/profiling p-dot-graph/profiling)
(define-who block-link*
(lambda (block)
(cond
[(goto-block? block) `(,(goto-block-next block))]
[(joto-block? block) `(,(joto-block-next block))]
[(if-block? block) `(,(if-block-true block) ,(if-block-false block))]
; leave out newframe-block => rp links, since we profiler uses its own start-block => rp links
[(newframe-block? block) `(,(newframe-block-next block))]
[(tail-block? block) `(,(tail-block-exit block))]
[(start-block? block) (start-block-link* block)]
[else (sorry! who "unrecognized block ~s" block)])))
(define block->pretty-name
(lambda (block)
(define block->label
(lambda (block)
(let ([label (block-label block)])
(or label
(let ([label (make-local-label 'unknown)])
(block-label-set! block label)
label)))))
(parameterize ([print-gensym 'pretty/suffix]) (format "~s" (block->label block)))))
(define p-dot-graph/profiling
(lambda (block* exit-block p)
(define print-link
(lambda (reversed?)
(lambda (link)
(let-values ([(from to) (if reversed?
(values (link-to link) (link-from link))
(values (link-from link) (link-to link)))])
(display " " p)
(display (block->pretty-string from) p)
(display " -> " p)
(display (block->pretty-string to) p)
#;(when (and (block-non-tail-call? (link-from link)) (eq? (link-to link) exit-block))
(display " [color=grey]" p))
(if (link-mst link)
(if reversed?
(display " [color=blue]" p)
(display " [color=black]" p))
(if reversed?
(display " [color=pink]" p)
(display " [color=red]" p)))
(write-char #\; p)
(newline p))
; print the tree in green
#;(when (link-mst link)
(let-values ([(from to) (if (eq? (link-mst link) (link-to link))
(values (link-from link) (link-to link))
(values (link-to link) (link-from link)))])
(display " " p)
(display (block->pretty-string from) p)
(display " -> " p)
(display (block->pretty-string to) p)
(display " [color=green];\n" p))))))
(define block->pretty-string
(lambda (block)
(list->string (subst #\_ #\. (subst #\_ #\- (string->list (block->pretty-name block)))))))
(newline p)
(display "digraph PROFILE {\n" p)
(display " node [shape = box];" p)
(let f ([block* block*] [link* '()] [in-link* '()])
(if (null? block*)
(begin
(newline p)
(newline p)
(for-each (print-link #f) link*)
(when #f (for-each (print-link #t) in-link*))
(display "}\n" p))
(let ([block (car block*)])
(display " " p)
(display (block->pretty-string block) p)
(f (cdr block*)
(append (block-link* block) link*)
(append (block-in-link* block) in-link*)))))))
(define-who p-graph/profiling
(lambda (block* name p)
(newline p)
(when name (fprintf p "~a:\n" name))
(parameterize ([print-graph #t] [print-length 6] [print-level 3] [print-gensym 'pretty/suffix])
(for-each
(lambda (block)
(fprintf p "~a: " (block->pretty-name block))
(let loop ([links (block-link* block)])
(unless (null? links)
(let ([link (car links)])
(fprintf p "~a(~d)~a"
(block->pretty-name (link-to link))
(link-weight link)
(if (link-mst link)
""
"*"))
(unless (null? (cdr links)) (display ", " p))
(loop (cdr links)))))
(fprintf p " in=~d:" (length (block-in-link* block)))
(begin
(newline p)
(for-each
(lambda (link)
(cond
[(link-counter link) (fprintf p " Bump count to ~a\n" (block->pretty-name (link-to link)))]
[(link-op link) (fprintf p " Link count to ~a computed from other counts\n" (block->pretty-name (link-to link)))])
(fprintf p " ~a -> ~a -- ~s\n" (block->pretty-name (link-from link))
(block->pretty-name (link-to link)) (link-op link)))
(block-link* block))
; We no longer have the code to report here, so we're reporting from source
(fprintf p "~{ ~s~%~}" (map unparse-L15a (block-effect* block)))
(cond
[(or (goto-block? block) (joto-block? block) (newframe-block? block) (start-block? block)) (void)]
[(if-block? block) (fprintf p " ~s~%" (unparse-L15a (if-block-pred block)))]
[(tail-block? block) (fprintf p " ~s~%" (unparse-L15a (tail-block-tail block)))]
[else (sorry! who "unrecognized block ~s" block)])))
block*)))))
(define-pass np-insert-profiling : L15a (ir) -> L15a ()
(CaseLambdaExpr : CaseLambdaExpr (ir) -> CaseLambdaExpr ()
[(lambda ,info ,max-fv (,local* ...) (,entry-block* ...) (,block* ...))
(let* ([start-block (make-start-block)]
[exit-block (make-goto-block start-block)])
(block-label-set! start-block 'start)
(block-label-set! exit-block 'exit)
(let ([entry-link* (add-link-records! start-block exit-block entry-block* block*)])
(weight-graph! start-block exit-block block*)
(mst-top start-block exit-block block*)
(info-lambda-pinfo*-set! info (append (build-pinfo exit-block block*) (info-lambda-pinfo* info)))
; now insert increments for counters added by build-pinfo
(let* ([block* (instrument start-block exit-block block*)]
[entry-block* (map link-to entry-link*)])
(safe-assert (andmap (lambda (block) (not (null? (block-in-link* block)))) block*))
(when ($assembly-output)
(let ([block* (cons start-block (append block* (list exit-block)))])
(p-graph/profiling block* (info-lambda-name info) ($assembly-output))
(p-dot-graph/profiling block* exit-block ($assembly-output))))
(remove-link-records! block*)
(for-each (lambda (block) (block-seen! block #f) (block-finished! block #f)) block*)
(safe-assert (andmap block-label (append entry-block* block*)))
(safe-assert (lambda (b) (eq? (local-label-block (block-label b)) b)) (append entry-block* block*))
`(lambda ,info ,max-fv (,local* ...) (,entry-block* ...) (,block* ...)))))])))
(module (p-graph p-dot-graph)
(define block->pretty-name
(lambda (block)
(define block->label
(lambda (block)
(let ([label (block-label block)])
(or label
(let ([label (make-local-label 'unknown)])
(block-label-set! block label)
label)))))
(parameterize ([print-gensym 'pretty/suffix]) (format "~s" (block->label block)))))
(define p-dot-graph
(lambda (block* p)
(define print-link
(lambda (link)
(display " " p)
(display (car link) p)
(display " -> " p)
(display (cdr link) p)
(write-char #\; p)
(newline p)))
(define block->pretty-string
(lambda (block)
(list->string (subst #\_ #\. (subst #\_ #\- (string->list (block->pretty-name block)))))))
(define-who block-link*
(lambda (block)
(let ([block-name (block->pretty-string block)])
(map (lambda (x) (cons block-name (block->pretty-string x)))
(cond
[(goto-block? block) `(,(goto-block-next block))]
[(joto-block? block) `(,(joto-block-next block))]
[(if-block? block) `(,(if-block-true block) ,(if-block-false block))]
[(newframe-block? block) `(,(newframe-block-next block) ,@(newframe-block-rp* block) ,(newframe-block-rp block))]
[(tail-block? block) '()]
[else (sorry! who "unrecognized block ~s" block)])))))
(display "digraph BLOCKS {\n" p)
(display " node [shape = box];" p)
(let f ([block* block*] [link* '()])
(if (null? block*)
(begin
(newline p)
(newline p)
(for-each print-link link*)
(display "}\n" p))
(let ([block (car block*)])
(display " " p)
(display (block->pretty-string block) p)
(when (block-pariah? block) (display " [color=red]" p))
(f (cdr block*) (append (block-link* block) link*)))))))
(define-who p-graph
(lambda (block* name p unparser)
(when name (fprintf p "\n~a:" name))
(parameterize ([print-graph #t] [print-length 6] [print-level 3] [print-gensym 'pretty/suffix])
(for-each
(lambda (block)
(fprintf p "~a (depth = ~s~@[, pariah~]):\n" (block->pretty-name block) (block-depth block) (block-pariah? block))
(fprintf p "~{ ~s~%~}" (map unparser (block-effect* block)))
(cond
[(goto-block? block) (fprintf p " ~s\n" `(goto ,(block->pretty-name (goto-block-next block))))]
[(joto-block? block) (fprintf p " ~s\n" `(joto ,(block->pretty-name (joto-block-next block))))]
[(if-block? block) (fprintf p " ~s\n" `(if ,(unparser (if-block-pred block))
(goto ,(block->pretty-name (if-block-true block)))
(goto ,(block->pretty-name (if-block-false block)))))]
[(tail-block? block) (fprintf p " ~s\n" (unparser (tail-block-tail block)))]
[(newframe-block? block) (fprintf p " ~s\n" `(goto ,(block->pretty-name (newframe-block-next block))))]
[else (sorry! who "unrecognized block ~s" block)]))
block*)))))
(define-pass np-add-in-links! : L15a (ir) -> L15a ()
(CaseLambdaExpr : CaseLambdaExpr (ir) -> CaseLambdaExpr ()
[(lambda ,info ,max-fv (,local* ...) (,entry-block* ...) (,block* ...))
(safe-assert (andmap (lambda (block) (eq? (block-in-link* block) '())) block*))
(for-each
(lambda (from)
(define add-in-link!
(lambda (to)
(block-in-link*-set! to (cons from (block-in-link* to)))))
(cond
[(goto-block? from) (add-in-link! (goto-block-next from))]
[(if-block? from) (add-in-link! (if-block-true from)) (add-in-link! (if-block-false from))]
[(newframe-block? from)
(add-in-link! (newframe-block-next from))
(for-each add-in-link! (newframe-block-rp* from))
(add-in-link! (newframe-block-rp from))]
[(joto-block? from) (add-in-link! (joto-block-next from))]
[(tail-block? from) (void)]
[else (sorry! who "unrecognized block ~s" from)]))
block*)
ir]))
(define-pass np-compute-loop-depth! : L15a (ir) -> L15a ()
(CaseLambdaExpr : CaseLambdaExpr (ir) -> CaseLambdaExpr ()
[(lambda ,info ,max-fv (,local* ...) (,entry-block* ...) (,block* ...))
(safe-assert (not (ormap block-seen? block*)) (not (ormap block-finished? block*)))
(let ([lh* '()])
(for-each
(rec f
(lambda (b)
(unless (block-finished? b)
(if (block-seen? b)
(begin
(block-loop-header! b #t)
(set! lh* (cons b lh*)))
(begin
(block-seen! b #t)
(cond
[(goto-block? b) (f (goto-block-next b))]
[(joto-block? b) (f (joto-block-next b))]
[(if-block? b) (f (if-block-true b)) (f (if-block-false b))]
[(tail-block? b) (void)]
[(newframe-block? b)
(f (newframe-block-next b))
(for-each f (newframe-block-rp* b))
(f (newframe-block-rp b))]
[else (sorry! who "unrecognized block ~s" b)])
(block-seen! b #f)
(block-finished! b #t))))))
entry-block*)
(unless (null? lh*)
(fold-left (lambda (i b) (block-index-set! b i) (fx+ i 1)) 0 lh*)
(let ([tree-size (length lh*)] [blockvec (list->vector lh*)] [lb* lh*])
(define remove-block
(lambda (b tree)
(let ([index (block-index b)])
(if index (tree-bit-unset tree tree-size index) tree))))
; invert sense of block-finished so we don't have to reset
(let ([block-finished? (lambda (b) (not (block-finished? b)))]
[block-finished! (lambda (b bool) (block-finished! b (not bool)))])
(for-each
(rec f
(lambda (b)
(cond
[(block-finished? b)
(tree-fold-left (lambda (lhs index)
(let ([b (vector-ref blockvec index)])
(if (block-finished? b)
lhs
(tree-bit-set lhs tree-size index))))
tree-size empty-tree (block-loop-headers b))]
[(block-seen? b)
(safe-assert (block-index b))
(tree-bit-set empty-tree tree-size (block-index b))]
[(tail-block? b) empty-tree]
[else
(block-seen! b #t)
(let ([lhs (remove-block b
(cond
[(goto-block? b) (f (goto-block-next b))]
[(joto-block? b) (f (joto-block-next b))]
[(if-block? b)
; must follow same order as loop above so we find the same loop headers
(let ([lhs (f (if-block-true b))])
(tree-merge lhs (f (if-block-false b)) tree-size))]
[(newframe-block? b)
; must follow same order as loop above so we find the same loop headers
(fold-left (lambda (lhs b) (tree-merge lhs (f b) tree-size))
(let ([lhs (f (newframe-block-next b))]) (tree-merge lhs (f (newframe-block-rp b)) tree-size))
(newframe-block-rp* b))]
[else (sorry! who "unrecognized block ~s" b)]))])
(unless (or (block-loop-header? b) (eqv? (block-loop-headers b) empty-tree))
(set! lb* (cons b lb*)))
(block-seen! b #f)
(block-finished! b #t)
(block-loop-headers-set! b lhs)
lhs)])))
; seems like we should be able to use (reverse lh*) rather than entry-block* here
; but we end up finding different loop headers in some cases
entry-block*))
(for-each
(rec g
(lambda (b)
(if (block-seen? b)
(block-depth b)
(begin
(block-seen! b #t)
(let ([depth (tree-fold-left (lambda (depth index) (fxmax (g (vector-ref blockvec index)) depth))
tree-size 0 (block-loop-headers b))])
(let ([depth (if (block-loop-header? b) (fx+ depth 1) depth)])
(block-depth-set! b depth)
depth))))))
lb*))
(for-each (lambda (b) (block-seen! b #f)) block*)
#;(p-dot-graph block* (current-output-port))
#;(p-graph block* (info-lambda-name info) (current-output-port) unparse-L15a)))
(for-each (lambda (b) (block-finished! b #f)) block*)
ir]))
(define-pass np-weight-references! : L15a (ir) -> L15a ()
(definitions
(define weight-block!
(lambda (max-weight)
(lambda (block weight)
(let ([weight (if (and weight (not (fl= max-weight 0.0)))
(flonum->fixnum (fl/ weight (fl/ max-weight 1024.0)))
(if (block-pariah? block)
0
(expt 4 (fxmin (block-depth block) 5))))])
(block-weight-set! block weight)
(unless (fx= weight 0)
(let ()
(define fixnum (lambda (x) (if (fixnum? x) x (most-positive-fixnum))))
; refs and sets are weighted equally
(define process-var
(lambda (x)
(when (uvar? x)
(uvar-ref-weight-set! x (fixnum (+ (uvar-ref-weight x) weight))))))
(define Lvalue
(lambda (lvalue)
(nanopass-case (L15a Lvalue) lvalue
[,x (process-var x)]
[(mref ,x1 ,x2 ,imm) (process-var x1) (process-var x2)])))
(define Triv
(lambda (t)
(nanopass-case (L15a Triv) t
[,lvalue (Lvalue lvalue)]
[else (void)])))
(define Rhs
(lambda (rhs)
(nanopass-case (L15a Rhs) rhs
[,lvalue (Lvalue lvalue)]
[(inline ,info ,value-prim ,t* ...)
(for-each Triv t*)]
[else (void)])))
(define Pred
(lambda (p)
(nanopass-case (L15a Pred) p
[(inline ,live-info ,info ,pred-prim ,t* ...)
(for-each Triv t*)]
[else (sorry! who "unexpected pred ~s" p)])))
(define Tail
(lambda (tl)
(nanopass-case (L15a Tail) tl
[(jump ,live-info ,t (,var* ...)) (Triv t)]
[else (void)])))
(for-each
(lambda (instr)
(nanopass-case (L15a Effect) instr
[(set! ,live-info ,lvalue ,rhs) (Lvalue lvalue) (Rhs rhs)]
[(inline ,live-info ,info ,effect-prim ,t* ...)
(for-each Triv t*)]
[else (void)]))
(block-effect* block))
(cond
[(or (goto-block? block) (joto-block? block)) (void)]
[(if-block? block) (Pred (if-block-pred block))]
[(newframe-block? block)
(let ([newframe-info (newframe-block-info block)])
(info-newframe-weight-set! newframe-info
(fixnum (+ (info-newframe-weight newframe-info) weight))))]
[(tail-block? block) (Tail (tail-block-tail block))]
[else (sorry! who "unrecognized block ~s" block)]))))))))
; now know for each block its loop nesting depth and pariahty
; now weight calls and refs accordingly
(CaseLambdaExpr : CaseLambdaExpr (ir) -> CaseLambdaExpr ()
[(lambda ,info ,max-fv (,local* ...) (,entry-block* ...) (,block* ...))
(if ($profile-block-data?)
(let* ([weight* (map (lambda (block)
(let ([psrc (block-pseudo-src block)])
(and psrc (profile-query-weight psrc))))
block*)]
[max-weight (fold-left (lambda (m block weight)
(if weight (flmax m weight) m))
0.0 block* weight*)])
(for-each (weight-block! max-weight) block* weight*))
(let ([wb (weight-block! #f)])
(for-each (lambda (block) (wb block #f)) block*)))
ir]))
; this must come before np-allocate-registers since asm-module is imported
; by the included file <architecture>-instructions.ss
(module (np-generate-code asm-module)
(define-threaded aop)
(define-threaded funcrel*)
(define-threaded current-func)
(define make-funcrel
(lambda (reloc l offset)
(let ([stuff (list offset l)])
(set! funcrel* (cons stuff funcrel*))
(cons reloc stuff))))
; TODO: generate code forward => backward and thread through a machine-state
; record that says what each register contains, including the condition-code
; register, so that we can avoid redundant loads and tests. For example,
; second set! of td in (seq (set! td ,(%mref tc 20)) ... (set! td ,(%mref tc 20)))
; should go away with no intervening assignment of td or tc[20]. Similarly,
; in (seq (mset! tc 36 (incr ,(%mref tc 36))) (if (eq? ,(%mref tc 36) 0) L1 L2),
; the test should reduce to a check of the 'z' flag.
; plain chunks arise only as the destination for a rachunk
(define-record-type chunk
(nongenerative)
(fields size code*)
(protocol
(lambda (new)
(lambda (code*) (new (asm-size* code*) code*)))))
(define-record-type lchunk
(parent chunk)
(nongenerative)
(sealed #t)
(fields l)
(protocol
(lambda (pargs->new)
(lambda (l code*)
((pargs->new code*) l)))))
(define-record-type gchunk
(parent chunk)
(nongenerative)
(sealed #t)
(fields l laddr next-offset)
(protocol
(lambda (pargs->new)
(lambda (l next-offset code*)
((pargs->new code*) l (local-label-offset l) next-offset)))))
(define-record-type cgchunk
(parent chunk)
(nongenerative)
(sealed #t)
(fields info l1 l2 laddr1 laddr2 next-offset)
(protocol
(lambda (pargs->new)
(lambda (info l1 l2 next-offset code*)
(define label-offset
(lambda (l)
(and (local-label? l) (local-label-offset l))))
((pargs->new code*) info l1 l2 (label-offset l1) (label-offset l2) next-offset)))))
; rachunks arise only during code generation to support machines like the ARM that determine
; return addresses for Scheme calls using pc-relative add or lea instructions
(define-record-type rachunk
(parent chunk)
(nongenerative)
(sealed #t)
(fields dest l incr-offset laddr next-offset)
(protocol
(lambda (pargs->new)
(lambda (dest l incr-offset next-offset code*)
((pargs->new code*) dest l incr-offset (local-label-offset l) next-offset)))))
(define-pass np-generate-code : L16 (ir) -> * (code)
(definitions
(define munge-recur?)
(define c-trace
; copied from compile.ss
(lambda (name size trace-list p)
(when p
(newline p)
(when name (fprintf p "~a: ~%" name))
(parameterize ([print-length 5] [print-level 3] [print-gensym 'pretty/suffix])
(let dump ([trace-list trace-list] [last-addr size])
(when (pair? trace-list)
(apply (lambda (addr op . args)
(if (eq? op 'label)
(begin
(fprintf p "~{~s~^, ~}:\n" addr)
(dump (cdr trace-list) last-addr))
(begin
(fprintf p "~d:~9t~a~24t" (- size last-addr) op)
(do ((args args (cdr args)))
((null? args))
(let ([arg (car args)])
(if (string? arg) (display arg p) (write arg p)))
(unless (null? (cdr args)) (display ", " p)))
(newline p)
(dump (cdr trace-list) addr))))
(car trace-list)))))
(fprintf p "~d:~9t<end~@[ ~a~]>\n" size name))))
; munge gets the code in forward order, but really wants to process it
; backwards to find the label offsets. Maybe the size would be better
; tracked by doing it more like cp2 does right now and then patching in
; the forward jumps and tightening up the code.
(define-who munge
(lambda (c* size)
(define (munge-pass c* iteration)
(define get-local-label-offset
(lambda (l)
(local-label-iteration-set! l iteration)
(local-label-offset l)))
(let f ([rc* (reverse c*)] [c* '()] [offset 0])
(if (null? rc*)
(values c* offset)
(let ([c (car rc*)] [rc* (cdr rc*)])
(cond
[(lchunk? c)
(let ([l (lchunk-l c)] [offset (fx+ offset (chunk-size c))])
(when l
(unless (eq? (get-local-label-offset l) offset)
(local-label-offset-set! l offset)
(when (fx= (local-label-iteration l) iteration)
(set! munge-recur? #t))))
(f rc* (cons c c*) offset))]
[(gchunk? c)
(let ([l (gchunk-l c)])
(if (and (eq? (get-local-label-offset l) (gchunk-laddr c))
(eq? (gchunk-next-offset c) offset))
(f rc* (cons c c*) (fx+ offset (chunk-size c)))
(let ([c (asm-jump l offset)])
(f rc* (cons c c*) (fx+ offset (chunk-size c))))))]
[(cgchunk? c)
(let ([l1 (cgchunk-l1 c)] [l2 (cgchunk-l2 c)])
(if (and (or (libspec-label? l1) (eq? (get-local-label-offset l1) (cgchunk-laddr1 c)))
(or (libspec-label? l2) (eq? (get-local-label-offset l2) (cgchunk-laddr2 c)))
(eq? (cgchunk-next-offset c) offset))
(f rc* (cons c c*) (fx+ offset (chunk-size c)))
(let ([c (asm-conditional-jump (cgchunk-info c) l1 l2 offset)])
(f rc* (cons c c*) (fx+ offset (chunk-size c))))))]
[(rachunk? c)
(let ([c (let ([l (rachunk-l c)])
(if (and (eq? (get-local-label-offset l) (rachunk-laddr c))
(eq? (rachunk-next-offset c) offset))
c
(asm-return-address (rachunk-dest c) l (rachunk-incr-offset c) offset)))])
(f rc* (cons c c*) (fx+ offset (chunk-size c))))]
; NB: generic test, so must be last!
[(chunk? c) (f rc* (cons c c*) (fx+ offset (chunk-size c)))]
[else (sorry! who "unexpected chunk ~s" c)])))))
(define (asm-fixup-opnd x)
(define-syntax tc-offset-map
(let ([q (datum->syntax #'*
(map (lambda (x) (cons (caddr x) (string->symbol (format "$~s" (car x)))))
(getprop 'tc '*fields*)))])
(lambda (x) #`'#,q)))
(if (pair? x)
(record-case x
[(library) (x) `(library ,(libspec-name x))]
[(library-code) (x) `(library-code ,(libspec-name x))]
[(entry) (i) `(entry ,(vector-ref (constant c-entry-name-vector) i))]
[(disp) (offset reg)
(cond
[(and (eq? reg %tc) (assv offset tc-offset-map)) => cdr]
[else `(disp ,offset ,(reg-name reg))])]
[(index) (offset reg1 reg2)
`(index ,offset ,(reg-name reg1) ,(reg-name reg2))]
[(reg) r (reg-name r)]
[(label) (offset l)
(if (local-label? l)
(parameterize ([print-gensym 'pretty/suffix])
(format "~s(~d)" l offset))
(format "~s" l))]
[else x])
x))
(define (extract-trace-code code*)
(let-values ([(trace* size)
(let f ([code* code*])
(if (null? code*)
(values '() 0)
(let ([code (car code*)])
(let-values ([(trace* offset) (f (cdr code*))])
(record-case code
[(asm) (op . opnd*)
(values
`((,offset ,op ,@(map asm-fixup-opnd opnd*)) ,@trace*)
offset)]
[(label) l*
(values
(if (null? l*)
trace*
`((,l* label) ,@trace*))
offset)]
[else (values trace* (fx+ (asm-size code) offset))])))))])
trace*))
(define (extract-code c*)
(let f ([c* c*])
(if (null? c*)
'()
(let ([c (car c*)])
(let ([code (append (chunk-code* (car c*)) (f (cdr c*)))])
(if (and aop (lchunk? c))
(let ([l (lchunk-l c)])
(if l (cons `(label ,l) code) code))
code))))))
(let f ([c* c*] [size size] [iteration 2])
(if munge-recur?
(begin
(set! munge-recur? #f)
(let-values ([(c* new-size) (munge-pass c* iteration)])
(f c* new-size (fx+ iteration 1))))
(let ([code* (extract-code c*)])
(if aop
(values
(remp (lambda (code) (record-case code [(asm label) stuff #t] [else #f])) code*)
(extract-trace-code code*)
size)
(values code* '() size)))))))
; TODO: teach c-mkcode & c-faslcode how to indirect labels
(define-who resolve-funcrel!
(lambda (funcrel)
(let* ([l (cadr funcrel)] [code ($c-func-code-record (local-label-func l))])
(record-case code
[(code) (func subtype free name arity-mask size code-list info)
(set-car!
funcrel
(let ([offset (local-label-offset l)])
(if offset
(fx+ (fx- size offset) (car funcrel) (constant code-data-disp))
(car funcrel))))
(set-car! (cdr funcrel) code)]
[else (sorry! who "unexpected record ~s" code)]))))
(define touch-label!
(lambda (l)
(unless (libspec-label? l) (local-label-iteration-set! l 1))))
(define LambdaBody
(lambda (entry-block* block* func)
#;(when (#%$assembly-output)
(p-dot-graph block* (current-output-port))
(p-graph block* 'whatever (current-output-port) unparse-L16))
(let ([block* (cons (car entry-block*) (remq (car entry-block*) block*))])
(for-each (lambda (block) (let ([l (block-label block)]) (when l (local-label-iteration-set! l 0) (local-label-func-set! l func)))) block*)
(fluid-let ([current-func func])
(let loop ([block* (reverse block*)] [chunk* '()] [offset 0])
(if (null? block*)
(munge chunk* offset)
(let ([block (car block*)])
(let-values ([(code* chunk* offset) (Block block chunk* offset)])
(let ([chunk (make-lchunk (block-label block) code*)])
(let ([offset (fx+ (chunk-size chunk) offset)])
(let ([l (block-label block)])
(when l
(local-label-offset-set! l offset)
(when (fx= (local-label-iteration l) 1) (set! munge-recur? #t))))
(loop (cdr block*) (cons chunk chunk*) offset)))))))))))
(define Block
(lambda (block chunk* offset)
(let f ([e* (block-effect* block)])
(if (null? e*)
(Exit block chunk* offset)
(let-values ([(code* chunk* offset) (f (cdr e*))])
(Effect (car e*) code* chunk* offset))))))
(define Exit
(lambda (block chunk* offset)
(define do-goto
(lambda (b)
(let ([l (block-label b)])
(safe-assert l)
(touch-label! l)
(let ([chunk (asm-jump l offset)])
(values '() (cons chunk chunk*) (fx+ (chunk-size chunk) offset))))))
(cond
[(goto-block? block) (do-goto (goto-block-next block))]
[(joto-block? block) (do-goto (joto-block-next block))]
[(if-block? block)
(let ([l1 (block-label (if-block-true block))] [l2 (block-label (if-block-false block))])
(safe-assert l1 l2)
(touch-label! l1)
(touch-label! l2)
(let-values ([(code* chunk) (Pred (if-block-pred block) l1 l2 offset)])
(values code* (cons chunk chunk*) (fx+ (chunk-size chunk) offset))))]
[(tail-block? block) (Tail (tail-block-tail block) chunk* offset)]
[(newframe-block? block) (do-goto (newframe-block-next block))]
[else (sorry! who "unrecognized block ~s" block)]))))
(Tail : Tail (ir chunk* offset) -> * (code* chunk* offset)
[(asm-return) (values (asm-return) chunk* offset)]
[(asm-c-return ,info) (values (asm-c-return info) chunk* offset)]
[(jump (label-ref ,l ,offset0))
(values (asm-direct-jump l offset0) chunk* offset)]
[(jump (literal ,info))
(values (asm-literal-jump info) chunk* offset)]
[(jump ,t)
(values (asm-indirect-jump t) chunk* offset)]
[(goto ,l)
(safe-assert (libspec-label? l))
(values (asm-library-jump l) chunk* offset)])
(Program : Program (ir) -> * (code)
[(labels ([,l* ,[Lambda->func : le* -> func*]] ...) ,l)
(define-syntax traceit
(syntax-rules (x)
[(_ name) (set! name (let ([t name]) (lambda args (apply t args))))]))
(fluid-let ([funcrel* '()] [aop ($assembly-output)] [munge-recur? #f])
(for-each local-label-func-set! l* func*)
(let ([ptrace* (map CaseLambdaExpr le* func*)])
(for-each resolve-funcrel! funcrel*)
(when aop
(for-each (lambda (ptrace) (ptrace aop)) ptrace*)
(flush-output-port aop))
(local-label-func l)))])
(Lambda->func : CaseLambdaExpr (ir) -> * (func)
[(lambda ,info (,entry-block* ...) (,block* ...)) (make-$c-func)])
; the final version of code* (which has things resolved)
(CaseLambdaExpr : CaseLambdaExpr (ir func) -> * ()
[(lambda ,info (,entry-block* ...) (,block* ...))
#;(let ()
(define block-printer
(lambda (unparser name block*)
(p-dot-graph block* (current-output-port))
(p-graph block* name (current-output-port) unparser)))
(block-printer unparse-L16 (info-lambda-name info) block*))
(let-values ([(code* trace* code-size) (LambdaBody entry-block* block* func)])
($c-make-code
func
(info-lambda-flags info)
(length (info-lambda-fv* info))
(info-lambda-name info)
(interface*->mask (info-lambda-interface* info))
code-size
code*
(cond
[(info-lambda-ctci info) =>
(lambda (ctci)
(include "types.ss")
(make-code-info
(info-lambda-src info)
(info-lambda-sexpr info)
(and (eq? (info-lambda-closure-rep info) 'closure)
(let f ([fv* (info-lambda-fv* info)] [n 0])
(if (null? fv*)
(make-vector n #f)
(let ([v (f (cdr fv*) (fx+ n 1))])
(cond
[(uvar-source (car fv*)) =>
(lambda (source) (vector-set! v n (unannotate source)))])
v))))
(ctci-live ctci)
(let ([v (vector-map
(let ([n (fx+ (constant code-data-disp) (constant size-rp-header) code-size)])
(lambda (ctrpi)
(make-rp-info
(fx- n (local-label-offset (ctrpi-label ctrpi)))
(ctrpi-src ctrpi)
(ctrpi-sexpr ctrpi)
(ctrpi-mask ctrpi))))
(list->vector (ctci-rpi* ctci)))])
(vector-sort! (lambda (x y) (fx< (rp-info-offset x) (rp-info-offset y))) v)
v)))]
[(and (generate-procedure-source-information)
(info-lambda-src info)) =>
(lambda (src)
(include "types.ss")
(make-code-info src #f #f #f #f))]
[else #f])
(info-lambda-pinfo* info))
(lambda (p) (c-trace (info-lambda-name info) code-size trace* p)))])
(Effect : Effect (ir code* chunk* offset) -> * (code* chunk* offset)
[(rp-header ,mrvl ,fs ,lpm) (values (asm-rp-header code* mrvl fs lpm current-func #f) chunk* offset)]
[(set! ,x (label-ref ,l ,offset1))
(guard (eq? (local-label-func l) current-func))
(let ([chunk (make-chunk code*)])
(let ([offset (fx+ (chunk-size chunk) offset)] [chunk* (cons chunk chunk*)])
(let ([chunk (asm-return-address x l offset1 offset)])
(values '() (cons chunk chunk*) (fx+ (chunk-size chunk) offset)))))]
[(set! ,lvalue (asm ,info ,proc ,t* ...)) (values (apply proc code* lvalue t*) chunk* offset)]
[(set! ,lvalue ,rhs) (values (asm-move code* lvalue rhs) chunk* offset)]
[(asm ,info ,proc ,t* ...) (values (apply proc code* t*) chunk* offset)])
(Pred : Pred (ir l1 l2 offset) -> * (code* chunk)
[(asm ,info ,proc ,t* ...) (apply proc l1 l2 offset t*)])
(Program ir))
(define-pass Triv->rand : (L16 Triv) (ir) -> * (operand)
(Triv : Triv (ir) -> * (operand)
[,x (cons 'reg x)]
[(mref ,x1 ,x2 ,imm)
(if (eq? x2 %zero)
`(disp ,imm ,x1)
`(index ,imm ,x2 ,x1))]
[(literal ,info)
`(,(if (info-literal-indirect? info) 'literal@ 'literal)
,(info-literal-offset info)
,(let ([type (info-literal-type info)])
(if (eq? type 'closure)
($c-make-closure (local-label-func (info-literal-addr info)))
`(,type ,(info-literal-addr info)))))]
[(immediate ,imm) `(imm ,imm)]
[(label-ref ,l ,offset) (make-funcrel 'literal l offset)])
(Triv ir))
(define build-mem-opnd
(lambda (base index offset)
(let ([offset (nanopass-case (L16 Triv) offset [(immediate ,imm) imm])])
(if (eq? index %zero)
`(disp ,offset ,base)
`(index ,offset ,base ,index)))))
(define asm-size*
(lambda (x*)
(fold-left (lambda (size x) (fx+ size (asm-size x))) 0 x*)))
(define-syntax Trivit
(syntax-rules ()
[(_ (x ...) b0 b1 ...) (let ([x (Triv->rand x)] ...) b0 b1 ...)]))
(define-syntax aop-cons*
(syntax-rules ()
[(_ asm e1 e2 ...)
(let ([ls (cons* e1 e2 ...)])
(if aop (cons asm ls) ls))]))
(define interface*->mask
(lambda (i*)
(fold-left (lambda (mask i)
(logor mask
(if (< i 0)
(- (ash 1 (- -1 i)))
(ash 1 i))))
0 i*)))
(architecture assembler)
(import asm-module))
(module (np-allocate-registers)
(define-threaded spillable*)
(define-threaded unspillable*)
(define-threaded max-fv)
(define-threaded max-fs@call)
(define-threaded poison-cset)
(define no-live* empty-tree)
(define union-live
; union live1 and live2. result is eq? to live1 if result is same as live1.
(lambda (live1 live2 live-size)
(tree-merge live1 live2 live-size)))
(define same-live?
(lambda (live1 live2)
(tree-same? live1 live2)))
(define live?
(lambda (live* live-size x)
(tree-bit-set? live* live-size (var-index x))))
(define get-live-vars
(lambda (live* live-size v)
(tree-extract live* live-size v)))
(define make-add-var
(lambda (live-size)
; add x to live*. result is eq? to live* if x is already in live*.
(lambda (live* x)
(let ([index (var-index x)])
(if index
(let ([new (tree-bit-set live* live-size index)])
(safe-assert (or (eq? new live*) (not (tree-same? new live*))))
new)
live*)))))
(define make-remove-var
; remove x from live*. result is eq? to live* if x is not in live*.
(lambda (live-size)
(lambda (live* x)
(let ([index (var-index x)])
(if index
(let ([new (tree-bit-unset live* live-size (var-index x))])
(safe-assert (or (eq? new live*) (not (tree-same? new live*))))
new)
live*)))))
(module (make-empty-cset make-full-cset cset-full? conflict-bit-set! conflict-bit-unset! conflict-bit-set? conflict-bit-count cset-merge! cset-copy cset-for-each extract-conflicts)
(define-record-type cset
(nongenerative)
(fields size (mutable tree)))
(define make-empty-cset
(lambda (size)
(make-cset size empty-tree)))
(define make-full-cset
(lambda (size)
(make-cset size full-tree)))
(define cset-full?
(lambda (cset)
(eq? (cset-tree cset) full-tree)))
(define conflict-bit-set!
(lambda (cset offset)
(cset-tree-set! cset
(tree-bit-set (cset-tree cset) (cset-size cset) offset))))
(define conflict-bit-unset!
(lambda (cset offset)
(cset-tree-set! cset
(tree-bit-unset (cset-tree cset) (cset-size cset) offset))))
(define conflict-bit-set?
(lambda (cset offset)
(tree-bit-set? (cset-tree cset) (cset-size cset) offset)))
(define conflict-bit-count
(lambda (cset)
(tree-bit-count (cset-tree cset) (cset-size cset))))
(define cset-merge!
(lambda (cset1 cset2)
(cset-tree-set! cset1 (tree-merge (cset-tree cset1) (cset-tree cset2) (cset-size cset1)))))
(define cset-copy
(lambda (cset)
(make-cset (cset-size cset) (cset-tree cset))))
(define cset-for-each
(lambda (cset proc)
(tree-for-each (cset-tree cset) (cset-size cset) 0 (cset-size cset) proc)))
(define extract-conflicts
(lambda (cset v)
(tree-extract (cset-tree cset) (cset-size cset) v)))
)
(define do-live-analysis!
(lambda (live-size entry-block*)
(define add-var (make-add-var live-size))
(define remove-var (make-remove-var live-size))
(define-who scan-block
; if we maintain a list of kills and a list of useless variables for
; each block, and we discover on entry to scan-block that the useless
; variables are still useless (not live in "out"), we can compute the
; new in set without scanning the block by removing the kills from
; the out set and unioning the result with the saved in set. should
; try this and see if it is enough of a win to justify the added
; complexity.
(lambda (block out)
(define Triv
(lambda (out t)
(nanopass-case (L15a Triv) t
[(mref ,x1 ,x2 ,imm) (add-var (add-var out x2) x1)]
[,x (add-var out x)]
[else out])))
(define Rhs
(lambda (out rhs)
(nanopass-case (L15a Rhs) rhs
[(inline ,info ,value-prim ,t* ...)
(let* ([out (if (info-kill*? info) (fold-left remove-var out (info-kill*-kill* info)) out)]
[out (if (info-kill*-live*? info) (fold-left add-var out (info-kill*-live*-live* info)) out)])
(fold-left Triv out t*))]
[else (Triv out rhs)])))
(define Pred
(lambda (out p)
(nanopass-case (L15a Pred) p
[(inline ,live-info ,info ,pred-prim ,t* ...)
(let* ([out (if (info-kill*? info) (fold-left remove-var out (info-kill*-kill* info)) out)]
[out (if (info-kill*-live*? info) (fold-left add-var out (info-kill*-live*-live* info)) out)])
(live-info-live-set! live-info out)
(fold-left Triv out t*))]
[else (sorry! who "unexpected pred ~s" p)])))
(define Tail
(lambda (out tl)
(nanopass-case (L15a Tail) tl
[(goto ,l)
(safe-assert (libspec-label? l))
(fold-left add-var no-live* (libspec-label-live-reg* l))]
[(asm-return ,reg* ...)
(safe-assert (eq? out no-live*))
(fold-left add-var no-live* reg*)]
[(asm-c-return ,info ,reg* ...)
(safe-assert (eq? out no-live*))
(fold-left add-var no-live* reg*)]
[(jump ,live-info ,t (,var* ...))
(let ([out (fold-left add-var out var*)])
(live-info-live-set! live-info out)
(Triv out t))]
[else (sorry! who "unexpected tail instruction ~s" tl)])))
(define Effect*
(lambda (out instr*)
(fold-left
(lambda (out instr)
(nanopass-case (L15a Effect) instr
[(set! ,live-info ,x ,rhs)
(if (var-index x)
(let ([new-out (remove-var out x)])
(if (and (eq? new-out out)
(nanopass-case (L15a Rhs) rhs
[(inline ,info ,value-prim ,t* ...) (primitive-pure? value-prim)]
[else #t]))
(begin
(live-info-useless-set! live-info #t)
out)
(begin
(live-info-useless-set! live-info #f)
(live-info-live-set! live-info new-out)
(Rhs new-out rhs))))
(begin
(live-info-live-set! live-info out)
(Rhs out rhs)))]
[(set! ,live-info (mref ,x1 ,x2 ,imm) ,rhs)
(live-info-live-set! live-info out)
(Rhs (add-var (add-var out x1) x2) rhs)]
[(inline ,live-info ,info ,effect-prim ,t* ...)
(let ([out (if (info-kill*? info) (fold-left remove-var out (info-kill*-kill* info)) out)])
(live-info-live-set! live-info out)
(let ([out (fold-left Triv out t*)])
(if (info-kill*-live*? info)
(fold-left add-var out (info-kill*-live*-live* info))
out)))]
[(remove-frame ,live-info ,info) (live-info-live-set! live-info out) out]
[(restore-local-saves ,live-info ,info) (live-info-live-set! live-info out) out]
[(shift-arg ,live-info ,reg ,imm ,info) (live-info-live-set! live-info out) out]
[(overflow-check ,live-info) (live-info-live-set! live-info out) out]
[(overflood-check ,live-info) (live-info-live-set! live-info out) out]
[(fcallable-overflow-check ,live-info) (live-info-live-set! live-info out) out]
[(check-live ,live-info ,reg* ...) (live-info-live-set! live-info out) out]
[else out]))
out instr*)))
; NB: consider storing instructions in reverse order back in expose-basic-blocks
(let ([effect* (reverse (block-effect* block))])
(cond
[(or (goto-block? block) (joto-block? block) (newframe-block? block)) (Effect* out effect*)]
[(if-block? block) (Effect* (Pred out (if-block-pred block)) effect*)]
[(tail-block? block) (Effect* (Tail out (tail-block-tail block)) effect*)]
[else (sorry! who "unrecognized block ~s" block)]))))
(define force-live-in!
(lambda (block)
(when (eq? (block-live-in block) 'uninitialized)
(if (block-seen? block)
; think we need need not recur on in-link* here even though we changed in
; - if an in-link is seen, it's already on the worklist
; - if an in-link is not seen, we must not have visited it yet or it would
; have already forced us. someone will visit it later unless it's
; orphaned, and we think we have no orphaned blocks
(block-live-in-set! block no-live*)
(begin
(block-seen! block #t)
(do-live! block))))))
(define different?
(lambda (out old-out)
(or (eq? old-out 'uninitialized)
(not (same-live? out old-out)))))
(define propagate-live!
(lambda (block out)
; NB: could record out, and if out hasn't changed, skip the scan
(let ([in (scan-block block out)])
(when (different? in (block-live-in block))
(block-live-in-set! block in)
(let f ([block* (block-in-link* block)])
(unless (null? block*)
(let ([block (car block*)])
(if (block-seen? block)
(f (cdr block*))
(begin
(block-seen! block #t)
(f (cdr block*))
(do-live! block))))))))))
(define-who do-live!
(lambda (block)
(safe-assert (block-seen? block))
(cond
[(goto-block? block)
(let ([next-block (goto-block-next block)])
(force-live-in! next-block)
(block-seen! block #f)
(propagate-live! block (block-live-in next-block)))]
[(if-block? block)
(let ([true-block (if-block-true block)] [false-block (if-block-false block)])
(force-live-in! true-block)
(force-live-in! false-block)
(block-seen! block #f)
(let ([out (union-live (block-live-in true-block) (block-live-in false-block) live-size)])
(when (different? out (if-block-live-out block))
(if-block-live-out-set! block out)
(propagate-live! block out))))]
[(joto-block? block)
(let ([next-block (joto-block-next block)])
(force-live-in! next-block)
(block-seen! block #f)
(propagate-live! block
(let loop ([nfv* (joto-block-nfv* block)] [i 1] [next (block-live-in next-block)])
(if (or (null? nfv*) (fx> i max-fv))
next
(loop (cdr nfv*) (fx+ i 1)
(let ([new-next (remove-var next (get-fv i))])
(if (eq? new-next next)
next
(add-var next (car nfv*)))))))))]
[(newframe-block? block)
(let ([next-block (newframe-block-next block)]
[rp-block* (newframe-block-rp* block)]
[rp-block (newframe-block-rp block)])
(force-live-in! next-block)
(for-each force-live-in! rp-block*)
(force-live-in! rp-block)
(block-seen! block #f)
(let ([rp (block-live-in rp-block)] [newframe-info (newframe-block-info block)])
(let ([call (if (eq? (newframe-block-live-rp block) rp)
(newframe-block-live-call block)
(begin
(newframe-block-live-rp-set! block rp)
(let ([call (add-var
(fold-left
(lambda (live* x*) (fold-left remove-var live* x*))
rp
(cons*
; could base set of registers to kill on expected return values
(reg-cons* %ret %ac0 arg-registers)
(info-newframe-cnfv* newframe-info)
(info-newframe-nfv** newframe-info)))
(get-fv 0))])
(newframe-block-live-call-set! block call)
call)))])
(let ([out (union-live
(fold-left (lambda (live b) (union-live (block-live-in b) live live-size))
(block-live-in next-block) rp-block*)
(fold-left add-var call (info-newframe-cnfv* newframe-info))
live-size)])
(when (different? out (newframe-block-live-out block))
(newframe-block-live-out-set! block out)
(propagate-live! block out))))))]
[(tail-block? block)
(block-seen! block #f)
(propagate-live! block no-live*)]
[else (sorry! who "unrecognized block ~s" block)])))
(for-each
(lambda (entry-block)
(when (eq? (block-live-in entry-block) 'uninitialized)
(block-seen! entry-block #t)
(do-live! entry-block)))
entry-block*)))
(define-who check-entry-live!
; when enabled, spits out messages about uvars and unexpected registers that are live
; on entry. there should never be any live uvars. for procedures that started life
; as ordinary lambda expressions, there shouldn't be anything but ac0, cp, and argument
; registers, which we weed out here. for library routines, there are often additional
; registers, sometimes for good reason and sometimes because we are lazy and didn't give
; ourselves a mechanism to prune out unneeded saves and restores. for foreign-callable
; procedures, C argument registers and callee-save registers might show up live.
; we could enable a variant of this always that just checks normal procedures. also,
; it might be nice to make it a bit more efficient, though it probably doesn't matter.
(lambda (name live-size varvec entry-block*)
(for-each
(lambda (entry-block)
(define okay-live?
(lambda (x)
(or (fv? x)
(eq? x %ac0)
(meta-cond
[(real-register? '%cp) (eq? x %cp)]
[else #f])
(memq x arg-registers))))
(let ([undead (remp okay-live? (get-live-vars (block-live-in entry-block) live-size varvec))])
(unless (null? undead)
(printf "Warning: live on entry to ~a: ~s\n" name undead))))
entry-block*)))
(define-who record-call-live!
(lambda (block* varvec)
(for-each
(lambda (block)
(when (newframe-block? block)
(let ([newframe-info (newframe-block-info block)])
(let ([call-live* (get-live-vars (newframe-block-live-call block) (vector-length varvec) varvec)])
(for-each
(lambda (x)
(define fixnum (lambda (x) (if (fixnum? x) x (most-positive-fixnum))))
(when (uvar? x)
(uvar-spilled! x #t)
(unless (block-pariah? block)
(uvar-save-weight-set! x
(fixnum
(+ (uvar-save-weight x)
(* (info-newframe-weight newframe-info) 2)))))))
call-live*)
(info-newframe-call-live*-set! newframe-info call-live*)))))
block*)))
; maintain move sets as (var . weight) lists, sorted by weight (largest first)
; 2014/06/26: allx move set size averages .79 elements with a max of 12, so no
; need for anything fancier than this weighted version of insertion sort
(define $add-move!
(lambda (x1 x2 weight)
(when (uvar? x1)
(when (or (not (uvar-poison? x1)) (fv? x2))
(uvar-move*-set! x1
(call-with-values
(lambda ()
(let f ([move* (uvar-move* x1)])
(if (null? move*)
(values (cons x2 weight) move*)
(let ([move (car move*)] [move* (cdr move*)])
(if (eq? (car move) x2)
(values (cons (car move) (fx+ (cdr move) weight)) move*)
(let-values ([(move2 move*) (f move*)])
(if (fx> (cdr move2) (cdr move))
(values move2 (cons move move*))
(values move (cons move2 move*)))))))))
cons))))))
(define-who identify-poison!
(lambda (kspillable varvec live-size block*)
(define kpoison 0)
(define increment-live-counts!
(lambda (live)
(tree-for-each live live-size 0 kspillable
(lambda (offset)
(let ([x (vector-ref varvec offset)])
(let ([range (fx+ (uvar-live-count x) 1)])
(when (fx= range 2)
(uvar-poison! x #t)
(set! kpoison (fx+ kpoison 1)))
(uvar-live-count-set! x range)))))))
(define Effect
(lambda (live* e)
(nanopass-case (L15a Effect) e
[(set! ,live-info ,x ,rhs)
(guard (uvar? x))
(if (live-info-useless live-info)
live*
(cons (live-info-live live-info) live*))]
[else live*])))
(let ([vlive (list->vector (fold-left (lambda (live* block) (fold-left Effect live* (block-effect* block))) '() block*))])
(let ([nvlive (vector-length vlive)])
(let refine ([skip 64] [stride 64])
(do ([i (fx- skip 1) (fx+ i stride)])
((fx>= i nvlive))
(increment-live-counts! (vector-ref vlive i)))
(unless (or (fx= stride 16) (< (* (fx- kspillable kpoison) (fx* stride 2)) 1000000))
(refine (fxsrl skip 1) skip)))))))
(define-who do-spillable-conflict!
(lambda (kspillable kfv varvec live-size block*)
(define remove-var (make-remove-var live-size))
(define add-move!
(lambda (x1 x2)
(when (var-index x2)
($add-move! x1 x2 2)
($add-move! x2 x1 2))))
(define add-conflict!
(lambda (x out)
; invariants:
; all poison spillables explicitly point to all spillables
; all non-poison spillables implicitly point to all poison spillables via poison-cset
(let ([x-offset (var-index x)])
(when x-offset
(if (and (fx< x-offset kspillable) (uvar-poison? x))
(tree-for-each out live-size kspillable (fx+ kspillable kfv)
(lambda (y-offset)
; frame y -> poison spillable x
(conflict-bit-set! (var-spillable-conflict* (vector-ref varvec y-offset)) x-offset)))
(let ([cset (var-spillable-conflict* x)])
(if (fx< x-offset kspillable)
(begin
(tree-for-each out live-size 0 kspillable
(lambda (y-offset)
(let ([y (vector-ref varvec y-offset)])
(unless (uvar-poison? y)
; non-poison spillable x -> non-poison spillable y
(conflict-bit-set! cset y-offset)
; and vice versa
(conflict-bit-set! (var-spillable-conflict* y) x-offset)))))
(tree-for-each out live-size kspillable live-size
(lambda (y-offset)
(let ([y (vector-ref varvec y-offset)])
; frame or register y -> non-poison spillable x
(conflict-bit-set! (var-spillable-conflict* y) x-offset)))))
(if (fx< x-offset (fx+ kspillable kfv))
(tree-for-each out live-size 0 kspillable
(lambda (y-offset)
; frame x -> poison or non-poison spillable y
(conflict-bit-set! cset y-offset)))
(tree-for-each out live-size 0 kspillable
(lambda (y-offset)
(unless (uvar-poison? (vector-ref varvec y-offset))
; register x -> non-poison spillable y
(conflict-bit-set! cset y-offset))))))))))))
(define Rhs
(lambda (rhs live)
(nanopass-case (L15a Rhs) rhs
[(inline ,info ,value-prim ,t* ...)
(guard (info-kill*? info))
(for-each (lambda (x) (add-conflict! x live)) (info-kill*-kill* info))]
[else (void)])))
(define Effect
(lambda (e new-effect*)
(nanopass-case (L15a Effect) e
[(set! ,live-info ,x ,rhs)
(if (live-info-useless live-info)
new-effect*
(let ([live (live-info-live live-info)])
(when (var-index x)
(if (and (var? rhs) (var-index rhs))
(begin
(add-conflict! x (remove-var live rhs))
(add-move! x rhs))
(add-conflict! x live)))
(Rhs rhs live)
(cons e new-effect*)))]
[(set! ,live-info ,lvalue ,rhs) (Rhs rhs (live-info-live live-info)) (cons e new-effect*)]
[(inline ,live-info ,info ,effect-prim ,t* ...)
(guard (info-kill*? info))
(let ([live (live-info-live live-info)])
(for-each (lambda (x) (add-conflict! x live)) (info-kill*-kill* info)))
(cons e new-effect*)]
[else (cons e new-effect*)])))
(do ([i 0 (fx+ i 1)])
((fx= i kspillable))
(let ([x (vector-ref varvec i)])
(if (uvar-poison? x)
(begin
(conflict-bit-set! poison-cset i)
; leaving each poison spillable in conflict with itself, but this shouldn't matter
; since we never ask for the degree of a poison spillable
(var-spillable-conflict*-set! x (make-full-cset kspillable)))
(var-spillable-conflict*-set! x (make-empty-cset kspillable)))))
(do ([i kspillable (fx+ i 1)])
((fx= i live-size))
(var-spillable-conflict*-set! (vector-ref varvec i) (make-empty-cset kspillable)))
(for-each
(lambda (block)
(block-effect*-set! block
(fold-right Effect '() (block-effect* block))))
block*)))
(define-who show-conflicts
(lambda (name varvec unvarvec)
(define any? #f)
(printf "\n~s conflicts:" name)
(for-each
(lambda (x)
(let ([ls (append
(let ([cset (var-spillable-conflict* x)])
(if cset (extract-conflicts cset varvec) '()))
(let ([cset (var-unspillable-conflict* x)])
(if cset (extract-conflicts cset unvarvec) '())))])
(unless (null? ls) (set! any? #t) (printf "\n~s:~{ ~s~}" x ls))))
(append spillable* unspillable* (vector->list regvec) (map get-fv (iota (fx+ max-fv 1)))))
(unless any? (printf " none"))
(newline)))
(module (assign-frame! assign-new-frame!)
(define update-conflict!
(lambda (fv spill)
(let ([cset1 (var-spillable-conflict* fv)]
[cset2 (var-spillable-conflict* spill)])
(if cset1
(cset-merge! cset1 cset2)
; tempting to set to cset2 rather than (cset-copy cset2), but this would not be
; correct for local saves, which need their unaltered sets for later, and copying
; is cheap anyway.
(var-spillable-conflict*-set! fv (cset-copy cset2))))
(unless (uvar-poison? spill) (cset-merge! (var-spillable-conflict* fv) poison-cset))))
(define assign-frame!
(lambda (spill*)
(define sort-spill*
; NB: sorts based on likelihood of successfully assigning move-related vars to the same location
; NB: probably should sort based on value of assigning move-related vars to the same location,
; NB: i.e., taking into account the ref-weight
(lambda (spill*)
(map car
(list-sort
(lambda (x y) (fx> (cdr x) (cdr y)))
(map (lambda (x)
(define relevant?
(lambda (x)
(or (fv? x) (and (uvar? x) (uvar-spilled? x)))))
(do ([move* (uvar-move* x) (cdr move*)]
[w 0 (let ([move (car move*)])
(if (relevant? (car move))
(fx+ w (cdr move))
w))])
((null? move*) (cons x w))))
spill*)))))
(define find-move-related-home
(lambda (x0 succ fail)
(define conflict-fv?
(lambda (x fv)
(let ([cset (var-spillable-conflict* fv)])
(and cset (conflict-bit-set? cset (var-index x))))))
(let f ([x x0] [work* '()] [clear-seen! void])
(if (uvar-seen? x)
(if (null? work*) (begin (clear-seen!) (fail)) (f (car work*) (cdr work*) clear-seen!))
(let ([clear-seen! (lambda () (uvar-seen! x #f) (clear-seen!))])
(uvar-seen! x #t)
(let loop ([move* (uvar-move* x)] [work* work*])
(if (null? move*)
(if (null? work*) (begin (clear-seen!) (fail)) (f (car work*) (cdr work*) clear-seen!))
(let ([var (caar move*)] [move* (cdr move*)])
(define try-fv
(lambda (fv)
(if (conflict-fv? x0 fv)
(loop move* work*)
(begin
(safe-assert (not (eq? fv (get-fv 0))))
(begin (clear-seen!) (succ fv))))))
(if (fv? var)
(try-fv var)
(if (uvar? var)
(let ([fv (uvar-location var)])
(if (fv? fv)
(try-fv fv)
(loop move* (cons var work*))))
(loop move* work*)))))))))))
(define find-home!
(lambda (spill max-fv first-open)
(define return
(lambda (home max-fv first-open)
(uvar-location-set! spill home)
(update-conflict! home spill)
(values max-fv first-open)))
(find-move-related-home spill
(lambda (home) (return home max-fv first-open))
(lambda ()
(let f ([first-open first-open])
(let* ([fv (get-fv first-open)] [cset (var-spillable-conflict* fv)])
(if (and cset (cset-full? cset))
(f (fx+ first-open 1))
(let ([spill-offset (var-index spill)])
(let f ([fv-offset first-open] [fv fv] [cset cset])
(if (and cset (conflict-bit-set? cset spill-offset))
(let* ([fv-offset (fx+ fv-offset 1)] [fv (get-fv fv-offset)] [cset (var-spillable-conflict* fv)])
(f fv-offset fv cset))
(return fv (fxmax fv-offset max-fv) first-open)))))))))))
(define find-homes!
(lambda (spill* max-fv first-open)
(if (null? spill*)
max-fv
(let-values ([(max-fv first-open) (find-home! (car spill*) max-fv first-open)])
(find-homes! (cdr spill*) max-fv first-open)))))
; NOTE: call-live uvars should be sorted so that those that are call-live with few other
; variables are earlier in the list (and more likely to get a low frame location);
; additionally if they are live across many frames they should be prioritized over those
; live across only a few (only when setup-nfv?)
(set! max-fv (find-homes! (sort-spill* spill*) max-fv 1))))
(define-pass assign-new-frame! : (L15a Dummy) (ir lambda-info live-size varvec block*) -> (L15b Dummy) ()
(definitions
(define remove-var (make-remove-var live-size))
(define find-max-fv
(lambda (call-live*)
(fold-left
(lambda (call-max-fv x)
(fxmax (fv-offset (if (uvar? x) (uvar-location x) x)) call-max-fv))
-1 call-live*)))
(define cool?
(lambda (base nfv*)
(let loop ([nfv* nfv*] [offset base])
(or (null? nfv*)
(and (or (not (car nfv*))
(let ([cset (var-spillable-conflict* (get-fv offset))])
(not (and cset (conflict-bit-set? cset (var-index (car nfv*)))))))
(loop (cdr nfv*) (fx+ offset 1)))))))
(define assign-new-frame!
(lambda (cnfv* nfv** call-live*)
(define set-offsets!
(lambda (nfv* offset)
(if (null? nfv*)
(set! max-fv (fxmax offset max-fv))
(let ([nfv (car nfv*)] [home (get-fv offset)])
(uvar-location-set! nfv home)
(update-conflict! home nfv)
(set-offsets! (cdr nfv*) (fx+ offset 1))))))
(let ([arg-offset (fx+ (length cnfv*) 1)]) ; +1 for return address slot
(let loop ([base (fx+ (find-max-fv call-live*) 1)])
(let ([arg-base (fx+ base arg-offset)])
(if (and (cool? base cnfv*) (andmap (lambda (nfv*) (cool? arg-base nfv*)) nfv**))
(begin
(set! max-fs@call (fxmax max-fs@call base)) ; max frame size @ call in ptrs
(set-offsets! cnfv* base)
(for-each (lambda (nfv*) (set-offsets! nfv* arg-base)) nfv**)
base)
(loop (fx+ base 1))))))))
(define build-mask
(lambda (index*)
(define bucket-width (if (fx> (fixnum-width) 32) 32 16))
(let* ([nbits (fx+ (fold-left (lambda (m index) (fxmax m index)) -1 index*) 1)]
[nbuckets (fxdiv (fx+ nbits (fx- bucket-width 1)) bucket-width)]
[buckets (make-fxvector nbuckets 0)])
(for-each
(lambda (index)
(let-values ([(i j) (fxdiv-and-mod index bucket-width)])
(fxvector-set! buckets i (fxlogbit1 j (fxvector-ref buckets i)))))
index*)
(let f ([base 0] [len nbuckets])
(if (fx< len 2)
(if (fx= len 0)
0
(fxvector-ref buckets base))
(let ([half (fxsrl len 1)])
(logor
(bitwise-arithmetic-shift-left (f (fx+ base half) (fx- len half)) (fx* half bucket-width))
(f base half))))))))
(define build-live-pointer-mask
(lambda (live*)
(build-mask
(fold-left
(lambda (index* live)
(define (cons-fv fv index*)
(let ([offset (fv-offset fv)])
(if (fx= offset 0) ; no bit for fv0
index*
(cons (fx- offset 1) index*))))
(cond
[(fv? live) (cons-fv live index*)]
[(eq? (uvar-type live) 'ptr) (cons-fv (uvar-location live) index*)]
[else index*]))
'() live*))))
(define (process-info-newframe! info)
(unless (info-newframe-frame-words info)
(let ([call-live* (info-newframe-call-live* info)])
(info-newframe-frame-words-set! info
(let ([cnfv* (info-newframe-cnfv* info)])
(fx+ (assign-new-frame! cnfv* (cons (info-newframe-nfv* info) (info-newframe-nfv** info)) call-live*)
(length cnfv*))))
(info-newframe-local-save*-set! info
(filter (lambda (x) (and (uvar? x) (uvar-local-save? x))) call-live*)))))
(define record-inspector-info!
(lambda (src sexpr rpl call-live* lpm)
(safe-assert (if call-live* rpl (not rpl)))
(cond
[(and call-live* (info-lambda-ctci lambda-info)) =>
(lambda (ctci)
(let ([mask (build-mask
(fold-left
(lambda (i* x)
(cond
[(and (uvar? x) (uvar-iii x)) =>
(lambda (index)
(safe-assert
(let ([name.offset (vector-ref (ctci-live ctci) index)])
(logbit? (fx- (cdr name.offset) 1) lpm)))
(cons index i*))]
[else i*]))
'() call-live*))])
(when (or src sexpr (not (eqv? mask 0)))
(ctci-rpi*-set! ctci (cons (make-ctrpi rpl src sexpr mask) (ctci-rpi* ctci))))))]))))
(Pred : Pred (ir) -> Pred ())
(Tail : Tail (ir) -> Tail ()
[(jump ,live-info ,[t] (,var* ...)) `(jump ,live-info ,t)]
[(asm-return ,reg* ...) `(asm-return)]
[(asm-c-return ,info ,reg* ...) `(asm-c-return ,info)])
(Effect : Effect (ir) -> Effect ())
(foldable-Effect : Effect (ir new-effect*) -> * (new-effect*)
[(return-point ,info ,rpl ,mrvl (,cnfv* ...))
(process-info-newframe! info)
(let ([lpm (build-live-pointer-mask (append cnfv* (info-newframe-call-live* info)))])
(record-inspector-info! (info-newframe-src info) (info-newframe-sexpr info) rpl (info-newframe-call-live* info) lpm)
(with-output-language (L15b Effect)
(safe-assert (< -1 lpm (ash 1 (fx- (info-newframe-frame-words info) 1))))
(cons `(rp-header ,mrvl ,(fx* (info-newframe-frame-words info) (constant ptr-bytes)) ,lpm) new-effect*)))]
[(remove-frame ,live-info ,info)
(process-info-newframe! info)
(with-output-language (L15b Effect)
(let ([live (live-info-live live-info)])
(cons*
`(fp-offset ,live-info ,(fx- (fx* (info-newframe-frame-words info) (constant ptr-bytes))))
`(overflood-check ,(make-live-info live))
new-effect*)))]
[(restore-local-saves ,live-info ,info)
(with-output-language (L15b Effect)
(let ([live (live-info-live live-info)])
(let loop ([x* (filter (lambda (x) (live? live live-size x)) (info-newframe-local-save* info))]
[live live]
[new-effect* new-effect*])
(if (null? x*)
new-effect*
(let* ([x (car x*)] [live (remove-var live x)])
(loop (cdr x*) live
(cons `(set! ,(make-live-info live) ,x ,(uvar-location x)) new-effect*)))))))]
[(shift-arg ,live-info ,reg ,imm ,info)
(process-info-newframe! info)
(with-output-language (L15b Effect)
(let ([frame-words (info-newframe-frame-words info)])
(safe-assert (not (fx= frame-words 0)))
(let ([shift-offset (fx* frame-words (constant ptr-bytes))])
(safe-assert (fx> shift-offset 0))
(cons `(set! ,live-info (mref ,reg ,%zero ,imm) (mref ,reg ,%zero ,shift-offset)) new-effect*))))]
[(check-live ,live-info ,reg* ...)
(let ([live (fold-left (lambda (live reg)
(let ([t (remove-var live reg)])
(when (eqv? t live) (sorry! who "(check-live) ~s is not live" reg))
t))
(live-info-live live-info)
reg*)])
(unless (eqv? live no-live*)
(sorry! who "(check-live) unexpected live vars ~s" (get-live-vars live live-size varvec))))
new-effect*]
[else (cons (Effect ir) new-effect*)])
(begin
(for-each
(lambda (x)
; NB: experiment with different comparisions. might want ref weight
; NB: to be at least more than save weight to relieve register pressure.
(when (and (uvar-spilled? x) (not (uvar-poison? x)) (fx>= (uvar-ref-weight x) (uvar-save-weight x)))
(uvar-local-save! x #t)))
spillable*)
(for-each
(lambda (block)
(block-effect*-set! block
(fold-right foldable-Effect
(cond
[(or (goto-block? block) (joto-block? block)) '()]
[(if-block? block) (if-block-pred-set! block (Pred (if-block-pred block))) '()]
[(tail-block? block) (tail-block-tail-set! block (Tail (tail-block-tail block))) '()]
[(newframe-block? block)
(let ([info (newframe-block-info block)])
(process-info-newframe! info)
(safe-assert (andmap (lambda (x) (live? (newframe-block-live-call block) live-size x)) (info-newframe-local-save* info)))
(with-output-language (L15b Effect)
(let ([live (newframe-block-live-out block)])
(fold-left
(lambda (new-effect* x)
(let ([loc (uvar-location x)])
($add-move! x loc 2)
(cons `(set! ,(make-live-info live) ,loc ,x) new-effect*)))
(cons `(fp-offset ,(make-live-info live) ,(fx* (info-newframe-frame-words info) (constant ptr-bytes))) '())
(info-newframe-local-save* info)))))]
[else (sorry! who "unrecognized block ~s" block)])
(block-effect* block))))
block*)
(for-each
(lambda (x)
(when (uvar-local-save? x)
(uvar-location-set! x #f)
(uvar-spilled! x #f)
(uvar-save-weight-set! x 0)))
spillable*)
`(dummy))))
(define record-fp-offsets!
(lambda (block*)
(define-who record-fp-offsets!
(lambda (block cur-off)
(define Effect
(lambda (cur-off effect)
(nanopass-case (L15b Effect) effect
[(fp-offset ,live-info ,imm)
(let ([cur-off (fx+ cur-off imm)])
(safe-assert (fx>= cur-off 0))
cur-off)]
[else cur-off])))
(let ([block-off (block-fp-offset block)])
(if block-off
(unless (fx= cur-off block-off)
(sorry! who "conflicting fp-offset value for block ~s" block))
(let ([effect* (block-effect* block)])
(block-fp-offset-set! block cur-off)
(cond
[(goto-block? block)
(record-fp-offsets! (goto-block-next block) (fold-left Effect cur-off effect*))]
[(joto-block? block)
(record-fp-offsets! (joto-block-next block) 0)]
[(if-block? block)
(let ([cur-off (fold-left Effect cur-off effect*)])
(record-fp-offsets! (if-block-true block) cur-off)
(record-fp-offsets! (if-block-false block) cur-off))]
[(tail-block? block) (void)]
[(newframe-block? block)
(let ([cur-off (fold-left Effect cur-off effect*)])
(record-fp-offsets! (newframe-block-next block) cur-off)
(for-each (lambda (rp) (record-fp-offsets! rp cur-off)) (newframe-block-rp* block))
(record-fp-offsets! (newframe-block-rp block) cur-off))]
[else (sorry! who "unrecognized block ~s" block)]))))))
(for-each (lambda (block) (record-fp-offsets! block 0)) block*)))
(define-pass finalize-frame-locations! : (L15b Dummy) (ir block*) -> (L15c Dummy) ()
(definitions
(define var->loc
(lambda (x)
(or (and (uvar? x) (uvar-location x)) x)))
(define fv->mref
(lambda (x cur-off)
(if (fv? x)
(with-output-language (L15c Lvalue)
`(mref ,%sfp ,%zero ,(fx- (fx* (fv-offset x) (constant ptr-bytes)) cur-off)))
x))))
(Lvalue : Lvalue (ir cur-off) -> Lvalue ()
[(mref ,x0 ,x1 ,imm)
`(mref ,(fv->mref (var->loc x0) cur-off) ,(fv->mref (var->loc x1) cur-off) ,imm)]
[,x (fv->mref (var->loc x) cur-off)])
; NB: defining Triv & Rhs with cur-off argument so we actually get to our version of Lvalue
(Triv : Triv (ir cur-off) -> Triv ())
(Rhs : Rhs (ir cur-off) -> Rhs ())
(Pred : Pred (ir cur-off) -> Pred ())
(Tail : Tail (ir cur-off) -> Tail ())
(Effect : Effect (ir cur-off) -> Effect ())
(begin
(for-each
(lambda (block)
(block-effect*-set! block
(let f ([effect* (block-effect* block)] [cur-off (block-fp-offset block)])
(if (null? effect*)
(begin
(cond
[(or (goto-block? block) (joto-block? block) (newframe-block? block)) (void)]
[(if-block? block) (if-block-pred-set! block (Pred (if-block-pred block) cur-off))]
[(tail-block? block) (tail-block-tail-set! block (Tail (tail-block-tail block) cur-off))]
[else (sorry! who "unrecognized block ~s" block)])
'())
(with-output-language (L15c Effect)
(nanopass-case (L15b Effect) (car effect*)
[(fp-offset ,live-info ,imm)
(cons `(set! ,live-info ,%sfp
,(if (fx< imm 0)
; subtract just to make the generated code more clear
`(inline ,null-info ,%- ,%sfp (immediate ,(fx- imm)))
`(inline ,null-info ,%+ ,%sfp (immediate ,imm))))
(f (cdr effect*) (fx+ cur-off imm)))]
[(set! ,live-info ,x0 ,x1)
(let ([x0 (var->loc x0)] [x1 (var->loc x1)])
(if (eq? x0 x1)
(f (cdr effect*) cur-off)
(cons `(set! ,live-info ,(fv->mref x0 cur-off) ,(fv->mref x1 cur-off)) (f (cdr effect*) cur-off))))]
[else (cons (Effect (car effect*) cur-off) (f (cdr effect*) cur-off))]))))))
block*)
`(dummy)))
(module (select-instructions!)
(define make-tmp
(lambda (x)
(import (only np-languages make-unspillable))
(let ([tmp (make-unspillable x)])
(set! unspillable* (cons tmp unspillable*))
tmp)))
(define make-restricted-unspillable
(lambda (x reg*)
(import (only np-languages make-restricted-unspillable))
(safe-assert (andmap reg? reg*) (andmap var-index reg*))
(let ([tmp (make-restricted-unspillable x reg*)])
(set! unspillable* (cons tmp unspillable*))
tmp)))
(define make-precolored-unspillable
; instead of using machine registers like eax explicitly, we use an unspillable that
; conflicts with everything but the machine register. this is semantically equivalent
; for correct code but causes a spilled unspillable error if we try to use the same
; machine register for two conflicting variables
(lambda (name reg)
(or (reg-precolored reg)
(let ([tmp (make-restricted-unspillable name (remq reg (vector->list regvec)))])
(safe-assert (memq reg (vector->list regvec)))
(reg-precolored-set! reg tmp)
tmp))))
(define-syntax build-set!
(lambda (x)
(syntax-case x ()
[(k lhs rhs)
(with-implicit (k quasiquote with-output-language)
#`(with-output-language (L15d Effect)
`(set! ,(make-live-info) lhs rhs)))])))
(define imm?
(lambda (x)
(nanopass-case (L15c Triv) x
[(immediate ,imm) #t]
[(literal ,info) (not (info-literal-indirect? info))]
[(label-ref ,l ,offset) #t]
[else #f])))
(define imm0?
(lambda (x)
(nanopass-case (L15c Triv) x
[(immediate ,imm) (eqv? imm 0)]
[else #f])))
(define imm32?
(lambda (x)
(nanopass-case (L15c Triv) x
[(immediate ,imm)
(constant-case ptr-bits
[(32) #t] ; allows 2^31...2^32-1 per immediate?
[(64) (signed-32? imm)])] ; 2^31...2^32-1 aren't 32-bit values on 64-bit machines
[(literal ,info)
(constant-case ptr-bits
[(32) (not (info-literal-indirect? info))]
[(64) #f])]
[(label-ref ,l ,offset)
(constant-case ptr-bits
[(32) #t]
[(64) #f])]
[else #f])))
(define literal@?
(lambda (x)
(nanopass-case (L15c Triv) x
[(literal ,info) (info-literal-indirect? info)]
[else #f])))
(define mref?
(lambda (x)
(nanopass-case (L15c Triv) x
[(mref ,lvalue1 ,lvalue2 ,imm) #t]
[else #f])))
(define same?
(lambda (a b)
(or (eq? a b)
(nanopass-case (L15c Triv) a
[(mref ,lvalue11 ,lvalue12 ,imm1)
(nanopass-case (L15c Triv) b
[(mref ,lvalue21 ,lvalue22 ,imm2)
(and (or (and (eq? lvalue11 lvalue21) (eq? lvalue12 lvalue22))
(and (eq? lvalue11 lvalue22) (eq? lvalue12 lvalue21)))
(eqv? imm1 imm2))]
[else #f])]
[else #f]))))
(define-pass imm->imm : (L15c Triv) (ir) -> (L15d Triv) ()
(Lvalue : Lvalue (ir) -> Lvalue ()
[(mref ,lvalue1 ,lvalue2 ,imm) (sorry! who "unexpected mref ~s" ir)])
(Triv : Triv (ir) -> Triv ()))
(define-pass literal@->literal : (L15c Triv) (ir) -> (L15d Triv) ()
(Triv : Triv (ir) -> Triv ()
[(literal ,info)
`(literal
,(make-info-literal #f (info-literal-type info)
(info-literal-addr info) (info-literal-offset info)))]
[else (sorry! who "unexpected literal ~s" ir)]))
(define-pass select-instructions! : (L15c Dummy) (ir block* live-size force-overflow?) -> (L15d Dummy) ()
(definitions
(module (handle-jump handle-effect-inline handle-pred-inline handle-value-inline)
(define add-var (make-add-var live-size))
(define Triv
(lambda (out t)
(nanopass-case (L15d Triv) t
[(mref ,x1 ,x2 ,imm) (add-var (add-var out x2) x1)]
[,x (add-var out x)]
[else out])))
(define Rhs
(lambda (out rhs)
(nanopass-case (L15d Rhs) rhs
[(asm ,info ,proc ,t* ...) (fold-left Triv out t*)]
[else (Triv out rhs)])))
(define Pred
(lambda (out pred)
(nanopass-case (L15d Pred) pred
[(asm ,info ,proc ,t* ...) (fold-left Triv out t*)])))
(define Tail
(lambda (out tail)
(nanopass-case (L15d Tail) tail
[(jump ,t) (Triv out t)])))
(define unwrap
(lambda (etree effect* out)
(safe-assert (not (eq? out 'uninitialized)))
(with-values
(let f ([etree etree] [effect* effect*] [out out])
(if (pair? etree)
(let-values ([(effect* out) (f (cdr etree) effect* out)])
(f (car etree) effect* out))
(if (null? etree)
(values effect* out)
(values
(cons etree effect*)
(nanopass-case (L15d Effect) etree
[(set! ,live-info ,x ,rhs)
(live-info-live-set! live-info out)
(Rhs out rhs)]
[(set! ,live-info ,lvalue ,rhs)
(live-info-live-set! live-info out)
(Triv (Rhs out rhs) lvalue)]
[(asm ,info ,proc ,t* ...) (fold-left Triv out t*)]
[else out])))))
(lambda (effect* out) effect*))))
(define-who handle-jump
(lambda (t live)
(let-values ([(etree tail) (md-handle-jump t)])
(values (unwrap etree '() (Tail live tail)) tail))))
(define-who handle-effect-inline
(lambda (effect-prim info new-effect* t* live)
(unwrap (apply (primitive-handler effect-prim) info t*) new-effect* live)))
(define-who handle-pred-inline
(lambda (pred-prim info t* live)
(let-values ([(etree pred) (apply (primitive-handler pred-prim) info t*)])
(values (unwrap etree '() (Pred live pred)) pred))))
(define-who handle-value-inline
(lambda (lvalue value-prim info new-effect* t* live)
(unwrap (apply (primitive-handler value-prim) info lvalue t*) new-effect* live))))
(define compute-overage
(lambda (max-fs@call)
(if force-overflow?
(fxmax
(fx- (fx* max-fs@call (constant ptr-bytes)) 0)
(fx- (fx* (fx+ max-fv 1) (constant ptr-bytes)) (fx- (constant stack-slop) (fx* (constant stack-frame-limit) 2))))
(fxmax
(fx- (fx* max-fs@call (constant ptr-bytes)) (constant stack-frame-limit))
(fx- (fx* (fx+ max-fv 1) (constant ptr-bytes)) (fx- (constant stack-slop) (constant stack-frame-limit)))))))
(define overage (compute-overage max-fs@call))
(define handle-overflow-check
(lambda (reg info new-effect* live)
(let-values ([(xnew-effect* pred) (handle-pred-inline %u< null-info
(list
reg
(meta-cond
[(real-register? '%esp) %esp]
[else (with-output-language (L15c Triv)
`(mref ,%tc ,%zero ,(tc-disp %esp)))]))
live)])
(append xnew-effect*
(cons (with-output-language (L15d Effect)
`(overflow-check ,pred
,(handle-effect-inline %asmlibcall! info '() '() live)
...))
new-effect*)))))
(define maybe-incr-instr-count
(lambda (block e*)
(define checks-cc? ; copied from instrument
(lambda (block)
(and (if-block? block)
(null? (block-effect* block))
(nanopass-case (L15c Pred) (if-block-pred block)
[(inline ,live-info ,info ,pred-prim ,t* ...) (eq? pred-prim %condition-code)]
[else #f]))))
(define count
(lambda (n e)
; overflow-check counts as one instruction...close enough, since it rarely fails
(nanopass-case (L15d Effect) e
[(rp-header ,mrvl ,fs ,lpm) n]
[(move-related ,x1 ,x2) n]
[else (fx+ n 1)])))
(if (generate-instruction-counts)
(let* ([n (fold-left count (if (goto-block? block) 0 1) e*)]
[f (lambda (e*)
(handle-effect-inline %inc-cc-counter null-info e*
(list %tc
(with-output-language (L15c Triv) `(immediate ,(constant tc-instr-counter-disp)))
(with-output-language (L15c Triv) `(immediate ,n)))
(block-live-in block)))])
(if (and (not (null? e*))
(nanopass-case (L15d Effect) (car e*)
[(rp-header ,mrvl ,fs ,lpm) #t]
[else #f]))
(cons (car e*) (f (cdr e*)))
(begin
(assert (not (checks-cc? block)))
(f e*))))
e*))))
(Rhs : Rhs (ir lvalue new-effect* live) -> * (new-effect*)
[(inline ,info ,value-prim ,t* ...)
(handle-value-inline lvalue value-prim info new-effect* t* live)]
[else (handle-value-inline lvalue %move null-info new-effect* (list ir) live)])
(Tail : Tail (ir) -> Tail ()
[(jump ,live-info ,t) (handle-jump t (live-info-live live-info))]
[(goto ,l) (values '() `(goto ,l))]
[(asm-return) (values '() `(asm-return))]
[(asm-c-return ,info) (values '() `(asm-c-return ,info))])
(Effect : Effect (ir new-effect*) -> * (new-effect*)
[(set! ,live-info ,lvalue ,rhs) (Rhs rhs lvalue new-effect* (live-info-live live-info))]
[(inline ,live-info ,info ,effect-prim ,t* ...)
(handle-effect-inline effect-prim info new-effect* t* (live-info-live live-info))]
[(rp-header ,mrvl ,fs ,lpm)
(cons (with-output-language (L15d Effect) `(rp-header ,mrvl ,fs ,lpm)) new-effect*)]
[(overflow-check ,live-info)
(if (fx> 1 overage (fx- (constant stack-frame-limit) (constant stack-slop)))
(handle-overflow-check %sfp (intrinsic-info-asmlib dooverflow #f) new-effect* (live-info-live live-info))
new-effect*)]
[(overflood-check ,live-info)
(if (fx> overage 0)
; dooverflood protocol requires %xp be set where we need esp to be
(let ([uxp (make-precolored-unspillable 'uxp %xp)])
(handle-value-inline uxp %+ null-info
(handle-overflow-check uxp (intrinsic-info-asmlib dooverflood #f) new-effect* (live-info-live live-info))
(list %sfp (with-output-language (L15c Triv) `(immediate ,overage)))
(live-info-live live-info)))
new-effect*)]
[(fcallable-overflow-check ,live-info)
; max-fs@call = 2: the return address and c-chain stored by C-call->XXX
(if (fx> 1 (compute-overage 2) (fx- (constant stack-frame-limit) (constant stack-slop)))
(handle-overflow-check %sfp (intrinsic-info-asmlib dooverflow #f) new-effect* (live-info-live live-info))
new-effect*)])
(Pred : Pred (ir) -> Pred ()
[(inline ,live-info ,info ,pred-prim ,t* ...)
(handle-pred-inline pred-prim info t* (live-info-live live-info))])
(begin
(for-each
(lambda (block)
(block-effect*-set! block
(maybe-incr-instr-count block
(fold-right Effect
(cond
[(or (goto-block? block) (joto-block? block) (newframe-block? block)) '()]
[(if-block? block)
(let-values ([(new-effect* pred) (Pred (if-block-pred block))])
(if-block-pred-set! block pred)
new-effect*)]
[(tail-block? block)
(let-values ([(new-effect* tail) (Tail (tail-block-tail block))])
(tail-block-tail-set! block tail)
new-effect*)]
[else (sorry! who "unrecognized block ~s" block)])
(block-effect* block)))))
block*)
`(dummy)))
; NB: try to reuse unspillables to reduce the number we create
(architecture instructions)
)
(define-who do-unspillable-conflict!
(lambda (kfv kspillable varvec live-size kunspillable unvarvec block*)
(define remove-var (make-remove-var live-size))
(define unspillable?
(lambda (x)
(and (uvar? x) (uvar-unspillable? x))))
(define add-unspillable
(lambda (unspillable* x)
(if (and (unspillable? x) (not (uvar-seen? x)))
(begin
(uvar-seen! x #t)
(cons x unspillable*))
unspillable*)))
(define add-move!
(lambda (x1 x2)
(when (var-index x2)
($add-move! x1 x2 2)
($add-move! x2 x1 2))))
(define add-move-hint!
(lambda (x1 x2)
(when (var-index x2)
($add-move! x1 x2 1)
($add-move! x2 x1 1))))
(define add-static-conflict!
(lambda (u reg*)
(let ([u-offset (var-index u)])
(for-each
(lambda (reg) (conflict-bit-set! (var-unspillable-conflict* reg) u-offset))
reg*))))
(define add-us->s-conflicts!
(lambda (x out) ; x is an unspillable
(let ([x-offset (var-index x)] [cset (var-spillable-conflict* x)])
(tree-for-each out live-size 0 live-size
(lambda (y-offset)
(let* ([y (vector-ref varvec y-offset)] [y-cset (var-unspillable-conflict* y)])
(when y-cset
; if y is a spillable, point the unspillable x at y
(when (fx< y-offset kspillable) (conflict-bit-set! cset y-offset))
; point y at the unspillable x
(conflict-bit-set! y-cset x-offset))))))))
(define add-us->us-conflicts!
(lambda (x unspillable*) ; x is a unspillable
(let ([x-offset (var-index x)] [cset (var-unspillable-conflict* x)])
(for-each
(lambda (y)
(let ([y-offset (var-index y)])
(conflict-bit-set! cset y-offset)
(conflict-bit-set! (var-unspillable-conflict* y) x-offset)))
unspillable*))))
(define add-s->us-conflicts!
(lambda (x unspillable*) ; x is a spillable or register
(let ([x-offset (var-index x)] [cset (var-unspillable-conflict* x)])
(for-each
(lambda (y)
(let ([y-offset (var-index y)])
; point x at unspillable y
(conflict-bit-set! cset y-offset)
; if x is a spillable, point unspillable y at x
(when (fx< x-offset kspillable) (conflict-bit-set! (var-spillable-conflict* y) x-offset))))
unspillable*))))
(define Triv
(lambda (unspillable* t)
(nanopass-case (L15d Triv) t
[(mref ,x1 ,x2 ,imm) (add-unspillable (add-unspillable unspillable* x2) x1)]
[,x (add-unspillable unspillable* x)]
[else unspillable*])))
(define Rhs
(lambda (unspillable* rhs)
(nanopass-case (L15d Rhs) rhs
[(asm ,info ,proc ,t* ...) (fold-left Triv unspillable* t*)]
[else (Triv unspillable* rhs)])))
(define Pred
(lambda (p)
(nanopass-case (L15d Pred) p
[(asm ,info ,proc ,t* ...) (fold-left Triv '() t*)]
[else (sorry! who "unexpected pred ~s" p)])))
(define Tail
(lambda (tl)
(nanopass-case (L15d Tail) tl
[(jump ,t) (Triv '() t)]
[else '()])))
(define Effect*
(lambda (e* unspillable*)
(if (null? e*)
(safe-assert (null? unspillable*))
(Effect* (cdr e*)
(nanopass-case (L15d Effect) (car e*)
[(set! ,live-info ,x ,rhs)
(let ([spillable-live (live-info-live live-info)])
(if (unspillable? x)
(let ([unspillable* (remq x unspillable*)])
(safe-assert (uvar-seen? x))
(uvar-seen! x #f)
(if (and (var? rhs) (var-index rhs))
(begin
(if (unspillable? rhs)
(begin
(add-us->us-conflicts! x (remq rhs unspillable*))
(add-us->s-conflicts! x spillable-live))
(begin
(add-us->us-conflicts! x unspillable*)
(add-us->s-conflicts! x (remove-var spillable-live rhs))))
(add-move! x rhs))
(begin
(add-us->us-conflicts! x unspillable*)
(add-us->s-conflicts! x spillable-live)))
(Rhs unspillable* rhs))
(begin
(when (var-unspillable-conflict* x)
(if (unspillable? rhs)
(begin
(add-s->us-conflicts! x (remq rhs unspillable*))
(add-move! x rhs))
(add-s->us-conflicts! x unspillable*)))
(Rhs unspillable* rhs))))]
[(set! ,live-info ,lvalue ,rhs) (Triv (Rhs unspillable* rhs) lvalue)]
[(asm ,info ,proc ,t* ...) (fold-left Triv unspillable* t*)]
[(move-related ,x1 ,x2) (add-move-hint! x1 x2) unspillable*]
[(overflow-check ,p ,e* ...) (Effect* (reverse e*) '()) (Pred p)]
[else unspillable*])))))
(for-each (lambda (x) (var-spillable-conflict*-set! x (make-empty-cset kspillable))) unspillable*)
(let ([f (lambda (x) (var-unspillable-conflict*-set! x (make-empty-cset kunspillable)))])
(vector-for-each f regvec)
(for-each f spillable*)
(vector-for-each f unvarvec))
(vector-for-each (lambda (x) (add-static-conflict! x (uvar-conflict* x))) unvarvec)
(for-each
(lambda (block)
(Effect* (reverse (block-effect* block))
(cond
[(or (goto-block? block) (joto-block? block) (newframe-block? block)) '()]
[(if-block? block) (Pred (if-block-pred block))]
[(tail-block? block) (Tail (tail-block-tail block))]
[else (sorry! who "unrecognized block ~s" block)])))
block*)))
(define-who assign-registers!
(lambda (lambda-info varvec unvarvec)
(define k (vector-length regvec))
(define uvar-weight
(lambda (x)
(fx- (uvar-ref-weight x) (uvar-save-weight x))))
; could also be calculated when the conflict set is built, which would be more
; efficient for low-degree variables
(define compute-degrees!
(lambda (x*)
; account for uvar -> uvar conflicts
(for-each
(lambda (x)
(uvar-degree-set! x
(fx+
; spills have been trimmed from the var-spillable-conflict* sets
(conflict-bit-count (var-spillable-conflict* x))
(conflict-bit-count (var-unspillable-conflict* x)))))
x*)
; account for reg -> uvar conflicts
(vector-for-each
(lambda (reg)
(cset-for-each (var-spillable-conflict* reg)
(lambda (x-offset)
(let ([x (vector-ref varvec x-offset)])
(unless (uvar-location x)
(uvar-degree-set! x (fx+ (uvar-degree x) 1))))))
(cset-for-each (var-unspillable-conflict* reg)
(lambda (x-offset)
(let ([x (vector-ref unvarvec x-offset)])
(uvar-degree-set! x (fx+ (uvar-degree x) 1))))))
regvec)))
(define-who find-home!
(lambda (x)
(define conflict?
(lambda (reg x)
(let ([cset (if (uvar-unspillable? x) (var-unspillable-conflict* reg) (var-spillable-conflict* reg))])
(conflict-bit-set? cset (var-index x)))))
(define find-move-related-home
(lambda (x0 succ fail)
(let f ([x x0] [work* '()] [clear-seen! void])
(if (uvar-seen? x)
(if (null? work*) (begin (clear-seen!) (fail)) (f (car work*) (cdr work*) clear-seen!))
(let ([clear-seen! (lambda () (uvar-seen! x #f) (clear-seen!))])
(uvar-seen! x #t)
(let loop ([move* (uvar-move* x)] [work* work*])
(if (null? move*)
(if (null? work*) (begin (clear-seen!) (fail)) (f (car work*) (cdr work*) clear-seen!))
(let ([var (caar move*)] [move* (cdr move*)])
(define try-reg
(lambda (reg)
(if (conflict? reg x0)
(loop move* work*)
(begin (clear-seen!) (succ reg)))))
(if (reg? var)
(try-reg var)
(if (uvar? var)
(let ([reg (uvar-location var)])
(if (reg? reg)
(try-reg reg)
(loop move* (cons var work*))))
(loop move* work*)))))))))))
(define set-home!
(lambda (home)
(define update-conflict!
(lambda (reg x)
(cset-merge! (var-spillable-conflict* reg) (var-spillable-conflict* x))
(cset-merge! (var-unspillable-conflict* reg) (var-unspillable-conflict* x))))
(uvar-location-set! x home)
(update-conflict! home x)))
(find-move-related-home x
set-home!
(lambda ()
(let f ([offset (fx- k 1)])
(cond
[(fx< offset 0)
(uvar-spilled! x #t)
(when (uvar-unspillable? x)
(sorry! who "spilled unspillable ~s" x))]
[(conflict? (vector-ref regvec offset) x) (f (fx- offset 1))]
[else (set-home! (vector-ref regvec offset))]))))))
(define pick-victims
(lambda (x*)
(define low-degree? (lambda (x) (fx< (uvar-degree x) k)))
(define pick-potential-spill
; x* is already sorted by weight, so this effectively picks uvar with
; the highest degree among those with the lowest weight
(lambda (x*)
(let ([x (let f ([x* (cdr x*)] [max-degree (uvar-degree (car x*))] [max-x (car x*)])
(if (null? x*)
max-x
(let ([x (car x*)] [x* (cdr x*)])
(if (or (uvar-unspillable? x) (fx> (uvar-weight x) (uvar-weight max-x)))
max-x
(let ([degree (uvar-degree x)])
(if (fx> degree max-degree)
(f x* degree x)
(f x* max-degree max-x)))))))])
(values x (remq x x*)))))
(define remove-victim!
(lambda (victim)
(cset-for-each (var-spillable-conflict* victim)
(lambda (offset)
(let ([x (vector-ref varvec offset)])
(uvar-degree-set! x (fx- (uvar-degree x) 1)))))
(cset-for-each (var-unspillable-conflict* victim)
(lambda (offset)
(let ([x (vector-ref unvarvec offset)])
(uvar-degree-set! x (fx- (uvar-degree x) 1)))))))
(define sort-victims
; NB: sorts based on likelihood of successfully assigning move-related vars to the same register
; NB: probably should sort based on value of assigning move-related vars to the same register,
; NB: i.e., taking into account the ref-weight
(lambda (victim*)
(map car
(list-sort
(lambda (x y) (fx> (cdr x) (cdr y)))
(map (lambda (x)
(define relevant?
(lambda (x)
(or (reg? x) (and (uvar? x) (not (uvar-spilled? x))))))
(do ([move* (uvar-move* x) (cdr move*)]
[w 0 (let ([move (car move*)])
(if (relevant? (car move))
(fx+ w (cdr move))
w))])
((null? move*) (cons x w))))
victim*)))))
(let-values ([(victim* keeper*) (partition low-degree? x*)])
(if (null? victim*)
(let-values ([(victim keeper*) (pick-potential-spill x*)])
; note: victim can be an unspillable if x* contains only precolored unspillables
(remove-victim! victim)
(values (list victim) keeper*))
(begin
(unless (null? keeper*)
; tried creating a mask from victim*, logand with bv for each x, count the bits,
; and subtract from x's uvar-degree-set!. code in chaff. didn't help at this point.
; perhaps if fxbit-count were implemented better it would
(for-each remove-victim! victim*))
(values (sort-victims victim*) keeper*))))))
(let ([x* (append (sort (lambda (x y) (fx< (uvar-weight x) (uvar-weight y))) spillable*) unspillable*)])
(compute-degrees! x*)
(let f ([x* x*])
(unless (null? x*)
(let-values ([(victim* x*) (pick-victims x*)])
(f x*)
(for-each find-home! victim*)))))))
(define everybody-home?
(lambda ()
(safe-assert (andmap uvar-location unspillable*))
(andmap uvar-location spillable*)))
(define record-inspector-information!
(lambda (info)
(define get-closure-fv-names
(lambda (info ctci)
(define (get-name fv) (unannotate (uvar-source fv)))
(or (ctci-closure-fv-names ctci)
(case (info-lambda-closure-rep info)
[(pair)
(let ([p (cons (get-name (car (info-lambda-fv* info)))
(get-name (cadr (info-lambda-fv* info))))])
(ctci-closure-fv-names-set! ctci p)
p)]
[(vector)
(let ([v (list->vector (map get-name (info-lambda-fv* info)))])
(ctci-closure-fv-names-set! ctci v)
v)]
[else #f]))))
(cond
[(info-lambda-ctci info) =>
(lambda (ctci)
(ctci-live-set! ctci
(let f ([i 0] [spillable* spillable*])
(if (null? spillable*)
(make-vector i)
(let ([spillable (car spillable*)])
(cond
[(and (uvar-spilled? spillable) (uvar-source spillable)) =>
(lambda (source)
(if (eq? source (let () (include "types.ss") cpsymbol))
(case (info-lambda-closure-rep info)
[(singleton)
(cond
[(uvar-source (car (info-lambda-fv* info))) =>
(lambda (source)
(let ([v (f (fx+ i 1) (cdr spillable*))])
(uvar-iii-set! spillable i)
(vector-set! v i (cons (unannotate source) (fv-offset (uvar-location spillable))))
v))]
[else (f i (cdr spillable*))])]
[(pair vector)
(let ([v (f (fx+ i 1) (cdr spillable*))])
(uvar-iii-set! spillable i)
(vector-set! v i
(cons (get-closure-fv-names info ctci)
(fv-offset (uvar-location spillable))))
v)]
[(closure)
(let ([v (f (fx+ i 1) (cdr spillable*))])
(uvar-iii-set! spillable i)
(vector-set! v i (cons (unannotate source) (fv-offset (uvar-location spillable))))
v)]
[else (f i (cdr spillable*))])
(let ([v (f (fx+ i 1) (cdr spillable*))])
(uvar-iii-set! spillable i)
(vector-set! v i (cons (unannotate source) (fv-offset (uvar-location spillable))))
v)))]
[else (f i (cdr spillable*))]))))))])))
(define-pass finalize-register-locations! : (L15d Dummy) (ir block*) -> (L15e Dummy) ()
(definitions
(define var->loc
(lambda (x)
(if (uvar? x)
(or (uvar-location x) (sorry! who "no location assigned to uvar ~s" x))
x))))
(Lvalue : Lvalue (ir) -> Lvalue ()
[(mref ,x0 ,x1 ,imm) `(mref ,(var->loc x0) ,(var->loc x1) ,imm)]
[,x (var->loc x)])
(Pred : Pred (ir) -> Pred ())
(Tail : Tail (ir) -> Tail ())
(Effect : Effect (ir) -> Effect ()
[(set! ,live-info ,[lvalue] ,[rhs]) `(set! ,lvalue ,rhs)])
(foldable-Effect : Effect (ir new-effect*) -> * (new-effect*)
[(move-related ,x1 ,x2) new-effect*]
[(set! ,live-info ,x0 ,x1)
(let ([x0 (var->loc x0)] [x1 (var->loc x1)])
(if (eq? x0 x1)
new-effect*
(cons (Effect ir) new-effect*)))]
[else (cons (Effect ir) new-effect*)])
(begin
(for-each
(lambda (block)
(block-effect*-set! block (fold-right foldable-Effect '() (block-effect* block)))
(cond
[(or (goto-block? block) (joto-block? block) (newframe-block? block)) (void)]
[(if-block? block) (if-block-pred-set! block (Pred (if-block-pred block)))]
[(tail-block? block) (tail-block-tail-set! block (Tail (tail-block-tail block)))]
[else (sorry! who "unrecognized block ~s" block)]))
block*)
`(dummy)))
(define-pass expose-overflow-check-blocks! : (L15e Dummy) (ir entry-block0* block0*) -> (L16 Dummy) (entry-block* block*)
(definitions
(define block* block0*)
(define entry-block* entry-block0*)
(define-who redirect-link!
(lambda (old new)
(lambda (from)
(cond
[(goto-block? from)
(cond
[(eq? (goto-block-next from) old) (goto-block-next-set! from new)]
[else (sorry! who "goto-block in-link not found")])]
[(joto-block? from)
(cond
[(eq? (joto-block-next from) old) (joto-block-next-set! from new)]
[else (sorry! who "joto-block in-link not found")])]
[(if-block? from)
(cond
[(eq? (if-block-true from) old) (if-block-true-set! from new)]
[(eq? (if-block-false from) old) (if-block-false-set! from new)]
[else (sorry! who "if-block in-link not found")])]
[(newframe-block? from)
(cond
[(eq? (newframe-block-next from) old) (newframe-block-next-set! from new)]
[(eq? (newframe-block-rp from) old) (newframe-block-rp-set! from new)]
[(memq old (newframe-block-rp* from)) (newframe-block-rp*-set! from (subst new old (newframe-block-rp* from)))]
[else (sorry! who "newframe-block in-link not found")])]
[else (sorry! who "unexpected block ~s" from)]))))
(define insert-check!
(lambda (block rebefore* p ehere* eafter*)
(let ([libcall-block (make-goto-block)])
(goto-block-next-set! libcall-block block)
(block-pariah! libcall-block #t)
(let ([check-block (make-if-block block libcall-block)])
(if-block-pred-set! check-block p)
(block-effect*-set! check-block (reverse rebefore*))
(block-effect*-set! libcall-block ehere*)
(set! entry-block* (subst check-block block entry-block*))
(let ([label (block-label block)])
(block-label-set! check-block label)
(local-label-block-set! label check-block))
(let ([label (make-local-label 'post-overflow-check)])
(block-label-set! block label)
(local-label-block-set! label block))
(let ([label (make-local-label 'overflowed)])
(block-label-set! libcall-block label)
(local-label-block-set! label libcall-block))
(for-each (redirect-link! block check-block) (block-in-link* block))
(block-in-link*-set! block (list check-block libcall-block))
(set! block* (cons* check-block libcall-block block*))
(Effect* block '() eafter*)))))
(define Effect*
(lambda (block rebefore* eafter*)
(if (null? eafter*)
(block-effect*-set! block (reverse rebefore*))
(let ([e (car eafter*)] [eafter* (cdr eafter*)])
(nanopass-case (L15e Effect) e
[(overflow-check ,[Pred : p] ,[Effect : e*] ...) (insert-check! block rebefore* p e* eafter*)]
[else (Effect* block (cons (Effect e) rebefore*) eafter*)]))))))
(Pred : Pred (ir) -> Pred ())
(Tail : Tail (ir) -> Tail ())
(Effect : Effect (ir) -> Effect ())
; NB: without the begin, seems to ignore all but the first subform below
(begin
(for-each
(lambda (block)
(Effect* block '() (block-effect* block))
(cond
[(or (goto-block? block) (joto-block? block) (newframe-block? block)) (void)]
[(if-block? block) (if-block-pred-set! block (Pred (if-block-pred block)))]
[(tail-block? block) (tail-block-tail-set! block (Tail (tail-block-tail block)))]
[else (sorry! who "unrecognized block ~s" block)]))
block0*)
(values `(dummy) entry-block* block*)))
(define-syntax with-live-info-record-writer
(lambda (x)
(syntax-case x ()
[(_ live-size varvec e1 e2 ...)
#'(parameterize ([(case-lambda
[() (record-writer (record-type-descriptor live-info))]
[(x) (record-writer (record-type-descriptor live-info) x)])
(lambda (x p wr)
(when (live-info-useless x) (fprintf p "useless "))
(fprintf p "<live:")
(let ([live (live-info-live x)])
(if (eq? live 'uninitialized)
(fprintf p " uninitialized")
(for-each (lambda (x) (fprintf p " ~s" x)) (get-live-vars live live-size varvec))))
(fprintf p ">"))])
e1 e2 ...)])))
(define-pass np-allocate-registers : L15a (ir) -> L16 ()
(CaseLambdaExpr : CaseLambdaExpr (ir) -> CaseLambdaExpr ()
[(lambda ,info ,max-fv0 (,local* ...) (,entry-block* ...) (,block* ...))
(let ()
(define block-printer
(lambda (unparser name block*)
(p-dot-graph block* (current-output-port))
(p-graph block* name (current-output-port) unparser)))
(module (RApass)
(define RAprinter
(lambda (unparser)
(lambda (val*)
(block-printer unparser (info-lambda-name info) block*))))
(define-syntax RApass
(lambda (x)
(syntax-case x ()
[(_ ?unparser pass-name ?arg ...)
#'(xpass pass-name (RAprinter ?unparser) (list ?arg ...))]))))
(safe-assert (andmap (lambda (x) (eq? (uvar-location x) #f)) local*))
(let ([kspillable (length local*)] [kfv (fx+ max-fv0 1)] [kreg (vector-length regvec)])
(fluid-let ([spillable* local*] [unspillable* '()] [max-fv max-fv0] [max-fs@call 0] [poison-cset (make-empty-cset kspillable)])
(let* ([live-size (fx+ kfv kreg kspillable)] [varvec (make-vector live-size)])
; set up var indices & varvec mapping from indices to vars
(fold-left (lambda (i x) (var-index-set! x i) (vector-set! varvec i x) (fx+ i 1)) 0 spillable*)
(do ([i 0 (fx+ i 1)]) ((fx= i kfv)) (let ([fv (get-fv i)] [i (fx+ i kspillable)]) (var-index-set! fv i) (vector-set! varvec i fv)))
(do ([i 0 (fx+ i 1)]) ((fx= i kreg)) (let ([reg (vector-ref regvec i)] [i (fx+ i kspillable kfv)]) (var-index-set! reg i) (vector-set! varvec i reg)))
(with-live-info-record-writer live-size varvec
; run intra/inter-block live analysis
(RApass unparse-L15a do-live-analysis! live-size entry-block*)
; this is worth enabling from time to time...
#;(check-entry-live! (info-lambda-name info) live-size varvec entry-block*)
; rerun intra-block live analysis and record (fv v reg v spillable) x spillable conflicts
(RApass unparse-L15a record-call-live! block* varvec)
;; NB: we could just use (vector-length varvec) to get live-size
(when (fx> kspillable 1000) ; NB: parameter?
(RApass unparse-L15a identify-poison! kspillable varvec live-size block*))
(RApass unparse-L15a do-spillable-conflict! kspillable kfv varvec live-size block*)
#;(show-conflicts (info-lambda-name info) varvec '#())
; find frame homes for call-live variables; adds new fv x spillable conflicts
(RApass unparse-L15a assign-frame! (filter uvar-spilled? spillable*))
#;(show-homes)
(RApass unparse-L15a record-inspector-information! info)
; determine frame sizes at nontail-call sites and assign homes to new-frame variables
; adds new fv x spillable conflicts
(let ([dummy (RApass unparse-L15b assign-new-frame! (with-output-language (L15a Dummy) `(dummy)) info live-size varvec block*)])
; record fp offset on entry to each block
(RApass unparse-L15b record-fp-offsets! entry-block*)
; assign frame homes to poison variables
(let ([spill* (filter (lambda (x) (and (not (uvar-location x)) (uvar-poison? x))) spillable*)])
(unless (null? spill*)
(for-each (lambda (x) (uvar-spilled! x #t)) spill*)
(RApass unparse-L15b assign-frame! spill*)))
; on entry to loop, have assigned call-live and new-frame variables to frame homes, determined frame sizes, and computed block-entry fp offsets
(let ([saved-reg-csets (vector-map (lambda (reg) (cset-copy (var-spillable-conflict* reg))) regvec)]
[bcache* (map cache-block-info block*)])
(let loop ()
(for-each
(lambda (spill)
; remove each spill from each other spillable's spillable conflict set
(unless (uvar-poison? spill)
(let ([spill-index (var-index spill)])
(cset-for-each (var-spillable-conflict* spill)
(lambda (i)
(let ([x (vector-ref varvec i)])
(unless (uvar-location x)
(conflict-bit-unset! (var-spillable-conflict* x) spill-index)))))))
; release the spill's conflict* set
(var-spillable-conflict*-set! spill #f))
(filter uvar-location spillable*))
(set! spillable* (remp uvar-location spillable*))
(let ([saved-move* (map uvar-move* spillable*)])
#;(show-homes)
(let ([dummy (RApass unparse-L15c finalize-frame-locations! dummy block*)])
(let ([dummy (RApass unparse-L15d select-instructions! dummy block* live-size
(let ([libspec (info-lambda-libspec info)])
(and libspec (libspec-does-not-expect-headroom? libspec))))])
(vector-for-each (lambda (reg) (reg-precolored-set! reg #f)) regvec)
(let* ([kunspillable (length unspillable*)] [unvarvec (make-vector kunspillable)])
; set up var indices & unvarvec mapping from indices to unspillables
(fold-left (lambda (i x) (var-index-set! x i) (vector-set! unvarvec i x) (fx+ i 1)) 0 unspillable*)
; rerun intra-block live analysis and record (reg v spillable v unspillable) x unspillable conflicts
(RApass unparse-L15d do-unspillable-conflict! kfv kspillable varvec live-size kunspillable unvarvec block*)
#;(show-conflicts (info-lambda-name info) varvec unvarvec)
(RApass unparse-L15d assign-registers! info varvec unvarvec)
; release the unspillable conflict sets
(for-each (lambda (x) (var-unspillable-conflict*-set! x #f)) spillable*)
(vector-for-each (lambda (x) (var-unspillable-conflict*-set! x #f)) regvec)
#;(show-homes unspillable*)
(if (everybody-home?)
(let ([dummy (RApass unparse-L15e finalize-register-locations! dummy block*)])
; release the spillable conflict sets
(vector-for-each (lambda (reg) (var-spillable-conflict*-set! reg #f)) regvec)
(do ([i max-fv (fx- i 1)]) ((fx< i 0)) (var-spillable-conflict*-set! (get-fv i) #f))
(let-values ([(dummy entry-block* block*)
(xpass expose-overflow-check-blocks!
(lambda (val*)
(apply (lambda (dummy entry-block* block*)
(block-printer unparse-L16 (info-lambda-name info) block*))
val*))
(list dummy entry-block* block*))])
(safe-assert (andmap block-label (append entry-block* block*)))
(safe-assert (lambda (b) (eq? (local-label-block (block-label b)) b)) (append entry-block* block*))
`(lambda ,info (,entry-block* ...) (,block* ...))))
(begin
(for-each restore-block-info! block* bcache*)
(vector-for-each var-spillable-conflict*-set! regvec saved-reg-csets)
(for-each (lambda (x) (uvar-location-set! x #f)) spillable*)
(for-each uvar-move*-set! spillable* saved-move*)
(set! unspillable* '())
(RApass unparse-L15b assign-frame! (filter uvar-spilled? spillable*))
(loop)))))))))))))))])))
; NB: commonize with earlier
(define-pass np-remove-repeater-blocks-again! : L16 (ir) -> L16 ()
(definitions
(define path-compress!
(lambda (b)
(cond
[(block-repeater? b) (goto-block-next b)]
; NB: ignoring block-src* here, post-profiling
[(and (goto-block? b) (null? (block-effect* b)))
(block-repeater! b #t)
(let ([end (path-compress! (goto-block-next b))])
(goto-block-next-set! b end)
end)]
[else b])))
(define resolve
(lambda (b)
(if (block-repeater? b)
(goto-block-next b)
b))))
(CaseLambdaExpr : CaseLambdaExpr (ir) -> CaseLambdaExpr ()
[(lambda ,info (,entry-block* ...) (,block* ...))
(for-each path-compress! block*)
(for-each
(lambda (from)
(define resolve!
(lambda (get put!)
(let ([to (get from)])
(when (block-repeater? to)
(put! from (goto-block-next to))))))
(cond
[(goto-block? from)
(unless (block-repeater? from)
(resolve! goto-block-next goto-block-next-set!))]
[(joto-block? from)
(resolve! joto-block-next joto-block-next-set!)]
[(if-block? from)
(resolve! if-block-true if-block-true-set!)
(resolve! if-block-false if-block-false-set!)]
[(newframe-block? from)
(resolve! newframe-block-next newframe-block-next-set!)
(newframe-block-rp*-set! from (map resolve (newframe-block-rp* from)))
(resolve! newframe-block-rp newframe-block-rp-set!)]
[(tail-block? from) (void)]
[else (sorry! who "unrecognized block ~s" from)]))
block*)
(for-each (lambda (dcl)
(let* ([b0 (local-label-block dcl)] [b (and b0 (resolve b0))])
(unless (eq? b b0)
(local-label-block-set! dcl b)
(block-label-set! b dcl))))
(info-lambda-dcl* info))
`(lambda ,info
(,(map resolve entry-block*) ...)
(,(filter (lambda (b) (or (not (block-repeater? b)) (eq? (goto-block-next b) b))) block*) ...))]))
; NB: might instead sort blocks in np-generate-code, which is in a better position
; NB: to deal with block ordering when branch displacement sizes are limited
(define-pass np-optimize-block-order! : L16 (ir) -> L16 ()
(definitions
(define invertible?
(lambda (pred)
(nanopass-case (L16 Pred) pred
[(asm ,info ,proc ,t* ...)
(safe-assert (info-condition-code? info))
(info-condition-code-invertible? info)])))
(define block-likeliness
(lambda (b)
(or (block-weight b) 0)))
(define block-in-degree
(lambda (b)
(fold-left (lambda (n b) (if (block-seen? b) n (fx+ n 1))) 0 (block-in-link* b)))))
(CaseLambdaExpr : CaseLambdaExpr (ir) -> CaseLambdaExpr ()
[(lambda ,info (,entry-block* ...) (,block* ...))
(safe-assert (not (ormap block-seen? block*)))
(safe-assert (not (null? entry-block*)))
(let loop ([b (car entry-block*)] [w* '()] [pariah* (cdr entry-block*)] [rblock* '()])
(define next-worklist-entry
(lambda (w* pariah* rblock*)
(if (null? w*)
(if (null? pariah*)
(begin
(safe-assert (andmap block-label (append entry-block* rblock*)))
(safe-assert (lambda (b) (eq? (local-label-block (block-label b)) b)) (append entry-block* rblock*))
(for-each (lambda (b) (block-seen! b #f)) block*)
`(lambda ,info (,entry-block* ...) (,(reverse rblock*) ...)))
(loop (car pariah*) '() (cdr pariah*) rblock*))
(loop (car w*) (cdr w*) pariah* rblock*))))
(if (block-seen? b)
(next-worklist-entry w* pariah* rblock*)
(let ([rblock* (cons b rblock*)])
(block-seen! b #t)
(cond
[(goto-block? b) (loop (goto-block-next b) w* pariah* rblock*)]
[(joto-block? b) (loop (joto-block-next b) w* pariah* rblock*)]
[(if-block? b)
(let ([true (if-block-true b)] [false (if-block-false b)])
(if (block-seen? true)
(loop false w* pariah* rblock*)
(if (block-seen? false)
(loop true w* pariah* rblock*)
(if (invertible? (if-block-pred b))
(let ([llntrue (block-likeliness true)] [llnfalse (block-likeliness false)])
(if (or (and (fx= llnfalse llntrue)
(fx< (block-in-degree false) (block-in-degree true)))
(fx< llntrue llnfalse))
(if (fx< llntrue 0)
(loop false w* (cons true pariah*) rblock*)
(loop false (cons true w*) pariah* rblock*))
(if (fx< llnfalse 0)
(loop true w* (cons false pariah*) rblock*)
(loop true (cons false w*) pariah* rblock*))))
(if (fx< (block-likeliness false) 0)
(loop true w* (cons false pariah*) rblock*)
(loop true (cons false w*) pariah* rblock*))))))]
[(newframe-block? b)
(loop (newframe-block-next b)
(append (newframe-block-rp* b) (cons (newframe-block-rp b) w*))
pariah* rblock*)]
[(tail-block? b) (next-worklist-entry w* pariah* rblock*)]
[else (sorry! who "unrecognized block ~s" b)]))))]))
(define (np-after-calling-conventions ir)
(compose ir
(pass np-expand-hand-coded unparse-L13.5)
(pass np-expose-allocation-pointer unparse-L14)
(pass np-expose-basic-blocks unparse-L15a)
(pass np-remove-repeater-blocks! unparse-L15a)
(lambda (ir)
(if (and (or (eq? ($compile-profile) 'block) ($profile-block-data?)) ($sfd))
((pass np-add-block-source! unparse-L15a) ir)
ir))
(pass np-propagate-pariahty! unparse-L15a)
(lambda (ir)
(if (or (eq? ($compile-profile) 'source)
(and (eq? ($compile-profile) 'block) ($sfd)))
((pass np-insert-profiling unparse-L15a) ir)
ir))
(pass np-add-in-links! unparse-L15a)
(pass np-compute-loop-depth! unparse-L15a)
(pass np-weight-references! unparse-L15a)
np-allocate-registers ; aggregate pass...don't use pass macro, or it will show up in timings
(pass np-remove-repeater-blocks-again! unparse-L16)
(pass np-optimize-block-order! unparse-L16)
(pass np-generate-code)))
(set! $np-compile
(lambda (original-input-expression pt?)
(with-initialized-registers
(fluid-let ([frame-vars (make-vector 8 #f)]
[next-lambda-seqno 0]
[pass-time? pass-time?])
(compose original-input-expression
(pass cpnanopass unparse-L1)
(pass np-recognize-let unparse-L2)
(pass np-discover-names unparse-L3)
#;(lambda (ir) (unless (eqv? (optimize-level) 3) ((pass np-check-flags) ir)) ir)
(pass np-convert-assignments unparse-L4)
(pass np-sanitize-bindings unparse-L4)
(pass np-suppress-procedure-checks unparse-L4)
(pass np-recognize-mrvs unparse-L4.5)
(pass np-expand-foreign unparse-L4.75)
(pass np-recognize-loops unparse-L4.875)
(pass np-name-anonymous-lambda unparse-L5)
(pass np-convert-closures unparse-L6)
(pass np-optimize-direct-call unparse-L6)
(pass np-identify-scc unparse-L6)
(if ($optimize-closures)
(pass np-expand/optimize-closures unparse-L7)
(pass np-expand-closures unparse-L7))
(lambda (ir)
(if (fxzero? ($loop-unroll-limit))
ir
((pass np-profile-unroll-loops unparse-L7) ir)))
(pass np-simplify-if unparse-L7)
(pass np-expand-primitives unparse-L9)
(pass np-place-overflow-and-trap unparse-L9.5)
(pass np-rebind-on-ruined-path unparse-L9.5)
(pass np-finalize-loops unparse-L9.75)
(pass np-optimize-pred-in-value unparse-L9.75)
(pass np-remove-complex-opera* unparse-L10)
(pass np-push-mrvs unparse-L10.5)
(pass np-normalize-context unparse-L11)
(pass np-insert-trap-check unparse-L11.5)
(pass np-flatten-case-lambda unparse-L12)
(pass np-impose-calling-conventions unparse-L13)
np-after-calling-conventions)))))
(set! $np-boot-code
(lambda (which)
(with-initialized-registers
($c-func-code-record
(fluid-let ([frame-vars (make-vector 8 #f)]
[next-lambda-seqno 0]
[pass-time? #t])
(parameterize ([generate-inspector-information #f] [$compile-profile #f])
(np-after-calling-conventions
(with-output-language (L13 Program)
(let ([l (make-local-label 'Linvoke)])
`(labels ([,l (hand-coded ,which)]) ,l))))))))))
)
(set! $np-tracer tracer)
(set! $np-last-pass last-pass)
(set! $track-dynamic-closure-counts track-dynamic-closure-counts)
(set! $track-static-closure-counts track-static-closure-counts)
(set! $optimize-closures (make-parameter #t (lambda (x) (and x #t))))
)