This repository has been archived on 2022-08-10. You can view files and clone it, but cannot push or open issues or pull requests.
chez-openbsd/s/cp0.ss

4808 lines
248 KiB
Scheme
Raw Normal View History

2022-07-29 15:12:07 +02:00
;;; cp0.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.
;; TODO:
;; * make seq should just drop effect-free portions of e1 rather than
;; asking if the whole of e1 is simple.
;; * folding/specializing loops
;; * later (much)
;; - split up score for seqs to allow us to avoid adding in score of
;; e2 when we encounter (seq e1 e2) for simple e2 in residualize-call-opnds
;; * try using other than value in visit-operand in contexts where we visit the
;; operand of a singly referenced identifier, e.g., if we see (values opnd) in
;; test context, visit opnd in test context
;;
;; we now no longer collapse quote's into void and true quotes, but
;; rather make if suffer through a (very slightly) more expensive test for
;; record equality
;; N.B.: we use (operand-wd opnd) in cp0 singly referenced case; this is not quite
;; legitimate, since we can visit the operand more than once with the same (possibly
;; passive) watchdog. Thus we are potentially nonlinear, but in practice it allows
;; us to integrate many harmless singly referenced procedures.
;; calls to not multiply-referenced identifiers handled as follows:
;; * propagate multiply-referenced flag on copy propagation
;; (let ((x e1))
;; (let ((y x)) ; set multiply referenced flag on x
;; (let ((z y))
;; (z y))))
;; * don't treat as singly referenced when id => id on env lookup, i.e., id is free
;; (presumably outside of operator position, or we would have integrated during
;; value-visit-operand) in procedure being integrated
;; (let ((f e))
;; (let ((g (lambda () f)))
;; (g) ; don't treat f as singly referenced
;; (g)))
;; * exploit as follows:
;; - maintain singly-referenced-score in operand
;; - if operand-exp of singly-referenced id is a lambda,
;; run with it with operand's watchdog and passive scorer
;; - otherwise value-visit operand, run with result-exp
;; with alert watchdog and passive scorer
;; - set singly-referenced to score from passive scorer in either case
;; if integration succeeds
;; - residualize-call-opnds uses singly-referenced-score if non-false
(define $cp0
(let ()
(import (nanopass))
(include "base-lang.ss")
;;; set to #f for monovariant filter
(define-threaded polyvariant #t)
;;; set to #f to disable inlining of various primitives into code containing
;;; lambda expressions, e.g., for-each and record-accessor---generally not
;;; desirable when interpreting rather than compiling the residual code.
(define-threaded likely-to-be-compiled?)
;;; score-limit determines max amount of code any integration attempt
;;; can result in; effort-limit determines max amount of work that can
;;; be done attempting to integrate
(define-threaded score-limit 20)
(define-threaded effort-limit 200)
;;; inner unrolling doesn't work, and when set nonzero, effectively
;;; disables outer unrolling as well
(define-threaded inner-unroll-limit 0)
;;; outer-unroll-limit of 0 disables integration of recursive
;;; procedures. outer-unroll-limit of 1 is probably a more
;;; reasonable default, except we then trash cp1's loop recognition
(define-threaded outer-unroll-limit 0)
;;; used to memoize pure?, etc.
(define-threaded cp0-info-hashtable)
(module ()
(define-syntax define-cp0-param
(syntax-rules ()
[(_ global-name local-name filter)
(set! global-name
(case-lambda
[() local-name]
[(x) (set! local-name (filter 'global-name x))]))]))
(define filter-limit
(lambda (who x)
(unless (and (fixnum? x) (fx>= x 0))
($oops who "invalid limit ~s" x))
x))
(define filter-bool (lambda (who x) (and x #t)))
(define-cp0-param cp0-effort-limit effort-limit filter-limit)
(define-cp0-param cp0-score-limit score-limit filter-limit)
(define-cp0-param cp0-outer-unroll-limit outer-unroll-limit filter-limit)
(define-cp0-param $cp0-inner-unroll-limit inner-unroll-limit filter-limit)
(define-cp0-param $cp0-polyvariant polyvariant filter-bool))
(define (rappend ls1 ls2)
(if (null? ls1)
ls2
(rappend (cdr ls1) (cons (car ls1) ls2))))
; don't use rtd-* as defined in record.ss in case we're building a patch
; file for cross compilation, because the offsets may be incorrect
(define rtd-flds (csv7:record-field-accessor #!base-rtd 'flds))
(define rtd-parent (csv7:record-field-accessor #!base-rtd 'parent))
(define rtd-size (csv7:record-field-accessor #!base-rtd 'size))
(define rtd-pm (csv7:record-field-accessor #!base-rtd 'pm))
; compile-time rtds (ctrtds)
(define ctrtd-opaque-known #b0000001)
(define ctrtd-sealed-known #b0000010)
(define base-ctrtd ($make-record-type #!base-rtd #!base-rtd "ctrtd" '((immutable flags)) #t #f))
(define ctrtd? (record-predicate base-ctrtd))
(define ctrtd-flags (record-accessor base-ctrtd 0))
(define record-type-sealed-known?
(lambda (rtd)
(or (not (ctrtd? rtd))
(fxlogtest (ctrtd-flags rtd) ctrtd-sealed-known))))
(define record-type-opaque-known?
(lambda (rtd)
(or (not (ctrtd? rtd))
(fxlogtest (ctrtd-flags rtd) ctrtd-opaque-known))))
(with-output-language (Lsrc Expr)
(define void-rec `(quote ,(void)))
(define true-rec `(quote #t))
(define false-rec `(quote #f))
(define null-rec `(quote ()))
(define empty-vector-rec `(quote #()))
(define empty-string-rec `(quote ""))
(define empty-bytevector-rec `(quote #vu8()))
(define empty-fxvector-rec `(quote #vfx()))
;;; environments
(module (empty-env with-extended-env lookup)
(define empty-env '())
(define-record-type env
(nongenerative)
(fields old-ids new-ids next))
(define-syntax with-extended-env
(syntax-rules ()
[(_ ((new-env new-ids) (?old-env ?old-ids ?opnds)) e1 e2 ...)
(let-values ([(new-env new-ids) (extend-env ?old-env ?old-ids ?opnds)])
(let ([e (let () e1 e2 ...)])
(deinitialize-ids! new-ids)
e))]))
(define extend-env
(lambda (old-env old-ids opnds)
(let ([new-ids (let loop ([old-ids old-ids] [opnds opnds] [rnew-ids '()])
(if (null? old-ids)
(reverse rnew-ids)
(loop
(cdr old-ids)
(and opnds (cdr opnds))
(cons
(let ([old-id (car old-ids)])
(make-prelex
(prelex-name old-id)
(let ([flags (prelex-flags old-id)])
(fxlogor
(fxlogand flags (constant prelex-sticky-mask))
(fxsll (fxlogand flags (constant prelex-is-mask))
(constant prelex-was-flags-offset))))
(prelex-source old-id)
(and opnds
(let ([opnd (car opnds)])
(when (operand? opnd)
(operand-name-set! opnd (prelex-name old-id)))
opnd))))
rnew-ids))))])
(values (make-env (list->vector old-ids) (list->vector new-ids) old-env) new-ids))))
(define deinitialize-ids!
(lambda (ids)
; clear operand field (a) to release storage the operands occupy and (b) to
; prevent fasling of useless operands in cte-optimization-locs. clear even
; if we didn't set (i.e., even if opnds or the corresponding opnd is #f), for
; the benefit of cp0-rec-let, which sets operand fields after creating env
(for-each (lambda (id) (prelex-operand-set! id #f)) ids)))
(define lookup
(lambda (id env)
(let loop1 ([env env])
(if (eqv? env empty-env)
id
(let ([old-rib (env-old-ids env)] [new-rib (env-new-ids env)])
(let ([n (vector-length old-rib)])
(let loop2 ([i 0])
(if (fx= i n)
(loop1 (env-next env))
(if (eq? (vector-ref old-rib i) id)
(vector-ref new-rib i)
(let ([i (fx+ i 1)])
(if (fx= i n)
(loop1 (env-next env))
(if (eq? (vector-ref old-rib i) id)
(vector-ref new-rib i)
(loop2 (fx+ i 1)))))))))))))))
(define cp0-make-temp ; returns an unassigned temporary
(lambda (multiply-referenced?)
(let ([t (make-prelex*)])
(when multiply-referenced? (set-prelex-multiply-referenced! t #t))
(set-prelex-referenced! t #t)
t)))
;;; contexts
;; app context:
;; opnds are the operands at the call site
;; ctxt is the outer context
;; convention is a symbol: call, apply2 (safe), or apply3 (unsafe)
;; src is the call source
;; used is set to a list of operands used (let-bound) by integrated call
;; unused is set to a list of operands not used by integrated call
(define-record-type app
(fields opnds ctxt convention name preinfo (mutable used) (mutable unused))
(nongenerative)
(sealed #t)
(protocol
(lambda (new)
(lambda (opnds ctxt convention name preinfo)
(new opnds ctxt convention name preinfo #f #f)))))
(define-syntax context-case
(lambda (x)
(define predicate
(lambda (type)
(syntax-case type (app)
[app #'app?]
[_ (with-syntax ([type type])
#'(lambda (x) (eq? x 'type)))])))
(syntax-case x (else)
[(_ ctxt-exp [(type ...) e1 e2 ...] more ...)
(with-syntax (((pred ...) (map predicate #'(type ...))))
#'(let ((ctxt ctxt-exp))
(if (or (pred ctxt) ...)
(begin e1 e2 ...)
(context-case ctxt more ...))))]
[(_ ctxt-exp [else e1 e2 ...]) #'(begin e1 e2 ...)]
[(_ ctxt-exp)
#'($oops 'cp0-internal "unexpected context ~s" ctxt-exp)])))
(define-syntax convention-case
(lambda (x)
(syntax-case x (else)
[(_ conv-exp [(key ...) e1 e2 ...] more ...)
#'(let ((conv conv-exp))
(if (or (eq? conv 'key) ...)
(begin e1 e2 ...)
(convention-case conv more ...)))]
[(_ conv-exp [else e1 e2 ...]) #'(begin e1 e2 ...)]
[(_ conv-exp)
#'($oops 'cp0-internal "unexpected app convention ~s" conv-exp)])))
;;; operands
(define-record-type operand
(fields
(immutable exp)
(immutable env)
(immutable wd)
(immutable moi)
(mutable name)
(mutable score)
(mutable pending)
(mutable opending)
(mutable value)
(mutable singly-referenced-score)
(mutable lifted))
(nongenerative)
(protocol
(lambda (new)
(lambda (exp env wd moi)
(new exp env wd moi #f 0 0 0 #f #f #f)))))
(define-record-type lifted
(fields (immutable seq?) (immutable ids) (immutable vals))
(nongenerative)
(sealed #t))
(define build-operands
(lambda (args env wd moi)
(map (lambda (x) (make-operand x env wd moi)) args)))
(define build-cooked-opnd
(lambda (e)
(let ([o (make-operand #f #f #f #f)])
(operand-value-set! o e)
o)))
;;; cycle detection
(define inner-cyclic?
(lambda (opnd)
(when (fx> (operand-pending opnd) 0)
; seed outer pending flag if cycle is detected
(operand-opending-set! opnd 1))
(fx> (operand-pending opnd) inner-unroll-limit)))
(define outer-cyclic?
(lambda (opnd)
(fx> (operand-opending opnd) outer-unroll-limit)))
(define-threaded opending-list '())
(define unwind-pending!
(lambda (oplist)
(do ((ls opending-list (cdr ls)))
((eq? ls oplist) (set! opending-list ls))
(operand-opending-set! (car ls)
(fx- (operand-opending (car ls)) 1)))))
(define-syntax pending-protect
; we don't need to maintain list of inner pending operands to be
; unwound by bug-out, since we never abort a visit to an operand
; that we actually need. in other words, when we bug out of an
; inlining attempt, we abort the visiting of only operands created
; during the inlining attempt.
(syntax-rules ()
((_ opnd e1 e2 ...)
(let ((o opnd))
(operand-pending-set! o (fx+ (operand-pending o) 1))
(let ((t (begin e1 e2 ...)))
(operand-pending-set! o (fx- (operand-pending o) 1))
t)))))
(define-syntax opending-protect
; dynamic wind could be used but is much slower
(syntax-rules ()
((_ opnd e1 e2 ...)
(let ((o opnd))
(operand-opending-set! o (fx+ (operand-opending o) 1))
(set! opending-list (cons opnd opending-list))
(let ((t (begin e1 e2 ...)))
(set! opending-list (cdr opending-list))
(operand-opending-set! o (fx- (operand-opending o) 1))
t)))))
;;; scorers
(define-record-type scorer
(fields (mutable limit) (immutable ctxt) (immutable k) (immutable oplist))
(nongenerative)
(sealed #t)
(protocol
(lambda (new)
(lambda (limit ctxt k)
(new limit ctxt k opending-list)))))
(define new-scorer
; with no arguments, create a passive scorer with a high limit that
; (we assume) won't overflow; this allows us to keep a tally without
; ever bugging out. with two arguments n and k, create a scorer that
; will bug out to if bumped n times.
(case-lambda
[() (make-scorer (most-positive-fixnum) #f oops-k)]
[(n ctxt k) (make-scorer n ctxt k)]))
(define oops-k
(list (lambda (x)
($oops 'compiler-internal "bug out from passive scorer"))))
(define scorer-score
; assuming we'll ask for score only of passive scorers
(lambda (sc)
(- (most-positive-fixnum) (scorer-limit sc))))
(define passive-scorer?
(lambda (sc)
(eq? (scorer-k sc) oops-k)))
(define new-watchdog
(case-lambda
[() (make-scorer (most-positive-fixnum) #f oops-k)]
[(wd ctxt k)
; create a new watchdog only if the old one isn't alert
(if (passive-scorer? wd)
(make-scorer effort-limit ctxt k)
wd)]))
(define bump
(lambda (sc amount)
(let ((n (fx- (scorer-limit sc) amount)))
(scorer-limit-set! sc n)
(when (fx< n 0) (bug-out! sc)))))
(define bug-out!
(lambda (sc)
(reset-integrated! (scorer-ctxt sc))
(unwind-pending! (scorer-oplist sc))
((scorer-k sc) #f)))
(define reset-integrated!
(lambda (ctxt)
(app-used-set! ctxt #f)
(let ((ctxt (app-ctxt ctxt)))
(when (app? ctxt) (reset-integrated! ctxt)))))
;;; visiting operands
(define visit-operand!
(lambda (opnd ctxt)
; NB: commonize with np-recognize-let
(define extract-profile-forms
(lambda (e)
(define seqs-and-profiles?
(lambda (e)
(nanopass-case (Lsrc Expr) e
[(profile ,src) #t]
[(seq ,e1 ,e2) (and (seqs-and-profiles? e1) (seqs-and-profiles? e2))]
[else #f])))
(if (eq? ($compile-profile) 'source)
(let loop ([e e] [eprof #f])
(nanopass-case (Lsrc Expr) e
[(seq ,e1 ,e2)
(guard (seqs-and-profiles? e1))
(loop e2 (if eprof `(seq ,eprof ,e1) e1))]
[else (values e eprof)]))
(values e #f))))
; set up to assimilate nested let/letrec/letrec* bindings.
; lifting job is completed by cp0-call or letrec/letrec*
(define (split-value e)
(nanopass-case (Lsrc Expr) e
[(call ,preinfo0 (case-lambda ,preinfo1 (clause (,x* ...) ,interface ,body)) ,e* ...)
(guard (fx= interface (length e*)))
(cond
; when lifting all assimilated let bindings, require each RHS to be
; simple, since they are treated as letrec/letrec* bindings, which does
; not preserve let semantics wrt continuation grabs in RHS expressions.
; further, require each RHS to be pure unless the body is pure, since it's
; unsound to split apart two things that can observe a side effect or two
; allocation operations that can be separated by a continuation grab.
[(if (ivory? body) (andmap simple/profile? e*) (andmap ivory? e*))
; associate each lhs with cooked operand for corresponding rhs. make-record-constructor-descriptor,
; at least, counts on this to allow protocols to be inlined.
(for-each (lambda (x e) (prelex-operand-set! x (build-cooked-opnd e)) (operand-name-set! opnd (prelex-name x))) x* e*)
(values (make-lifted #f x* e*) body)]
; okay, so we don't pass that test. if body and e* are simple, we can
; still lift by making a binding for body and requesting letrec* semantics.
; that way, we aren't splitting e* and body. we still can't lift anything
; that might capture a continuation, though it's tricky to come up with
; example that breaks.
; we don't presently have any justification (benchmark results or expand/optimize
; mats) that establish the benefit of this, but might want to revisit/refine at
; some point.
#;[(and (simple? body) (andmap simple? e*))
(let ([t (cp0-make-temp #f)]) ; mark was-referenced?
(let ([x* (append x* (list t))] [e* (append e* (list body))])
(for-each (lambda (x e) (prelex-operand-set! x (build-cooked-opnd e)) (operand-name-set! opnd (prelex-name x))) x* e*)
(values (make-lifted #t x* e*) (build-ref t))))]
; otherwise lift out only bindings with unasigned lhs and ivory rhs
; we don't presently have any justification (benchmark results or expand/optimize
; mats) that establish the benefit of this, but might want to revisit/refine at
; some point.
#;[(ormap (lambda (x e) (and (not (prelex-assigned x)) (ivory? e))) x* e*)
(let loop ([x* x*] [e* e*] [rx* '()] [re* '()] [rlx* '()] [rle* '()])
(if (null? x*)
(values (make-lifted #f (reverse rlx*) (reverse rle*))
(build-let (reverse rx*) (reverse re*) body))
(let ([x (car x*)] [e (car e*)])
(if (and (not (prelex-assigned x)) (ivory? e))
(begin
; associate each lhs with cooked operand for corresponding rhs. see note above.
(prelex-operand-set! x (build-cooked-opnd e))
(operand-name-set! opnd (prelex-name x))
(loop (cdr x*) (cdr e*) rx* re* (cons x rlx*) (cons e rle*)))
(loop (cdr x*) (cdr e*) (cons x rx*) (cons e re*) rlx* rle*)))))]
[else (values #f e)])]
; for assimilated letrec/letrec* bindings, require each RHS to be
; pure OR body to be pure, since we can't separate non-pure
; RHS and body expressions
[(letrec ([,x* ,e*] ...) ,body)
(guard (or (ivory? body) (andmap ivory? e*)))
; associate each lhs with cooked operand for corresponding rhs. see note above.
(for-each (lambda (x e) (prelex-operand-set! x (build-cooked-opnd e)) (operand-name-set! opnd (prelex-name x))) x* e*)
(values (make-lifted #f x* e*) body)]
; force the issue by creating an extra tmp for body
; we don't presently have any justification (benchmark results or expand/optimize
; mats) that establish the benefit of this, but might want to revisit/refine at
; some point.
#;[(letrec ([,x* ,e*] ...) ,body)
(let ([x (cp0-make-temp #f)])
(let ([x* (append x* (list x))] [e* (append e* (list body))])
(for-each (lambda (x e) (prelex-operand-set! x (build-cooked-opnd e)) (operand-name-set! opnd (prelex-name x))) x* e*)
(values (make-lifted #t x* e*) (build-ref x))))]
[(letrec* ([,x* ,e*] ...) ,body)
(guard (or (ivory? body) (andmap ivory? e*)))
; associate each lhs with cooked operand for corresponding rhs. see note above.
(for-each (lambda (x e) (prelex-operand-set! x (build-cooked-opnd e)) (operand-name-set! opnd (prelex-name x))) x* e*)
(values (make-lifted #t x* e*) body)]
; force the issue by creating an extra tmp for body.
; we don't presently have any justification (benchmark results or expand/optimize
; mats) that establish the benefit of this, but might want to revisit/refine at
; some point.
#;[(letrec* ([,x* ,e*] ...) ,body)
(let ([x (cp0-make-temp #f)])
(let ([x* (append x* (list x))] [e* (append e* (list body))])
(for-each (lambda (x e) (prelex-operand-set! x (build-cooked-opnd e)) (operand-name-set! opnd (prelex-name x))) x* e*)
(values (make-lifted #t x* e*) (build-ref x))))]
; we can lift arbitrary subforms of record forms if we also lift
; a binding for the record form itself. there's no worry about
; continuation captures: if rtd-expr or e* capture a continuation,
; invoking the continuation to return from a rhs is no worse than
; invoking the continuation to build the record and then return
; from a rhs.
[(record ,rtd ,rtd-expr ,e* ...)
(let-values ([(liftmt* liftme* e*)
(let ([fld* (rtd-flds rtd)])
(let f ([e* e*] [fld* fld*])
(if (null? e*)
(values '() '() '())
(let ([e (car e*)])
(let-values ([(liftmt* liftme* e*) (f (cdr e*) (cdr fld*))])
(if (nanopass-case (Lsrc Expr) e
[(ref ,maybe-src ,x) #f]
[(quote ,d) #f]
[,pr #f]
[else (not (fld-mutable? (car fld*)))])
(let ([t (cp0-make-temp #f)])
(values (cons t liftmt*) (cons e liftme*) (cons (build-ref t) e*)))
(values liftmt* liftme* (cons e e*))))))))])
(let ([e `(record ,rtd ,rtd-expr ,e* ...)])
(if (null? liftmt*)
(values #f e)
(let ([x (cp0-make-temp #f)])
(let ([x* (append liftmt* (list x))] [e* (append liftme* (list e))])
(for-each (lambda (x e) (prelex-operand-set! x (build-cooked-opnd e)) (operand-name-set! opnd (prelex-name x))) x* e*)
(values (make-lifted #t x* e*) (build-ref x)))))))]
[else (values #f e)]))
(or (operand-value opnd)
(let ([sc (new-scorer)])
(let ([e0 (pending-protect opnd
(cp0 (operand-exp opnd) ctxt (operand-env opnd) sc (operand-wd opnd) (operand-name opnd) (operand-moi opnd)))])
(let-values ([(e1 eprof) (extract-profile-forms e0)])
(with-values (split-value e1)
(lambda (lifted e)
(let ([e (if eprof (make-seq ctxt eprof e) e)])
(operand-lifted-set! opnd lifted)
(operand-value-set! opnd e)
(operand-score-set! opnd (scorer-score sc))
e)))))))))
(define value-visit-operand!
(lambda (opnd)
(visit-operand! opnd 'value)))
(define test-visit-operand!
(lambda (opnd)
(visit-operand! opnd 'test)))
(define value-visit-operands!
(lambda (opnds)
(map value-visit-operand! opnds)))
(define residualize-seq
; ctxt must be an app context. set used and unused lists in context
(lambda (used unused ctxt)
(safe-assert (fx= (fx+ (length used) (length unused)) (length (app-opnds ctxt))))
(app-used-set! ctxt used)
(app-unused-set! ctxt unused)))
(define residualize-call-opnds
(lambda (used unused e ctxt sc)
(let f ((used used) (n 0))
(if (null? used)
(let f ((unused unused) (n n) (todo '()))
(if (null? unused)
(begin
(bump sc n)
(let f ((todo todo) (e e))
(if (null? todo)
e
(f (cdr todo)
(make-seq ctxt
(let ((opnd (car todo)))
(cp0 (operand-exp opnd) 'effect (operand-env opnd)
sc (operand-wd opnd) (operand-name opnd) (operand-moi opnd)))
e)))))
(let ((opnd (car unused)))
(let ((e (operand-value opnd)))
(if e
(if (simple? e)
(if (operand-singly-referenced-score opnd)
; singly-referenced integration attempt in copy2 succeeded
(f (cdr unused) (fx+ (operand-singly-referenced-score opnd) n) todo)
(f (cdr unused) n todo))
; overscoring bug: make-seq may drop e2 if e is (seq e1 e2), but
; we add in the entire score here
; if singly-referenced integration attempt in copy2 succeeded, but
; value isn't simple, we also pay the whole price
(make-seq ctxt e (f (cdr unused) (fx+ n (operand-score opnd)) todo)))
(if (operand-singly-referenced-score opnd)
; singly-referenced integration attempt in ref-case of cp0 succeeded
(f (cdr unused) (fx+ (operand-singly-referenced-score opnd) n) todo)
(f (cdr unused) n (cons opnd todo))))))))
(f (cdr used) (fx+ (operand-score (car used)) n))))))
(define cp0-constant?
(case-lambda
[(x)
(nanopass-case (Lsrc Expr) x
[(quote ,d) #t]
[else #f])]
[(pred? x)
(nanopass-case (Lsrc Expr) x
[(quote ,d) (pred? d)]
[else #f])]))
(define-who cp0-datum
(lambda (x)
(nanopass-case (Lsrc Expr) x
[(quote ,d) d]
[else (sorry! who "~s is not a constant" x)])))
(define preinfo-call->preinfo-lambda
(lambda (preinfo)
(make-preinfo-lambda (preinfo-src preinfo) (preinfo-sexpr preinfo))))
(define build-quote
(lambda (d)
`(quote ,d)))
(define build-ref
(lambda (x)
`(ref #f ,x)))
(module (build-primcall)
(define $build-primcall
(case-lambda
[(primref args) ($build-primcall (make-preinfo) primref args)]
[(preinfo primref args) `(call ,preinfo ,primref ,args ...)]))
(define-syntax build-primcall
(syntax-rules ()
[(_ level name args) ($build-primcall (lookup-primref level name) args)]
[(_ preinfo level name args) ($build-primcall preinfo (lookup-primref level name) args)])))
(define build-lambda
(case-lambda
[(ids body) (build-lambda (make-preinfo-lambda) ids body)]
[(preinfo ids body) `(case-lambda ,preinfo (clause (,ids ...) ,(length ids) ,body))]))
(define build-case-lambda
(case-lambda
[(clause*) (build-case-lambda (make-preinfo-lambda) clause*)]
[(preinfo clause*)
`(case-lambda ,preinfo
,(map (lambda (clause)
(with-output-language (Lsrc CaseLambdaClause)
(let ([x* (car clause)])
`(clause (,x* ...) ,(length x*) ,(cadr clause)))))
clause*) ...)]))
; build-call is not very cp0-like, since it doesn't enable further
; optimization, but it does clean up some silly looking code.
(define build-call
(lambda (preinfo proc args)
(let ([n (length args)])
(nanopass-case (Lsrc Expr) proc
; eta reduce ((lambda (x ...) (prim x ...)) e ...) => (prim e ...)
[(case-lambda ,preinfo0
(clause (,x* ...) ,interface
(call ,preinfo1 ,pr ,e* ...)))
(guard (fx= interface n) (fx= (length e*) n)
(andmap (lambda (x e)
(nanopass-case (Lsrc Expr) e
[(ref ,maybe-src ,x1) (eq? x1 x)]
[else #f]))
x* e*))
`(call ,preinfo1 ,pr ,args ...)]
[else `(call ,preinfo ,proc ,args ...)]))))
(define build-let
(case-lambda
[(lambda-preinfo ids exps body)
(build-call (make-preinfo) (build-lambda lambda-preinfo ids body) exps)]
[(ids exps body) (build-call (make-preinfo) (build-lambda ids body) exps)]))
(define build-named-let
(lambda (name ids exps body)
`(call ,(make-preinfo)
(letrec ([,name ,(build-lambda ids body)])
(ref #f ,name))
,exps ...)))
(define make-seq
; ensures that the right subtree of the output seq is not a seq if the
; second argument is similarly constrained, to facilitate result-exp
(lambda (ctxt e1 e2)
(if (simple? e1)
e2
(if (and (eq? ctxt 'effect) (simple? e2))
e1
(let ([e1 (nanopass-case (Lsrc Expr) e1
[(seq ,e11 ,e12)
(guard (simple? e12))
e11]
[else e1])])
(nanopass-case (Lsrc Expr) e2
[(seq ,e21 ,e22) `(seq (seq ,e1 ,e21) ,e22)]
[else `(seq ,e1 ,e2)]))))))
(define make-seq* ; requires at least one operand
(lambda (ctxt e*)
(if (null? (cdr e*))
(car e*)
(make-seq ctxt (car e*) (make-seq* ctxt (cdr e*))))))
(define make-if
(lambda (ctxt sc e1 e2 e3)
(cond
[(record-equal? e2 e3 ctxt) (make-seq ctxt e1 e2)]
[(and (cp0-constant? (lambda (x) (eq? x #f)) e3)
(record-equal? e1 e2 (if (eq? ctxt 'test) 'test 'value))
(simple? e1))
e1]
[(nanopass-case (Lsrc Expr) (result-exp e1)
[(if ,e11 ,[result-exp : e12 -> re12] ,[result-exp : e13 -> re13])
(if (and (cp0-constant? re12) (cp0-constant? re13))
(let ([d12 (cp0-datum re12)] [d13 (cp0-datum re13)])
(non-result-exp e1
(cond
[(and d12 d13) (make-seq ctxt (make-if 'effect sc e11 e12 e13) e2)]
[(not (or d12 d13)) (make-seq ctxt (make-if 'effect sc e11 e12 e13) e3)]
[else (let-values ([(e2 e3) (if d12 (values e2 e3) (values e3 e2))])
(make-if ctxt sc e11 (non-result-exp e12 e2) (non-result-exp e13 e3)))])))
#f)]
[else #f])]
[else
(bump sc 1)
`(if ,e1 ,e2 ,e3)])))
(define result-exp
(lambda (e)
(nanopass-case (Lsrc Expr) e
[(seq ,e1 ,e2) e2]
[else e])))
(define result-exp/indirect-ref
; useful only when interested in non-propagatable result expressions, e.g., lambda expressions
; NB: to avoid code duplication, don't residualize the resulting value
(lambda (x)
(let ([x (result-exp x)])
(or (nanopass-case (Lsrc Expr) x
[(ref ,maybe-src ,x)
(and (not (prelex-was-assigned x))
(let ([opnd (prelex-operand x)])
(and opnd
(let ([x (operand-value opnd)])
(and x (result-exp x))))))]
[else #f])
x))))
(define non-result-exp
(lambda (e body)
(nanopass-case (Lsrc Expr) e
[(seq ,e1 ,e2) `(seq ,e1 ,body)]
[else body])))
(define (arity-okay? arity n)
(or (not arity) ; presumably system routine w/no recorded arity
(ormap
(lambda (a)
(or (fx= n a)
(and (fx< a 0) (fx>= n (fx- -1 a)))))
arity)))
(define okay-to-copy?
(lambda (obj)
; okay to copy obj if (eq? (faslin (faslout x)) x) => #t or (in the case of numbers and characters)
; the value of (eq? x x) is unspecified
(or (symbol? obj)
(number? obj)
(char? obj)
(boolean? obj)
(null? 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)
(record-type-descriptor? obj))))
(define externally-inlinable?
(lambda (clause)
(call/cc
(lambda (exit)
(define bump!
(let ([size 0])
(lambda ()
(set! size (fx+ size 1))
(when (fx> size score-limit) (exit #f)))))
(define (ids->do-clause ids)
(rec do-clause
(lambda (clause)
(define (ids->do-expr ids)
(rec do-expr
(lambda (e)
(nanopass-case (Lsrc Expr) e
[(quote ,d) (if (okay-to-copy? d) (bump!) (exit #f))]
[(moi) (bump!)]
[,pr (bump!)]
[(ref ,maybe-src ,x) (unless (memq x ids) (exit #f)) (bump!)]
[(seq ,[do-expr : e1] ,[do-expr : e2]) (void)]
[(if ,[do-expr : e1] ,[do-expr : e2] ,[do-expr : e3]) (void)]
[(set! ,maybe-src ,x ,e)
(unless (memq x ids) (exit #f))
(bump!)
(do-expr e)]
[(call ,preinfo ,e ,e* ...)
; reject calls to gensyms, since they might represent library exports,
; and we have no way to set up the required invoke dependencies
(when (and (nanopass-case (Lsrc Expr) e
[,pr (eq? (primref-name pr) '$top-level-value)]
[else #f])
(= (length e*) 1)
(cp0-constant? gensym? (car e*)))
(exit #f))
(bump!)
(do-expr e)
(for-each do-expr e*)]
[(case-lambda ,preinfo ,cl* ...)
(bump!)
(for-each (ids->do-clause ids) cl*)]
[(letrec ([,x* ,e*] ...) ,body)
(bump!)
(safe-assert (andmap (lambda (x) (not (prelex-operand x))) x*))
(let ([do-expr (ids->do-expr (append x* ids))])
(for-each do-expr e*)
(do-expr body))]
[(letrec* ([,x* ,e*] ...) ,body)
(bump!)
(safe-assert (andmap (lambda (x) (not (prelex-operand x))) x*))
(let ([do-expr (ids->do-expr (append x* ids))])
(for-each do-expr e*)
(do-expr body))]
[(record-type ,rtd ,[do-expr : e]) (void)]
[(record-cd ,rcd ,rtd-expr ,[do-expr : e]) (void)]
[(record-ref ,rtd ,type ,index ,[do-expr : e]) (bump!)]
[(record-set! ,rtd ,type ,index ,[do-expr : e1] ,[do-expr : e2]) (bump!)]
[(record ,rtd ,[do-expr : rtd-expr] ,[do-expr : e*] ...) (bump!)]
[(immutable-list (,[e*] ...) ,[e]) (void)]
[(pariah) (void)]
[(profile ,src) (void)]
[else (exit #f)]))))
(nanopass-case (Lsrc CaseLambdaClause) clause
[(clause (,x* ...) ,interface ,body)
(safe-assert (andmap (lambda (x) (not (prelex-operand x))) x*))
((ids->do-expr (append x* ids)) body)]))))
((ids->do-clause '()) clause)
#t))))
(module (pure? ivory? simple? simple/profile? boolean-valued?)
(define-syntax make-$memoize
(syntax-rules ()
[(_ flag-known flag)
(lambda (e pred?)
(let ([a (eq-hashtable-cell cp0-info-hashtable e 0)])
(let ([flags (cdr a)])
(if (all-set? (cp0-info-mask flag-known) flags)
(all-set? (cp0-info-mask flag) flags)
(let ([bool (pred?)])
(set-cdr! a (set-flags (if bool (cp0-info-mask flag-known flag) (cp0-info-mask flag-known)) flags))
bool)))))]))
(define-syntax with-memoize
(lambda (x)
(syntax-case x ()
[(k (flag-known flag) ?e e* ...)
(with-implicit (k memoize)
#'(let ([$memoize (make-$memoize flag-known flag)] [e ?e])
(define-syntax memoize
(syntax-rules ()
[(_ e1 e2 (... ...)) ($memoize e (lambda () e1 e2 (... ...)))]))
e* ...))])))
(define-who pure?
; does not cause or observe any effects, capture or invoke a continuation,
; or allocate mutable data structures. might contain profile forms, so
; pure forms cannot necessarily be discarded. mostly used to determine if
; we can move an expression. differs from ivory in that restricted primitives
; and record refs are not considered pur at optimize-level 3, which allows
; pure expressions to be moved in more circumstances.
(lambda (e)
(with-memoize (pure-known pure) e
; 2015/02/11 sorted by frequency
(nanopass-case (Lsrc Expr) e
[(ref ,maybe-src ,x) (not (prelex-was-assigned x))]
[(call ,preinfo ,e ,e* ...)
(let ()
(define pure-call?
(lambda (maybe-e e)
(nanopass-case (Lsrc Expr) e
[,pr
(and (let ([flags (primref-flags e)])
(all-set? (prim-mask (or pure unrestricted)) flags))
(arity-okay? (primref-arity e) (length e*))
(memoize (and (or (not maybe-e) (pure? maybe-e)) (andmap pure? e*))))]
[(case-lambda ,preinfo1 (clause (,x* ...) ,interface ,body))
(guard (fx= interface (length e*)))
(memoize (and (or (not maybe-e) (pure? maybe-e)) (pure? body) (andmap pure? e*)))]
[else #f])))
(nanopass-case (Lsrc Expr) e
[(seq ,e1 ,e2) (pure-call? e1 e2)]
[else (pure-call? #f e)]))]
[(quote ,d) #t]
[,pr (all-set? (prim-mask proc) (primref-flags pr))]
[(case-lambda ,preinfo ,cl* ...) #t]
[(if ,e1 ,e2 ,e3) (memoize (and (pure? e1) (pure? e2) (pure? e3)))]
[(seq ,e1 ,e2) (memoize (and (pure? e1) (pure? e2)))]
[(record-ref ,rtd ,type ,index ,e) #f]
[(record-set! ,rtd ,type ,index ,e1 ,e2) #f]
[(record ,rtd ,rtd-expr ,e* ...)
(and (andmap (lambda (fld)
(and (not (fld-mutable? fld))
(eq? (filter-foreign-type (fld-type fld)) 'scheme-object)))
(rtd-flds rtd))
(memoize (and (pure? rtd-expr) (andmap pure? e*))))]
[(set! ,maybe-src ,x ,e) #f]
[(record-cd ,rcd ,rtd-expr ,e) (memoize (pure? e))]
[(letrec ([,x* ,e*] ...) ,body) (memoize (and (andmap pure? e*) (pure? body)))]
[(record-type ,rtd ,e) (memoize (pure? e))]
[(foreign (,conv* ...) ,name ,e (,arg-type* ...) ,result-type) (memoize (pure? e))]
[(letrec* ([,x* ,e*] ...) ,body) (memoize (and (andmap pure? e*) (pure? body)))]
[(immutable-list (,e* ...) ,e) (memoize (and (andmap pure? e*) (pure? e)))]
[(profile ,src) #t]
[(cte-optimization-loc ,box ,e) (memoize (pure? e))]
[(moi) #t]
[(fcallable (,conv* ...) ,e (,arg-type* ...) ,result-type) (memoize (pure? e))]
[(pariah) #t]
[else ($oops who "unrecognized record ~s" e)]))))
(define-who ivory? ; 99.44% pure
; does not cause or observe any effects, capture or invoke a continuation,
; or allocate mutable data structures. might contain profile forms, so
; ivory forms cannot necessarily be discarded. mostly used to determine if
; we can move an expression. differs from pure in that restricted primitives
; and record refs are considered ivory at optimize-level 3.
(lambda (e)
(with-memoize (ivory-known ivory) e
; 2015/02/11 sorted by frequency
(nanopass-case (Lsrc Expr) e
[(ref ,maybe-src ,x) (not (prelex-was-assigned x))]
[(call ,preinfo ,e ,e* ...)
(let ()
(define ivory-call?
(lambda (maybe-e e)
(nanopass-case (Lsrc Expr) e
[,pr
(and (let ([flags (primref-flags e)])
; here ivory? differs from pure?
(if (all-set? (prim-mask unsafe) flags)
(all-set? (prim-mask pure) flags)
(all-set? (prim-mask (or pure unrestricted)) flags)))
(arity-okay? (primref-arity e) (length e*))
(memoize (and (or (not maybe-e) (ivory? maybe-e)) (andmap ivory? e*))))]
[(case-lambda ,preinfo1 (clause (,x* ...) ,interface ,body))
(guard (fx= interface (length e*)))
(memoize (and (or (not maybe-e) (ivory? maybe-e)) (ivory? body) (andmap ivory? e*)))]
[else #f])))
(nanopass-case (Lsrc Expr) e
[(seq ,e1 ,e2) (ivory-call? e1 e2)]
[else (ivory-call? #f e)]))]
[(quote ,d) #t]
[,pr (all-set? (prim-mask proc) (primref-flags pr))]
[(case-lambda ,preinfo ,cl* ...) #t]
[(if ,e1 ,e2 ,e3) (memoize (and (ivory? e1) (ivory? e2) (ivory? e3)))]
[(seq ,e1 ,e2) (memoize (and (ivory? e1) (ivory? e2)))]
[(record-ref ,rtd ,type ,index ,e)
; here ivory? differs from pure?
(and (not (fld-mutable? (list-ref (rtd-flds rtd) index)))
(memoize (ivory? e)))]
[(record-set! ,rtd ,type ,index ,e1 ,e2) #f]
[(record ,rtd ,rtd-expr ,e* ...)
; here ivory? differs from pure?
(and (andmap (lambda (fld) (not (fld-mutable? fld))) (rtd-flds rtd))
(memoize (and (ivory? rtd-expr) (andmap ivory? e*))))]
[(set! ,maybe-src ,x ,e) #f]
[(record-cd ,rcd ,rtd-expr ,e) (memoize (ivory? e))]
[(letrec ([,x* ,e*] ...) ,body) (memoize (and (andmap ivory? e*) (ivory? body)))]
[(record-type ,rtd ,e) (memoize (ivory? e))]
[(foreign (,conv* ...) ,name ,e (,arg-type* ...) ,result-type) (memoize (ivory? e))]
[(letrec* ([,x* ,e*] ...) ,body) (memoize (and (andmap ivory? e*) (ivory? body)))]
[(immutable-list (,e* ...) ,e) (memoize (and (andmap ivory? e*) (ivory? e)))]
[(profile ,src) #t]
[(cte-optimization-loc ,box ,e) (memoize (ivory? e))]
[(moi) #t]
[(fcallable (,conv* ...) ,e (,arg-type* ...) ,result-type) (memoize (ivory? e))]
[(pariah) #t]
[else ($oops who "unrecognized record ~s" e)]))))
(define-who simple?
(lambda (e)
(with-memoize (simple-known simple) e
; does not cause any effects or capture or invoke a continuation, and does not
; contain profile forms, but might observe effects or allocate mutable data
; structures. ; mostly used to determine if we can discard an expression.
; 2015/02/11 sorted by frequency
(nanopass-case (Lsrc Expr) e
; might be nice to have an ignorem style syntax for the nanopass-case (and passes)
[(quote ,d) #t]
[(call ,preinfo ,e ,e* ...)
(nanopass-case (Lsrc Expr) e
[,pr (let ([flags (primref-flags pr)])
(and (if (all-set? (prim-mask unsafe) flags)
(all-set? (prim-mask discard) flags)
(all-set? (prim-mask (or discard unrestricted)) flags))
(arity-okay? (primref-arity pr) (length e*))
(memoize (andmap simple? e*))))]
[(case-lambda ,preinfo1 (clause (,x* ...) ,interface ,body))
(guard (fx= interface (length e*)))
(memoize (and (simple? body) (andmap simple? e*)))]
[else #f])]
[(ref ,maybe-src ,x) #t]
[(case-lambda ,preinfo ,cl* ...) #t]
[(if ,e1 ,e2 ,e3) (memoize (and (simple? e1) (simple? e2) (simple? e3)))]
[(seq ,e1 ,e2) (memoize (and (simple? e1) (simple? e2)))]
[(set! ,maybe-src ,x ,e) #f]
[(immutable-list (,e* ...) ,e) (memoize (and (andmap simple? e*) (simple? e)))]
[(letrec ([,x* ,e*] ...) ,body) (memoize (and (andmap simple? e*) (simple? body)))]
[(letrec* ([,x* ,e*] ...) ,body) (memoize (and (andmap simple? e*) (simple? body)))]
[,pr #t]
[(record-cd ,rcd ,rtd-expr ,e) (memoize (simple? e))]
[(record-ref ,rtd ,type ,index ,e) (memoize (simple? e))]
[(record-set! ,rtd ,type ,index ,e1 ,e2) #f]
[(foreign (,conv* ...) ,name ,e (,arg-type* ...) ,result-type) (memoize (simple? e))]
[(record-type ,rtd ,e) (memoize (simple? e))]
[(record ,rtd ,rtd-expr ,e* ...) (memoize (and (simple? rtd-expr) (andmap simple? e*)))]
[(pariah) #f]
[(profile ,src) #f]
[(cte-optimization-loc ,box ,e) (memoize (simple? e))]
[(moi) #t]
[(fcallable (,conv* ...) ,e (,arg-type* ...) ,result-type) (memoize (simple? e))]
[else ($oops who "unrecognized record ~s" e)]))))
(define-who simple/profile?
; like simple? but allows profile forms. used for lifting bindings.
(lambda (e)
(with-memoize (simple-known simple) e
; does not cause any effects or capture or invoke a continuation, and does not
; contain profile forms, but might observe effects or allocate mutable data
; structures. ; mostly used to determine if we can discard an expression.
; 2015/02/11 sorted by frequency
(nanopass-case (Lsrc Expr) e
; might be nice to have an ignorem style syntax for the nanopass-case (and passes)
[(quote ,d) #t]
[(call ,preinfo ,e ,e* ...)
(nanopass-case (Lsrc Expr) e
[,pr (let ([flags (primref-flags pr)])
(and (if (all-set? (prim-mask unsafe) flags)
(all-set? (prim-mask discard) flags)
(all-set? (prim-mask (or discard unrestricted)) flags))
(arity-okay? (primref-arity pr) (length e*))
(memoize (andmap simple/profile? e*))))]
[(case-lambda ,preinfo1 (clause (,x* ...) ,interface ,body))
(guard (fx= interface (length e*)))
(memoize (and (simple/profile? body) (andmap simple/profile? e*)))]
[else #f])]
[(ref ,maybe-src ,x) #t]
[(case-lambda ,preinfo ,cl* ...) #t]
[(if ,e1 ,e2 ,e3) (memoize (and (simple/profile? e1) (simple/profile? e2) (simple/profile? e3)))]
[(seq ,e1 ,e2) (memoize (and (simple/profile? e1) (simple/profile? e2)))]
[(set! ,maybe-src ,x ,e) #f]
[(immutable-list (,e* ...) ,e) (memoize (and (andmap simple/profile? e*) (simple/profile? e)))]
[(letrec ([,x* ,e*] ...) ,body) (memoize (and (andmap simple/profile? e*) (simple/profile? body)))]
[(letrec* ([,x* ,e*] ...) ,body) (memoize (and (andmap simple/profile? e*) (simple/profile? body)))]
[,pr #t]
[(record-cd ,rcd ,rtd-expr ,e) (memoize (simple/profile? e))]
[(record-ref ,rtd ,type ,index ,e) (memoize (simple/profile? e))]
[(record-set! ,rtd ,type ,index ,e1 ,e2) #f]
[(foreign (,conv* ...) ,name ,e (,arg-type* ...) ,result-type) (memoize (simple/profile? e))]
[(record-type ,rtd ,e) (memoize (simple/profile? e))]
[(record ,rtd ,rtd-expr ,e* ...) (memoize (and (simple/profile? rtd-expr) (andmap simple/profile? e*)))]
[(pariah) #t]
[(profile ,src) #t]
[(cte-optimization-loc ,box ,e) (memoize (simple/profile? e))]
[(moi) #t]
[(fcallable (,conv* ...) ,e (,arg-type* ...) ,result-type) (memoize (simple/profile? e))]
[else ($oops who "unrecognized record ~s" e)]))))
(define-who boolean-valued?
(lambda (e)
(with-memoize (boolean-valued-known boolean-valued) e
; 2015/02/11 sorted by frequency
(nanopass-case (Lsrc Expr) e
[(call ,preinfo ,e ,e* ...)
(nanopass-case (Lsrc Expr) (result-exp e)
[,pr (all-set? (prim-mask boolean-valued) (primref-flags pr))]
[(case-lambda ,preinfo1 (clause (,x* ...) ,interface ,body))
(guard (fx= interface (length e*)))
(memoize (boolean-valued? body))]
[else #f])]
[(if ,e0 ,e1 ,e2) (memoize (and (boolean-valued? e1) (boolean-valued? e2)))]
[(record-ref ,rtd ,type ,index ,e) (eq? type 'boolean)]
[(ref ,maybe-src ,x) #f]
[(quote ,d) (boolean? d)]
[(seq ,e1 ,e2) (memoize (boolean-valued? e2))]
[(case-lambda ,preinfo ,cl* ...) #f]
[(letrec* ([,x* ,e*] ...) ,body) (memoize (boolean-valued? body))]
[(letrec ([,x* ,e*] ...) ,body) (memoize (boolean-valued? body))]
[,pr #f]
[(record-type ,rtd ,e) #f]
[(record-cd ,rcd ,rtd-expr ,e) #f]
[(record-set! ,rtd ,type ,index ,e1 ,e2) #f]
[(record ,rtd ,rtd-expr ,e* ...) #f]
[(immutable-list (,e* ...) ,e) #f]
[(cte-optimization-loc ,box ,e) (memoize (boolean-valued? e))]
[(profile ,src) #f]
[(set! ,maybe-src ,x ,e) #f]
[(moi) #f]
[(foreign (,conv* ...) ,name ,e (,arg-type* ...) ,result-type) #f]
[(fcallable (,conv* ...) ,e (,arg-type* ...) ,result-type) #f]
[(pariah) #f]
[else ($oops who "unrecognized record ~s" e)])))))
(define find-call-lambda-clause
(lambda (exp opnds)
(define rest-clause
; ((lambda (x1 ... xn . xr) e) a1 ... am) m >= n
; => ((lambda (x1 ... xn tn+1 ... tm)
; (let ((xr (list tn+1 ... tm)))
; e))
; a1 ... am)
(lambda (ids opnds body)
(with-values
(let split ((ids ids) (opnds opnds))
(if (null? (cdr ids))
(let ((temps (map (lambda (x) (cp0-make-temp #f)) opnds)))
(values temps temps (car ids)))
(with-values (split (cdr ids) (cdr opnds))
(lambda (new-ids temps rest-id)
(values (cons (car ids) new-ids) temps rest-id)))))
(lambda (ids temps rest-id)
(values ids
(build-let (list rest-id)
(list
(let* ([tref* (map build-ref temps)]
[e (build-primcall 3 'list tref*)])
; immutable-value presently set only by record-constructor
(if (prelex-immutable-value rest-id)
`(immutable-list (,tref* ...) ,e)
e)))
body))))))
(nanopass-case (Lsrc Expr) exp
[(case-lambda ,preinfo ,cl* ...)
(let ((n (length opnds)))
(let find-clause ([cl* cl*])
(if (null? cl*)
(values)
(nanopass-case (Lsrc CaseLambdaClause) (car cl*)
[(clause (,x* ...) ,interface ,body)
(cond
[(fx= interface n) (values x* body)]
[(and (fx< interface 0)
(fx>= n (fx- -1 interface)))
(rest-clause x* opnds body)]
[else (find-clause (cdr cl*))])]))))])))
(define find-apply-lambda-clause
(lambda (exp opnds)
(define apply-clause
; (apply (lambda (x1 ... xn) e) a1 ... am ar) m <= n
; => ((lambda (x1 ... xm t)
; (let ((xm+1 (car t)) (t (cdr t)))
; ...
; (let ((xn-1 (car t)) (t (cdr t)))
; (let ((xn (car t)))
; e))))
; a1 ... am ar)
; we insist on m <= n to simplify the code below. since this
; optimization is performed only when optimize-level is 3, we
; don't otherwise concern ourselves with argument-count checks
(lambda (ids opnds body)
(with-values
(let split ([ids ids] [opnds opnds])
(if (null? (cdr opnds))
(let ([t (cp0-make-temp #f)])
(values (list t) t ids))
(with-values (split (cdr ids) (cdr opnds))
(lambda (new-ids t more-ids)
(values (cons (car ids) new-ids) t more-ids)))))
(lambda (ids t more-ids)
(values ids
(if (null? more-ids)
body
(let f ([ids more-ids] [t t])
(let ([tref (list (build-ref t))])
(if (null? (cdr ids))
(build-let ids (list (build-primcall 3 'car tref)) body)
(begin
(set-prelex-multiply-referenced! t #t)
(let ([t (cp0-make-temp #f)])
(build-let (list (car ids) t)
(list
(build-primcall 3 'car tref)
(build-primcall 3 'cdr tref))
(f (cdr ids) t)))))))))))))
(nanopass-case (Lsrc Expr) exp
[(case-lambda ,preinfo ,cl* ...)
(let ([n (length opnds)])
(cond
[(fx= (length cl*) 1)
(nanopass-case (Lsrc CaseLambdaClause) (car cl*)
[(clause (,x* ...) ,interface ,body)
(if (fx>= interface (fx- (length opnds) 1))
(apply-clause x* opnds body)
(values))])]
[else (values)]))])))
(define find-lambda-clause
(lambda (exp ctxt)
(convention-case (app-convention ctxt)
[(call) (find-call-lambda-clause exp (app-opnds ctxt))]
[(apply2) (values)]
[(apply3) (find-apply-lambda-clause exp (app-opnds ctxt))])))
(define letify
(case-lambda
[(lambda-preinfo id* ctxt body) (letify lambda-preinfo id* ctxt '() body)]
[(lambda-preinfo id* ctxt used body)
(if (cp0-constant? body)
; don't allow conservative referenced flags prevent constant folding
(begin
(residualize-seq '() (app-opnds ctxt) ctxt)
body)
(with-values
(let loop ([id* id*] [opnd* (app-opnds ctxt)] [rid* '()] [rrhs* '()] [used used] [unused '()])
(if (null? id*)
(begin
(residualize-seq used unused ctxt)
(values (reverse rid*) (reverse rrhs*)))
(let ([id (car id*)] [opnd (car opnd*)])
(cond
[(prelex-referenced id)
(loop (cdr id*) (cdr opnd*) (cons id rid*) (cons (operand-value opnd) rrhs*) (cons opnd used) unused)]
[(prelex-assigned id)
(loop (cdr id*) (cdr opnd*) (cons id rid*) (cons void-rec rrhs*) used (cons opnd unused))]
[else (loop (cdr id*) (cdr opnd*) rid* rrhs* used (cons opnd unused))]))))
(lambda (id* rhs*)
(cond
[(null? id*) body]
[(and (= (length id*) 1)
(nanopass-case (Lsrc Expr) body
[(ref ,maybe-src ,x) (eq? x (car id*))]
[else #f]))
; (let ((x e)) x) => e
; x is clearly not assigned, even if flags are polluted and say it is
(car rhs*)]
; we drop the RHS of a let binding into the let body when the body expression is a call
; and we can do so without violating evaluation order of bindings wrt the let body:
; * for pure, singly referenced bindings, we drop them to the variable reference site
; * for impure, singly referenced bindings, we drop them only into the most deeply
; nested call of the let body to ensure the expression is fully evaluated before
; any body (sub-)expressions
; when we drop an impure let binding, we require the other bindings at the same level
; to be unassigned so the location creation for the other bindings remains in the
; continuation of the impure RHS.
;
; dropping let bindings enables pattern-based optimizations downstream that would
; otherwise be inhibited by the let binding. An example is the optimization in
; expand-primitives to eliminate unnecessary ftype-pointer creation for nested
; ftype-ref expressions. dropping let bindings can also reduce register pressure,
; though it can increase it as well.
;
; NB. nested let expressions can result in multiple traversals of the inner let bodies
; NB. via multiple calls to letify, causing O(n^2) behavior.
[(and (ormap (lambda (x) (not (prelex-multiply-referenced x))) id*)
(let ([all-unassigned? (not (ormap (lambda (x) (prelex-assigned x)) id*))])
(define drop-let
(lambda (e* build-body)
(let ([alist (map cons id* rhs*)])
(with-values (let f ([e* e*] [pure-left? #t])
(if (null? e*)
(values '() #t)
(let ([e (car e*)] [e* (cdr e*)])
(let ([pure-e? (pure? e)]) ; would cause O(n^2) behavior except pure? caches purity of calls
(let-values ([(e* pure-right?) (f e* (and pure-left? pure-e?))])
(values
(cons
(nanopass-case (Lsrc Expr) e
[(call ,preinfo ,e2 ,e2* ...)
(let ([e2* (cons e2 e2*)])
(let-values ([(new-e2* pure-e2*?) (f e2* (and pure-left? pure-right?))])
(if (andmap eq? new-e2* e2*)
e
(build-call preinfo (car new-e2*) (cdr new-e2*)))))]
[(record-ref ,rtd ,type ,index ,e2)
(let-values ([(new-e2* pure-e2*?) (f (list e2) (and pure-left? pure-right?))])
(safe-assert (= (length new-e2*) 1))
(let ([new-e2 (car new-e2*)])
(if (eq? new-e2 e2)
e
`(record-ref ,rtd ,type ,index ,new-e2))))]
[(record-set! ,rtd ,type ,index ,e21 ,e22)
(let-values ([(new-e2* pure-e2*?) (f (list e21 e22) (and pure-left? pure-right?))])
(safe-assert (= (length new-e2*) 2))
(let ([new-e21 (car new-e2*)] [new-e22 (cadr new-e2*)])
(if (and (eq? new-e21 e21) (eq? new-e22 e22))
e
`(record-set! ,rtd ,type ,index ,new-e21 ,new-e22))))]
[(record ,rtd ,rtd-expr ,e2* ...)
(let ([e2* (cons rtd-expr e2*)])
(let-values ([(new-e2* pure-e2*?) (f e2* (and pure-left? pure-right?))])
(if (andmap eq? new-e2* e2*)
e
`(record ,rtd ,(car new-e2*) ,(cdr new-e2*) ...))))]
[(record-type ,rtd ,e)
(let ([e* (list e)])
(let-values ([(new-e* pure-e*?) (f e* (and pure-left? pure-right?))])
(safe-assert (= (length new-e*) 1))
(if (andmap eq? new-e* e*)
e
`(record-type ,rtd ,(car new-e*)))))]
[(ref ,maybe-src ,x)
(guard (not (prelex-assigned x)) (not (prelex-multiply-referenced x)))
(let ([a (assq x alist)])
(if a
(let ([rhs (cdr a)])
(safe-assert rhs)
(if (or (and pure-left? pure-right? all-unassigned?) (pure? rhs))
(begin (set-cdr! a #f) rhs)
e))
e))]
[else e])
e*)
(and pure-e? pure-right?)))))))
(lambda (new-e* . ignore)
(let ([body (if (andmap eq? new-e* e*) body (build-body (car new-e*) (cdr new-e*)))])
(let ([alist (filter cdr alist)])
(if (null? alist) body (build-let lambda-preinfo (map car alist) (map cdr alist) body)))))))))
(nanopass-case (Lsrc Expr) body
[(call ,preinfo ,e ,e* ...)
(drop-let (cons e e*) (lambda (e e*) (build-call preinfo e e*)))]
[(record-ref ,rtd ,type ,index ,e)
(drop-let (list e) (lambda (e e*) (safe-assert (null? e*)) `(record-ref ,rtd ,type ,index ,e)))]
[(record-set! ,rtd ,type ,index ,e1 ,e2)
(drop-let (list e1 e2) (lambda (e e*) (safe-assert (= (length e*) 1)) `(record-set! ,rtd ,type ,index ,e ,(car e*))))]
[(record ,rtd ,rtd-expr ,e* ...)
(drop-let (cons rtd-expr e*) (lambda (rtd-expr e*) `(record ,rtd ,rtd-expr ,e* ...)))]
[(record-type ,rtd ,e)
(drop-let (list e) (lambda (e e*) (safe-assert (null? e*)) `(record-type ,rtd ,e)))]
[else #f])))]
[else (build-let lambda-preinfo id* rhs* body)]))))]))
(define cp0-let
(lambda (lambda-preinfo ids body ctxt env sc wd name moi)
(let ((opnds (app-opnds ctxt)))
(with-extended-env ((env ids) (env ids opnds))
(letify lambda-preinfo ids ctxt (cp0 body (app-ctxt ctxt) env sc wd (app-name ctxt) moi))))))
(define cp0-call
(lambda (preinfo e opnds ctxt env sc wd name moi)
(define (build-wrapper b* body)
(if (null? b*)
body
(let ([b (car b*)] [body (build-wrapper (cdr b*) body)])
(if (not b)
body
(let ([ids (lifted-ids b)] [vals (lifted-vals b)])
(for-each (lambda (id) (prelex-operand-set! id #f)) ids)
(if (lifted-seq? b)
`(letrec* ([,ids ,vals] ...) ,body)
`(letrec ([,ids ,vals] ...) ,body)))))))
(let* ((ctxt (make-app opnds ctxt 'call name preinfo))
(e (cp0 e ctxt env sc wd #f moi)))
(build-wrapper (map operand-lifted opnds)
(if (app-used ctxt)
(residualize-call-opnds (app-used ctxt) (app-unused ctxt) e (app-ctxt ctxt) sc)
(build-call preinfo e
(let f ((opnds opnds) (n 0))
(if (null? opnds)
(begin (bump sc n) '())
(let ((opnd (car opnds)))
(let ((e (operand-value opnd)))
(if e
(cons e (f (cdr opnds) (fx+ n (operand-score opnd))))
; do rest first to bump for previsited operands first so
; that we bug out quicker if bug out we do
(let ((rest (f (cdr opnds) n)))
(cons (cp0 (operand-exp opnd) 'value env sc wd #f moi) rest)))))))))))))
(define cp0-rec-let
(lambda (seq? ids vals body ctxt env sc wd name moi)
(with-extended-env ((env ids) (env ids #f))
(let ((opnds (build-operands vals env wd moi)))
; these operands will be cleared by with-extended-env
(for-each (lambda (id opnd)
(prelex-operand-set! id opnd)
(operand-name-set! opnd (prelex-name id)))
ids opnds)
; for r5rs letrec semantics: prevent copy propagation of references
; to lhs id if rhs might invoke call/cc
; not needed r6rs
#;(for-each
(lambda (id val)
(unless (simple? val)
(set-prelex-was-assigned! id #t)))
ids vals)
(let ((body (cp0 body ctxt env sc wd name moi)))
; visit operands as necessary
(let loop ([ids ids]
[opnds opnds]
[pending-ids '()]
[pending-opnds '()]
[change? #f])
(if (null? ids)
(when change? (loop pending-ids pending-opnds '() '() #f))
(let ([id (car ids)] [opnd (car opnds)])
(if (or (prelex-referenced id)
(not (simple? (operand-exp opnd))))
(begin
(value-visit-operand! opnd)
(loop (cdr ids) (cdr opnds) pending-ids pending-opnds
(not (null? pending-ids))))
(loop (cdr ids) (cdr opnds)
(cons id pending-ids)
(cons opnd pending-opnds)
change?)))))
(let loop ([old-ids ids] [opnds opnds] [ids '()] [vals '()] [n 0] [seq? seq?])
(if (null? old-ids)
(begin
(bump sc n)
(if (or (null? ids)
; don't allow conservative referenced flags prevent constant folding
(and (cp0-constant? body) (andmap simple? vals)))
body
(if seq?
`(letrec* ([,(reverse ids) ,(reverse vals)] ...) ,body)
`(letrec ([,ids ,vals] ...) ,body))))
(let ([id (car old-ids)] [opnd (car opnds)])
(cond
[(operand-value opnd) =>
(lambda (val)
; scoring bug: we don't count size of bindings when we
; drop the rest of the RHS
(define (f ids vals seq?)
(if (or (prelex-referenced id) (not (simple? val)))
(loop (cdr old-ids) (cdr opnds) (cons id ids)
(cons val vals) (+ n (operand-score opnd)) seq?)
(let ([n (+ (or (operand-singly-referenced-score opnd) 0) n)])
(if (prelex-assigned id)
(loop (cdr old-ids) (cdr opnds) (cons id ids)
(cons void-rec vals) n seq?)
(loop (cdr old-ids) (cdr opnds) ids vals n seq?)))))
(let ([b (operand-lifted opnd)])
(if (not b)
(f ids vals seq?)
(f (let ([lifted (lifted-ids b)])
(for-each (lambda (id) (prelex-operand-set! id #f)) lifted)
(rappend lifted ids))
(rappend (lifted-vals b) vals)
; must treat outer letrec as letrec* if assimilating
; letrec* bindings
(or seq? (lifted-seq? b))))))]
[(prelex-assigned id)
(loop (cdr old-ids) (cdr opnds) (cons id ids) (cons void-rec vals) n seq?)]
[else (loop (cdr old-ids) (cdr opnds) ids vals n seq?)])))))))))
(define residualize-ref
(lambda (maybe-src id sc)
(bump sc 1)
(when (prelex-referenced id)
(set-prelex-multiply-referenced! id #t))
(set-prelex-referenced! id #t)
`(ref ,maybe-src ,id)))
(define copy
; ctxt is value, test, or app
; opnd has already been visited
(lambda (maybe-src id opnd ctxt sc wd name moi)
(let ((rhs (result-exp (operand-value opnd))))
(nanopass-case (Lsrc Expr) rhs
[(quote ,d) rhs]
[(record-type ,rtd ,e)
`(record-type ,rtd
,(residualize-ref maybe-src
(nanopass-case (Lsrc Expr) e
[(ref ,maybe-src ,x)
(guard (not (prelex-was-assigned x))
; protect against (letrec ([x x]) ---)
(not (eq? x id)))
(when (prelex-was-multiply-referenced id)
(set-prelex-was-multiply-referenced! x #t))
x]
[else id])
sc))]
[(record-cd ,rcd ,rtd-expr ,e)
`(record-cd ,rcd ,rtd-expr
,(residualize-ref maybe-src
(nanopass-case (Lsrc Expr) e
[(ref ,maybe-src ,x)
(guard (not (prelex-was-assigned x))
; protect against (letrec ([x x]) ---)
(not (eq? x id)))
(when (prelex-was-multiply-referenced id)
(set-prelex-was-multiply-referenced! x #t))
x]
[else id])
sc))]
[(immutable-list (,e* ...) ,e)
`(immutable-list (,e* ...)
,(residualize-ref maybe-src
(nanopass-case (Lsrc Expr) e
[(ref ,maybe-src ,x)
(guard (not (prelex-was-assigned x))
; protect against (letrec ([x x]) ---)
(not (eq? x id)))
(when (prelex-was-multiply-referenced id)
(set-prelex-was-multiply-referenced! x #t))
x]
[else id])
sc))]
[(ref ,maybe-src1 ,x)
(cond
[(and (not (prelex-was-assigned x))
; protect against (letrec ([x x]) ---)
(not (eq? x id)))
(when (prelex-was-multiply-referenced id)
(set-prelex-was-multiply-referenced! x #t))
(let ([opnd (prelex-operand x)])
(if (and opnd (operand-value opnd))
(copy2 maybe-src x opnd ctxt sc wd name moi)
(residualize-ref maybe-src x sc)))]
[else (residualize-ref maybe-src id sc)])]
[else (copy2 maybe-src id opnd ctxt sc wd name moi)]))))
(define copy2
; ctxt is value, test, or app
(lambda (maybe-src id opnd ctxt sc wd name moi)
(let ([rhs (result-exp (operand-value opnd))])
(nanopass-case (Lsrc Expr) rhs
[(case-lambda ,preinfo1 ,cl* ...)
(context-case ctxt
[(test) true-rec]
[(app)
(with-values (find-lambda-clause rhs ctxt)
(case-lambda
[(ids body)
(let ([limit (if (passive-scorer? sc)
(fx+ score-limit (length (app-opnds ctxt)))
(scorer-limit sc))])
(if (outer-cyclic? opnd)
(or (and polyvariant
(fx= (operand-opending opnd) (fx+ outer-unroll-limit 1))
; Give it one (more) whirl, but bug out if recursive
; refs remain. We do this by setting id's opnd to new
; scorer and bugging out when we find a scorer in place
; of an operand in decode-ref. we don't have to worry
; about finding an assignment because we don't attempt
; to integrated assigned variables
(call/1cc
(lambda (k)
(let ([new-sc (new-scorer limit ctxt k)]
[new-wd (new-watchdog wd ctxt k)])
(with-extended-env ((env ignore-ids) (empty-env (list id) (list new-sc)))
(let ([x (opending-protect opnd
(cp0-let preinfo1 ids body ctxt env new-sc new-wd name moi))])
(bump sc (fx- limit (scorer-limit new-sc)))
x))))))
(residualize-ref maybe-src id sc))
; the monovariant filter below is flawed because opnd sizes do
; necessarily reflect integrated singly referenced items
(or (and (or polyvariant (fx< (operand-score opnd) limit))
(call/1cc
(lambda (k)
(let ([new-wd (new-watchdog wd ctxt k)])
(if (prelex-was-multiply-referenced id)
(let ([new-sc (new-scorer limit ctxt k)])
(let ([x (opending-protect opnd
(cp0-let preinfo1 ids body ctxt empty-env new-sc new-wd name moi))])
(bump sc (fx- limit (scorer-limit new-sc)))
x))
(let ([new-sc (new-scorer)])
(let ([x (opending-protect opnd
(cp0-let preinfo1 ids body ctxt empty-env new-sc new-wd name moi))])
(operand-singly-referenced-score-set! opnd (scorer-score new-sc))
x)))))))
(residualize-ref maybe-src id sc))))]
[() (residualize-ref maybe-src id sc)]))]
[else (residualize-ref maybe-src id sc)])]
[,pr
(context-case ctxt
[(value)
(if (all-set? (prim-mask (or primitive proc)) (primref-flags pr))
rhs
(residualize-ref maybe-src id sc))]
[(test)
(if (all-set? (prim-mask proc) (primref-flags pr))
true-rec
(residualize-ref maybe-src id sc))]
[else (fold-primref rhs ctxt sc wd name moi)])]
[else (residualize-ref maybe-src id sc)]))))
(define fold-primref
(lambda (pr ctxt sc wd name moi)
(let ([opnds (app-opnds ctxt)])
(convention-case (app-convention ctxt)
[(call)
(let ([flags (primref-flags pr)] [outer-ctxt (app-ctxt ctxt)])
(cond
[(and (eq? outer-ctxt 'effect)
(if (all-set? (prim-mask unsafe) flags)
(all-set? (prim-mask discard) flags)
(and (all-set? (prim-mask (or unrestricted discard)) flags)
(arity-okay? (primref-arity pr) (length opnds)))))
(residualize-seq '() opnds ctxt)
void-rec]
[(and (eq? outer-ctxt 'test)
(all-set?
(if (all-set? (prim-mask unsafe) flags)
(prim-mask (or discard true))
(prim-mask (or unrestricted discard true)))
flags))
(residualize-seq '() opnds ctxt)
true-rec]
[(and (eq? outer-ctxt 'test)
(all-set? (prim-mask true) flags))
(make-seq outer-ctxt
(fold-primref2 pr (primref-name pr) opnds flags ctxt sc wd name moi)
true-rec)]
[else (fold-primref2 pr (primref-name pr) opnds flags ctxt sc wd name moi)]))]
[(apply2 apply3)
; handler for apply will have turned the apply into a call if the last
; argument is discovered to be a list. nothing more we can do here.
(residualize-primcall pr #f opnds ctxt sc)]))))
(define fold-primref2
(lambda (pr sym opnds pflags ctxt sc wd name moi)
(safe-assert (convention-case (app-convention ctxt) [(call) #t] [else #f]))
(let ([handler (or (and (all-set? (prim-mask unsafe) pflags)
(all-set? (prim-mask cp03) pflags)
($sgetprop sym 'cp03 #f))
(and (all-set? (prim-mask cp02) pflags)
($sgetprop sym 'cp02 #f)))])
(or (and handler
(let ([level (if (all-set? (prim-mask unsafe) pflags) 3 2)])
(handler level opnds ctxt sc wd name moi)))
(let ([args (value-visit-operands! opnds)])
(cond
[(and (all-set? (prim-mask mifoldable) pflags)
(let ([objs (objs-if-constant args)])
(and objs (guard (c [#t #f])
(call-with-values
(lambda () (apply ($top-level-value sym) objs))
(case-lambda
[(v) `(quote ,v)]
[v* `(call ,(app-preinfo ctxt) ,(lookup-primref 3 'values)
,(map (lambda (x) `(quote ,x)) v*)
...)])))))) =>
(lambda (e)
(residualize-seq '() opnds ctxt)
e)]
[else
(residualize-primcall pr args opnds ctxt sc)]))))))
(define residualize-primcall
(lambda (pr args opnds ctxt sc)
(let ([args (or args (value-visit-operands! opnds))])
(residualize-seq opnds '() ctxt)
(bump sc 1)
(let ([preinfo (app-preinfo ctxt)])
(convention-case (app-convention ctxt)
[(call) `(call ,preinfo ,pr ,args ...)]
[(apply2) (build-primcall preinfo 2 'apply (cons pr args))]
[(apply3) (build-primcall preinfo 3 'apply (cons pr args))])))))
(define objs-if-constant
(lambda (e*)
(if (null? e*)
'()
(nanopass-case (Lsrc Expr) (result-exp (car e*))
[(quote ,d)
(let ([rest (objs-if-constant (cdr e*))])
(and rest (cons d rest)))]
[else #f]))))
(define record-equal?
; not very ambitious
(lambda (e1 e2 ctxt)
(if (eq? ctxt 'effect)
(and (simple? e1) (simple? e2))
(nanopass-case (Lsrc Expr) e1
[(ref ,maybe-src1 ,x1)
(nanopass-case (Lsrc Expr) e2
[(ref ,maybe-src2 ,x2) (eq? x1 x2)]
[else #f])]
[(quote ,d1)
(nanopass-case (Lsrc Expr) e2
[(quote ,d2)
(if (eq? ctxt 'test)
(if d1 d2 (not d2))
(eq? d1 d2))]
[else #f])]
[else #f]))))
(module ()
(define-syntax define-inline
(lambda (x)
(syntax-case x ()
((_key lev prim clause ...)
(identifier? #'prim)
#'(_key lev (prim) clause ...))
((_key lev (prim ...) clause ...)
(andmap identifier? #'(prim ...))
(with-implicit (_key prim-name level ctxt sc wd name moi)
(with-syntax
((key (case (datum lev)
((2) #'cp02)
((3) #'cp03)
(else ($oops #f "invalid inline level ~s" (datum lev)))))
(body
(let f ((clauses #'(clause ...)))
(if (null? clauses)
#'#f
(with-syntax ((rest (f (cdr clauses))))
(syntax-case (car clauses) ()
(((x ...) e1 e2 ...)
(with-syntax ((n (length #'(x ...))))
#'(if (eq? count n)
(apply (lambda (x ...) e1 e2 ...) args)
rest)))
((r e1 e2 ...)
(identifier? #'r)
#'(apply (lambda r e1 e2 ...) args))
((r e1 e2 ...)
(with-syntax ((n (let loop ((r #'r) (n 0))
(syntax-case r ()
((v . r)
(identifier? #'v)
(loop #'r (+ n 1)))
(v
(identifier? #'v)
n)))))
#'(if (fx>= count n)
(apply (lambda r e1 e2 ...) args)
rest)))))))))
(for-each
(lambda (sym-name)
(let ([sym-key (datum key)])
(if (getprop sym-name sym-key #f)
(warningf #f "duplicate ~s handler for ~s" sym-key sym-name)
(putprop sym-name sym-key #t))
(unless (all-set?
(case (datum lev)
[(2) (prim-mask cp02)]
[(3) (prim-mask cp03)])
($sgetprop sym-name '*flags* 0))
(warningf #f "undeclared ~s handler for ~s~%" sym-key sym-name))))
(datum (prim ...)))
#'(let ((foo (lambda (prim-name)
(lambda (level args ctxt sc wd name moi)
(let ((count (length args)))
body)))))
($sputprop 'prim 'key (foo 'prim)) ...)))))))
(define generic-nan?
(lambda (x)
(and (flonum? x) ($nan? x))))
(define fl-nan?
(lambda (x)
($nan? x)))
(define cfl-nan?
(lambda (z)
(and ($nan? (cfl-real-part z)) ($nan? (cfl-imag-part z)))))
(define exact-zero?
(lambda (x)
(eq? x 0)))
(define exact-negone?
(lambda (x)
(eq? x -1)))
;;; what to include here vs. in cp1in:
;;; Include here transformations that eliminate the need to evaluate an
;;; operand (for value) or that may open up opportunities for further
;;; folding. For example, memq with second argument '() doesn't need
;;; the value of its first operand. It also evaluates to #f or
;;; (seq <something> #f) and thus may lead to further folding.
;;;
;;; Don't bother with optimizations, such as strength reduction, that
;;; just result in other calls. For example, memq with a constant,
;;; non-null second argument expands into calls to eq?, which we can't
;;; do anything with.
;;; caution:
;;; We must use value-visit-operand here rather than calling cp0 on
;;; operand expressions. Although at this level we may be guaranteed
;;; to succeed, we may be succeeding in an outer context of an inner
;;; context that will eventually fail. For example, in:
;;;
;;; (let ((f (lambda ()
;;; (let ((x huge-a))
;;; (let ((g (lambda () x)))
;;; (g) not)))))
;;; ((f) huge-b))
;;;
;;; where huge-a and huge-b are large unreducible expressions, we
;;; create an operand O1 for huge-a, process (f) in an app context
;;; A1, process f in another app context A2 whose outer context is
;;; A1, encounter reference to f, process the (lambda () ...) for
;;; value, producing:
;;;
;;; (lambda ()
;;; (let ((x huge-a))
;;; not))
;;;
;;; then attempt to integrate the body of this lambda expression in
;;; the outer app context A1, resulting in an attempt to apply not to
;;; the operand O1. Say not extracts the expression from O1 to
;;; produce (if huge-b #f #t). We would then process this if,
;;; including huge-b. when trying to rebuild (let ((x huge-a)) ...),
;;; we would discover that x is not referenced, but would leave
;;; behind huge-a and, because of its size, abort the attempted
;;; inlining of app context A1. We would then residualize the call
;;; ((f) huge-b), processing O1's expression, huge-b, again.
;;;
;;; Primitives that must extract the operand expression (such as
;;; not and apply) should be caught in the call case of cp0 before
;;; the expressions are encapsulated in operand structures. A
;;; downside to catching prims at that level is that in
;;; (let ((f prim)) (f e)), the call (f e) won't be recognized as
;;; a primitive application of prim.
;;;
;;; On the other hand, this arises only while we are integrating,
;;; so if we charge the first processing of huge-b to the watchdog
;;; and scorer associated with the A1 integration attempt rather
;;; than to the top level watchdog and scorer that we would usually
;;; use for huge-b, we could be okay.
; okay-to-handle? should return #f only when interpreting code on a
; host machine with a different target-machine compiler loaded. we
; try to treat the other cases the same (not cross-compiling, with
; no cross compiler loaded, or cross-compiling, with cross compiler
; loaded) so that we don't have mostly untested handler code for
; cross-compiled case.
(define (okay-to-handle?) (eq? ($target-machine) (constant machine-type-name)))
(define-syntax visit-and-maybe-extract*
(lambda (x)
(syntax-case x ()
[(_ ?pred? ([x opnd] ...) e1 e2 ...)
#`(let ([pred? ?pred?])
(and (okay-to-handle?)
#,(fold-right (lambda (x opnd e)
#`(let ([xval (value-visit-operand! #,opnd)])
(nanopass-case (Lsrc Expr) (result-exp xval)
[(quote ,d) (and (pred? d) (let ([#,x d]) #,e))]
[else #f])))
#'(begin e1 e2 ...) #'(x ...) #'(opnd ...))))])))
(define handle-shift
(lambda (level ctxt x y)
(and (fx= level 3)
(let ([xval (value-visit-operand! x)]
[yval (value-visit-operand! y)])
(cond
[(cp0-constant? (lambda (obj) (eqv? obj 0)) (result-exp yval))
(residualize-seq (list x) (list y) ctxt)
xval]
[else #f])))))
; could handle inequalities as well (returning #f), but that seems less likely to crop up
(define handle-equality
(lambda (ctxt arg arg*)
(and
(or (null? arg*)
(nanopass-case (Lsrc Expr) (result-exp (value-visit-operand! arg))
[(ref ,maybe-src ,x0)
(and (not (prelex-was-assigned x0))
(andmap
(lambda (arg)
(and (nanopass-case (Lsrc Expr) (result-exp (value-visit-operand! arg))
[(ref ,maybe-src ,x) (eq? x x0)]
[else #f])))
arg*))]
[else #f]))
(begin
(residualize-seq '() (cons arg arg*) ctxt)
true-rec))))
(define-inline 2 machine-type
[() (begin
(residualize-seq '() '() ctxt)
`(quote ,($target-machine)))])
(let ()
(define-syntax define-inline-constant-parameter
(syntax-rules ()
[(_ (name ...) k)
(define-inline 2 (name ...)
[() (and (okay-to-handle?)
(begin
(residualize-seq '() '() ctxt)
`(quote ,k)))])]))
(define-inline-constant-parameter (native-endianness) (constant native-endianness))
(define-inline-constant-parameter (directory-separator) (if-feature windows #\\ #\/))
(define-inline-constant-parameter (threaded?) (if-feature pthreads #t #f))
(define-inline-constant-parameter (most-negative-fixnum least-fixnum) (constant most-negative-fixnum))
(define-inline-constant-parameter (most-positive-fixnum greatest-fixnum) (constant most-positive-fixnum))
(define-inline-constant-parameter (fixnum-width) (constant fixnum-bits))
(define-inline-constant-parameter (virtual-register-count) (constant virtual-register-count)))
(define-inline 2 directory-separator?
[(c) (visit-and-maybe-extract* char? ([dc c])
(residualize-seq '() (list c) ctxt)
`(quote ,(and (memv dc (if-feature windows '(#\\ #\/) '(#\/))) #t)))])
(define-inline 2 foreign-sizeof
[(x) (and (okay-to-handle?)
(let ([xval (value-visit-operand! x)])
(nanopass-case (Lsrc Expr) (result-exp xval)
[(quote ,d)
(let ()
(define-syntax size
(syntax-rules ()
[(_ type bytes pred)
(begin
(residualize-seq '() (list x) ctxt)
`(quote ,bytes))]))
(record-datatype cases (filter-foreign-type d) size #f))]
[else #f])))])
(let ([addr-int? (constant-case address-bits [(32) $integer-32?] [(64) $integer-64?])])
(define-inline 2 $verify-ftype-address
[(who e) (visit-and-maybe-extract* addr-int? ([de e])
(residualize-seq '() (list who e) ctxt)
true-rec)]))
(define-inline 2 (memq memv member assq assv assoc)
[(x ls)
(and (cp0-constant? null? (result-exp (value-visit-operand! ls)))
(begin
(residualize-seq '() (list x ls) ctxt)
false-rec))])
(define-inline 3 (memp assp find)
[(pred ls)
(and (cp0-constant? null? (result-exp (value-visit-operand! ls)))
(begin
(residualize-seq '() (list pred ls) ctxt)
false-rec))])
(define-inline 2 (remq remv remove)
[(x ls)
(and (cp0-constant? null? (result-exp (value-visit-operand! ls)))
(begin
(residualize-seq '() (list x ls) ctxt)
null-rec))])
(define-inline 3 (remp filter)
[(pred ls)
(and (cp0-constant? null? (result-exp (value-visit-operand! ls)))
(begin
(residualize-seq '() (list pred ls) ctxt)
null-rec))])
(define-inline 2 apply
[(proc opnd1 . opnds)
(let ([opnds (cons opnd1 opnds)])
(let ([last-opnd (car (last-pair opnds))])
(cond
[(nanopass-case (Lsrc Expr) (result-exp (value-visit-operand! last-opnd))
[(quote ,d) (guard (list? d) (<= (length d) 1000)) (map build-quote d)]
[(immutable-list (,e* ...) ,e) e*]
[(call ,preinfo ,pr ,e* ...) (guard (eq? (primref-name pr) 'list)) e*]
[else #f]) =>
(lambda (e*)
(let ([opnds (let f ([opnds opnds])
(let ([rest (cdr opnds)])
(if (null? rest)
'()
(cons (car opnds) (f (cdr opnds))))))])
(let ([tproc (cp0-make-temp #f)] [t* (map (lambda (x) (cp0-make-temp #f)) opnds)])
(with-extended-env ((env ids) (empty-env (cons tproc t*) (cons proc opnds)))
(letify (make-preinfo-lambda) ids ctxt (list last-opnd)
(non-result-exp (operand-value last-opnd)
(cp0-call (app-preinfo ctxt) (build-ref tproc)
(fold-right
(lambda (t opnd*) (cons (make-operand (build-ref t) env wd moi) opnd*))
(map build-cooked-opnd e*)
t*)
(app-ctxt ctxt) env sc wd (app-name ctxt) moi)))))))]
[(nanopass-case (Lsrc Expr) (result-exp (value-visit-operand! last-opnd))
[(call ,preinfo ,pr ,e1 ,e2) (guard (eq? (primref-name pr) 'cons)) (list e1 e2)]
[(call ,preinfo ,pr ,e ,e* ...) (guard (memq (primref-name pr) '(list* cons*))) (cons e e*)]
[else #f]) =>
(lambda (e*)
(let ([opnds (cons proc
(let f ([opnds opnds])
(let ([rest (cdr opnds)])
(if (null? rest)
'()
(cons (car opnds) (f (cdr opnds)))))))])
(let ([t* (map (lambda (x) (cp0-make-temp #f)) opnds)])
(with-extended-env ((env ids) (empty-env t* opnds))
(letify (make-preinfo-lambda) ids ctxt (list last-opnd)
(non-result-exp (operand-value last-opnd)
(cp0-call (app-preinfo ctxt) (lookup-primref level 'apply)
(fold-right
(lambda (t opnd*) (cons (make-operand (build-ref t) env wd moi) opnd*))
(map build-cooked-opnd e*)
t*)
(app-ctxt ctxt) env sc wd (app-name ctxt) moi)))))))]
[else
(let ([temp (cp0-make-temp #f)]) ; N.B.: temp is singly referenced
(with-extended-env ((env ids) (empty-env (list temp) (list proc)))
(let* ([new-ctxt (make-app opnds (app-ctxt ctxt)
(if (fx= level 3) 'apply3 'apply2)
(app-name ctxt)
(app-preinfo ctxt))]
[e (cp0 (build-ref temp) new-ctxt env sc wd #f moi)])
(and (app-used new-ctxt)
(begin
(residualize-seq (app-used new-ctxt) (cons proc (app-unused new-ctxt)) ctxt)
e)))))])))])
(define-inline 2 not
[(e)
(let ([e-val (test-visit-operand! e)])
(nanopass-case (Lsrc Expr) (result-exp e-val)
[(quote ,d)
(residualize-seq '() (list e) ctxt)
(if d false-rec true-rec)]
[else
(residualize-seq (list e) '() ctxt)
(make-if ctxt sc e-val false-rec true-rec)]))])
(define-inline 2 call-with-values
[(p-opnd c-opnd)
(let ((p-temp (cp0-make-temp #f)) (c-temp (cp0-make-temp #f)))
(with-extended-env ((env ids) (empty-env (list p-temp c-temp) (app-opnds ctxt)))
(let ((ctxt1 (make-app '() 'value 'call #f (app-preinfo ctxt))))
(let ((*p-val (cp0 (build-ref p-temp) ctxt1 env sc wd #f moi)))
(cond
[(and (app-used ctxt1)
(let ([e (result-exp *p-val)])
(nanopass-case (Lsrc Expr) e
; in dire need of matching more than one pattern
[(quote ,d) (list e)]
[(ref ,maybe-src ,x) (list e)]
[(set! ,maybe-src ,x0 ,e0) (list e)]
[(case-lambda ,preinfo ,cl* ...) (list e)]
[,pr (list e)]
[(foreign (,conv* ...) ,name ,e0 (,arg-type* ...) ,result-type) (list e)]
[(fcallable (,conv* ...) ,e0 (,arg-type* ...) ,result-type) (list e)]
[(record-type ,rtd0 ,e0) (list e)]
[(record-cd ,rcd0 ,rtd-expr0 ,e0) (list e)]
[(immutable-list (,e0* ...) ,e0) (list e)]
[(record-ref ,rtd ,type ,index ,e0) (list e)]
[(record-set! ,rtd ,type ,index ,e1 ,e2) (list e)]
[(record ,rtd ,rtd-expr ,e* ...) (list e)]
[(call ,preinfo ,pr ,e* ...)
(guard (eq? (primref-name pr) 'values))
e*]
[else #f]))) =>
(lambda (args)
; (with-values (values arg ...) c-temp) => (c-temp arg ...)
(letify (make-preinfo-lambda) ids ctxt
(non-result-exp *p-val
(cp0-call (app-preinfo ctxt) (build-ref c-temp)
(map build-cooked-opnd args)
(app-ctxt ctxt) env sc wd (app-name ctxt) moi))))]
[else
(call-with-values
(lambda ()
(let ((e (value-visit-operand! c-opnd)))
(nanopass-case (Lsrc Expr) (result-exp e)
[(case-lambda ,preinfo ,cl* ...)
(values (result-exp e) '() (list c-opnd))]
[,pr (values (result-exp e) '() (list c-opnd))]
[else (values e (list c-opnd) '())])))
(lambda (c-val used unused)
(if (app-used ctxt1)
(begin
(residualize-seq used (cons p-opnd unused) ctxt)
(non-result-exp *p-val
(build-primcall (app-preinfo ctxt) level 'call-with-values
(list
(build-lambda '() (result-exp *p-val))
c-val))))
(build-primcall (app-preinfo ctxt) level 'call-with-values
(list
(let ((e (value-visit-operand! p-opnd)))
(nanopass-case (Lsrc Expr) (result-exp e)
[(case-lambda ,preinfo ,cl* ...)
(residualize-seq used (cons p-opnd unused) ctxt)
(result-exp e)]
[,pr
(residualize-seq used (cons p-opnd unused) ctxt)
(result-exp e)]
[else
(residualize-seq (cons p-opnd used) unused ctxt)
e]))
c-val)))))])))))])
(define-inline 2 list
[() (begin
(residualize-seq '() '() ctxt)
null-rec)]
[args #f])
(define-inline 2 (cons* list* values append append!)
[(x) (let ((xval (value-visit-operand! x)))
(residualize-seq (list x) '() ctxt)
xval)]
[args #f])
(define-inline 2 vector
[() (begin
(residualize-seq '() '() ctxt)
empty-vector-rec)]
[args #f])
(define-inline 2 string
[() (begin
(residualize-seq '() '() ctxt)
empty-string-rec)]
[args #f])
(define-inline 2 bytevector
[() (begin
(residualize-seq '() '() ctxt)
empty-bytevector-rec)]
[args #f])
(define-inline 2 fxvector
[() (begin
(residualize-seq '() '() ctxt)
empty-fxvector-rec)]
[args #f])
(define-inline 2 (eq? eqv? equal?)
[(arg1 arg2) (handle-equality ctxt arg1 (list arg2))])
(define-inline 3 (bytevector=? enum-set=? bound-identifier=? free-identifier=? ftype-pointer=? literal-identifier=? time=?)
[(arg1 arg2) (handle-equality ctxt arg1 (list arg2))])
(define-inline 3 (char=? char-ci=? string=? string-ci=?)
[(arg . arg*) (handle-equality ctxt arg arg*)])
(define-inline 3 (boolean=? symbol=? r6rs:char=? r6rs:char-ci=? r6rs:string=? r6rs:string-ci=?)
[(arg1 arg2 . arg*) (handle-equality ctxt arg1 (cons arg2 arg*))])
(define-inline 3 (ash
bitwise-arithmetic-shift bitwise-arithmetic-shift-left
bitwise-arithmetic-shift-right)
[(x y) (handle-shift 3 ctxt x y)])
(define-inline 3 fxbit-field ; expose internal fx ops for partial optimization
[(?n ?start ?end)
(cp0
(let ([n (cp0-make-temp #f)]
[start (cp0-make-temp #f)]
[end (cp0-make-temp #f)])
(build-lambda (list n start end)
(build-primcall 3 'fxsra
(list
(build-primcall 3 'fxand
(list
(build-ref n)
(build-primcall 3 'fxnot
(list
(build-primcall 3 'fxsll
(list
`(quote -1)
(build-ref end)))))))
(build-ref start)))))
ctxt empty-env sc wd name moi)])
(let ()
(define make-fold?
(lambda (op generic-op)
(lambda (val a) ; returns value of (op a val) or #f
(nanopass-case (Lsrc Expr) (result-exp val)
[(quote ,d)
(guard (c [#t #f])
(if (eq? generic-op op)
(op a d)
(and (target-fixnum? d)
(let ([folded (generic-op a d)])
(and (target-fixnum? folded) folded)))))]
[else #f]))))
(define (partial-fold-plus level orig-arg* ctxt prim op generic-op ident bottom?)
(define fold? (make-fold? op generic-op))
(let loop ([arg* (reverse orig-arg*)] [a ident] [val* '()] [used '()] [unused '()])
(if (null? arg*)
(cond
[(bottom? a)
(cond
[(or (fx= level 3) (null? val*))
(residualize-seq '() orig-arg* ctxt)
`(quote ,a)]
[else
(residualize-seq used unused ctxt)
`(seq
,(build-primcall (app-preinfo ctxt) level prim val*)
(quote ,a))])]
[else
(residualize-seq used unused ctxt)
(cond
[(null? val*) `(quote ,a)]
[(eqv? a ident)
(if (and (fx= level 3) (null? (cdr val*)))
(car val*)
(build-primcall (app-preinfo ctxt) level prim val*))]
[else
(build-primcall (app-preinfo ctxt) level prim (cons `(quote ,a) val*))])])
(let* ([arg (car arg*)] [val (value-visit-operand! arg)])
(cond
[(fold? val a) =>
(lambda (a) (loop (cdr arg*) a val* used (cons arg unused)))]
[else (loop (cdr arg*) a (cons val val*) (cons arg used) unused)])))))
(define (partial-fold-minus level arg arg* ctxt prim op generic-op ident)
; ident is such that (op ident x) == (op x)
(define fold? (make-fold? op generic-op))
(define (finish a val* used unused)
(residualize-seq used unused ctxt)
(if (null? val*)
`(quote ,a)
(build-primcall (app-preinfo ctxt) level prim
(if (and (eqv? a ident) (null? (cdr val*)))
val*
(cons `(quote ,a) val*)))))
; to maintain left-associative behavior, stop when we get to the first non-constant arg
(let ([val (value-visit-operand! arg)])
(cond
[(nanopass-case (Lsrc Expr) (result-exp val)
; (op obj ident) is not necessarily the same as obj, so return obj
[(quote ,d) (and (guard (c [#t #f]) (op d ident)) d)]
[else #f]) =>
(lambda (a)
(let loop ([arg* arg*] [a a] [val* '()] [unused (list arg)])
(if (null? arg*)
(finish a (reverse val*) '() unused)
(let* ([arg (car arg*)] [val (value-visit-operand! arg)])
(cond
[(fold? val a) => (lambda (a) (loop (cdr arg*) a val* (cons arg unused)))]
[else (finish a (rappend val* (map value-visit-operand! arg*)) arg* unused)])))))]
[else #f])))
(define (partial-fold-negate level arg ctxt prim op generic-op ident)
(define fold? (make-fold? op generic-op))
(let ([val (value-visit-operand! arg)])
(cond
[(fold? val ident) =>
(lambda (a)
(residualize-seq '() (list arg) ctxt)
`(quote ,a))]
[else #f])))
(define-syntax partial-folder
; partial-fold-plus assumes arg* is nonempty
(syntax-rules (plus minus)
[(_ plus prim generic-op ident)
(partial-folder plus prim generic-op ident (lambda (x) #f))]
[(_ plus prim generic-op ident bottom?)
(begin
(define-inline 2 prim
; (fl+) might should return -0.0, but we force it to return +0.0 per TSPL4
[() (residualize-seq '() '() ctxt) `(quote ,(if (eqv? ident -0.0) +0.0 ident))]
[arg* (partial-fold-plus 2 arg* ctxt 'prim prim generic-op ident bottom?)])
(define-inline 3 prim
; (fl+) might should return -0.0, but we force it to return +0.0 per TSPL4
[() (residualize-seq '() '() ctxt) `(quote ,(if (eqv? ident -0.0) +0.0 ident))]
[arg* (partial-fold-plus 3 arg* ctxt 'prim prim generic-op ident bottom?)]))]
[(_ minus prim generic-op ident)
(begin
(define-inline 2 prim
[(arg) (partial-fold-negate 2 arg ctxt 'prim prim generic-op ident)]
[(arg . arg*) (partial-fold-minus 2 arg arg* ctxt 'prim prim generic-op ident)])
(define-inline 3 prim
[(arg) (partial-fold-negate 3 arg ctxt 'prim prim generic-op ident)]
[(arg . arg*) (partial-fold-minus 3 arg arg* ctxt 'prim prim generic-op ident)]))]))
(define-syntax r6rs-fixnum-partial-folder
; fx+ and fx* limited to exactly two args, fx- limited to one or two args
(syntax-rules (plus minus)
[(_ plus r6rs:prim prim generic-op ident)
(r6rs-fixnum-partial-folder plus r6rs:prim prim generic-op ident (lambda (x) #f))]
[(_ plus r6rs:prim prim generic-op ident bottom?)
(begin
(define-inline 2 r6rs:prim
[(arg1 arg2) (partial-fold-plus 2 (list arg1 arg2) ctxt 'prim prim generic-op ident bottom?)])
(define-inline 3 r6rs:prim
[(arg1 arg2) (partial-fold-plus 3 (list arg1 arg2) ctxt 'prim prim generic-op ident bottom?)]))]
[(_ minus r6rs:prim prim generic-op ident)
(begin
(define-inline 2 r6rs:prim
[(arg) (partial-fold-negate 2 arg ctxt 'prim prim generic-op ident)]
[(arg1 arg2)
(partial-fold-minus 2 arg1 (list arg2) ctxt 'prim prim generic-op ident)])
(define-inline 3 r6rs:prim
[(arg) (partial-fold-negate 3 arg ctxt 'prim prim generic-op ident)]
[(arg1 arg2)
(partial-fold-minus 3 arg1 (list arg2) ctxt 'prim prim generic-op ident)]))]))
; handling nans here using the support for handling exact zero in
; the multiply case. maybe shouldn't bother with nans anyway.
(partial-folder plus + + 0 generic-nan?)
(partial-folder plus fx+ + 0)
(r6rs-fixnum-partial-folder plus r6rs:fx+ fx+ + 0)
(partial-folder plus fl+ fl+ -0.0 fl-nan?)
(partial-folder plus cfl+ cfl+ -0.0 cfl-nan?)
(partial-folder plus * * 1 exact-zero?) ; exact zero trumps nan
(partial-folder plus fx* * 1 exact-zero?)
(r6rs-fixnum-partial-folder plus r6rs:fx* fx* * 1 exact-zero?)
(partial-folder plus fl* fl* 1.0 fl-nan?)
(partial-folder plus cfl* cfl* 1.0 cfl-nan?)
; not handling nans here since we don't have support for the exact
; zero case in division. it would be nice to reduce (/ 0 x1 x2 ...)
; to 0, but (/ 0 n) is only 0 if divisor turns out not to be 0.
(partial-folder minus - - 0)
(partial-folder minus fx- - 0)
(r6rs-fixnum-partial-folder minus r6rs:fx- fx- - 0)
(partial-folder minus fl- fl- -0.0)
(partial-folder minus cfl- cfl- -0.0)
(partial-folder minus / / 1)
(partial-folder minus fx/ quotient 1)
(partial-folder minus fxquotient quotient 1)
(partial-folder minus fl/ fl/ 1.0)
(partial-folder minus cfl/ cfl/ 1.0)
(partial-folder plus logior logior 0 exact-negone?)
(partial-folder plus logor logor 0 exact-negone?)
(partial-folder plus bitwise-ior bitwise-ior 0 exact-negone?)
(partial-folder plus fxlogior logor 0 exact-negone?)
(partial-folder plus fxior logor 0 exact-negone?)
(partial-folder plus fxlogor logor 0 exact-negone?)
(partial-folder plus logxor logxor 0)
(partial-folder plus bitwise-xor bitwise-xor 0)
(partial-folder plus fxlogxor logxor 0)
(partial-folder plus fxxor logxor 0)
(partial-folder plus logand logand -1 exact-zero?)
(partial-folder plus bitwise-and bitwise-and -1 exact-zero?)
(partial-folder plus fxlogand logand -1 exact-zero?)
(partial-folder plus fxand logand -1 exact-zero?)
)
(let ()
(define $fold
(lambda (generic-op orig-opnd* pred* opred level ctxt handler)
(define cookie '(fig . newton))
(and (okay-to-handle?)
(or (let loop ([opnd* orig-opnd*] [pred* pred*] [rval* '()])
(if (null? opnd*)
(let ([val (guard (c [#t cookie]) (apply generic-op (reverse rval*)))])
(and (not (eq? val cookie))
(opred val)
(begin
(residualize-seq '() orig-opnd* ctxt)
`(quote ,val))))
(let-values ([(pred pred*) (if (procedure? pred*) (values pred* pred*) (values (car pred*) (cdr pred*)))])
(visit-and-maybe-extract* pred ([val (car opnd*)])
(loop (cdr opnd*) pred* (cons val rval*))))))
(apply handler level ctxt orig-opnd*)))))
(define null-handler (lambda args #f))
(define-syntax fold
(lambda (x)
(syntax-case x ()
[(_ (prim ipred ...) opred generic-op) #'(fold (prim ipred ...) opred generic-op null-handler)]
[(_ (prim ipred ...) opred generic-op handler)
(with-syntax ([(arg ...) (generate-temporaries #'(ipred ...))])
#'(define-inline 2 prim
[(arg ...)
($fold generic-op (list arg ...) (list ipred ...) opred level ctxt handler)]))]
[(_ (prim ipred ... . rpred) opred generic-op) #'(fold (prim ipred ... . rpred) opred generic-op null-handler)]
[(_ (prim ipred ... . rpred) opred generic-op handler)
(with-syntax ([(arg ...) (generate-temporaries #'(ipred ...))])
#'(define-inline 2 prim
[(arg ... . rest)
($fold generic-op (cons* arg ... rest) (cons* ipred ... rpred) opred level ctxt handler)]))])))
(define tfixnum? target-fixnum?)
(define u<=fxwidth?
(lambda (x)
(and (fixnum? x)
(fx<= 0 x (constant fixnum-bits)))))
(define u<fxwidth?
(lambda (x)
(and (fixnum? x)
(fx<= 0 x (- (constant fixnum-bits) 1)))))
(define s<fxwidth?
(lambda (x)
(and (fixnum? x)
(fx<= (- 1 (constant fixnum-bits)) x (- (constant fixnum-bits) 1)))))
(define u<fxwidth-1?
(lambda (x)
(and (fixnum? x)
(fx<= 0 x (- (constant fixnum-bits) 2)))))
(fold (fx< tfixnum? . tfixnum?) boolean? #2%<)
(fold (fx<= tfixnum? . tfixnum?) boolean? #2%<=)
(fold (fx= tfixnum? . tfixnum?) boolean? #2%=
(lambda (level ctxt arg . arg*)
(and (fx= level 3) (handle-equality ctxt arg arg*))))
(fold (fx> tfixnum? . tfixnum?) boolean? #2%>)
(fold (fx>= tfixnum? . tfixnum?) boolean? #2%>=)
(fold (fx<? tfixnum? tfixnum? . tfixnum?) boolean? #2%<)
(fold (fx<=? tfixnum? tfixnum? . tfixnum?) boolean? #2%<=)
(fold (fx=? tfixnum? tfixnum? . tfixnum?) boolean? #2%=
(lambda (level ctxt arg . arg*)
(and (fx= level 3) (handle-equality ctxt arg arg*))))
(fold (fx>? tfixnum? tfixnum? . tfixnum?) boolean? #2%>)
(fold (fx>=? tfixnum? tfixnum? . tfixnum?) boolean? #2%>=)
(fold ($fxu< tfixnum? tfixnum?) boolean?
(lambda (x y)
(if (#2%< x 0)
(and (#2%< y 0) (#2%< x y))
(or (#2%< y 0) (#2%< x y))))
(lambda (level ctxt x y)
(let ([xval (value-visit-operand! x)]
[yval (value-visit-operand! y)])
(and (cp0-constant? (lambda (obj) (eqv? obj (constant most-positive-fixnum))) (result-exp xval))
(begin
(residualize-seq (list y) (list x) ctxt)
(build-primcall (app-preinfo ctxt) level 'fx< (list yval `(quote 0))))))))
(fold (fxmax tfixnum? . tfixnum?) tfixnum? #2%max)
(fold (fxmin tfixnum? . tfixnum?) tfixnum? #2%min)
(fold (fxabs tfixnum?) tfixnum? #2%abs)
(fold (fxdiv tfixnum? tfixnum?) tfixnum? #2%div)
(fold (fxmod tfixnum? tfixnum?) tfixnum? #2%mod)
(fold (fxmodulo tfixnum? tfixnum?) tfixnum? #2%modulo)
(fold (fxdiv0 tfixnum? tfixnum?) tfixnum? #2%div0)
(fold (fxmod0 tfixnum? tfixnum?) tfixnum? #2%mod0)
(fold (fxremainder tfixnum? tfixnum?) tfixnum? #2%remainder)
(fold ((fxnot fxlognot) tfixnum?) tfixnum? #2%bitwise-not)
(fold (fxlogtest tfixnum? tfixnum?) boolean? #2%logtest)
(fold (fxif tfixnum? tfixnum? tfixnum?) tfixnum? #2%bitwise-if)
(fold (fxbit-count tfixnum?) tfixnum? #2%bitwise-bit-count)
(fold (fxlength tfixnum?) tfixnum? #2%bitwise-length)
(fold (fxfirst-bit-set tfixnum?) tfixnum? #2%bitwise-first-bit-set)
(fold (fx1+ tfixnum?) tfixnum? #2%1+)
(fold (fx1- tfixnum?) tfixnum? #2%1-)
(fold (fxbit-set? tfixnum? tfixnum?) boolean? #2%bitwise-bit-set?)
(fold (fxcopy-bit tfixnum? u<fxwidth-1? tfixnum?) tfixnum? #2%bitwise-copy-bit)
(fold (fxbit-field tfixnum? u<fxwidth? u<fxwidth?) tfixnum? #2%bitwise-bit-field)
(fold (fxcopy-bit-field tfixnum? u<fxwidth? u<fxwidth? tfixnum?) tfixnum? #2%bitwise-copy-bit-field)
(fold (fxrotate-bit-field tfixnum? u<fxwidth? u<fxwidth? u<fxwidth?) tfixnum?
(lambda (x1 x2 x3 x4)
(unless (fx<= x4 (fx- x3 x2)) ($oops #f "strange x4 value"))
(#2%bitwise-rotate-bit-field x1 x2 x3 x4)))
(fold (fxreverse-bit-field tfixnum? u<fxwidth? u<fxwidth?) tfixnum? #2%bitwise-reverse-bit-field)
(fold (fxlogbit? tfixnum? tfixnum?) boolean? #2%logbit?)
(fold (fxlogbit0 u<fxwidth-1? tfixnum?) tfixnum? #2%logbit0)
(fold (fxlogbit1 u<fxwidth-1? tfixnum?) tfixnum? #2%logbit1)
(fold (fxarithmetic-shift tfixnum? s<fxwidth?) tfixnum? #2%bitwise-arithmetic-shift handle-shift)
(fold (fxarithmetic-shift-left tfixnum? u<fxwidth?) tfixnum? #2%bitwise-arithmetic-shift-left handle-shift)
(fold (fxarithmetic-shift-right tfixnum? u<fxwidth?) tfixnum? #2%bitwise-arithmetic-shift-right handle-shift)
(fold (fxsll tfixnum? u<=fxwidth?) tfixnum? #2%bitwise-arithmetic-shift-left handle-shift)
(fold (fxsra tfixnum? u<=fxwidth?) tfixnum? #2%bitwise-arithmetic-shift-right handle-shift)
(fold (fxsrl tfixnum? u<=fxwidth?) tfixnum?
(lambda (x k)
(if (eqv? k 0)
x
(#2%bitwise-arithmetic-shift-right
(#2%logand x (- (ash 1 (constant fixnum-bits)) 1))
k)))
handle-shift)
(fold (fixnum->flonum fixnum?) flonum? #2%inexact)
(fold (flonum->fixnum flonum?) target-fixnum? (lambda (x) (#2%truncate (#2%exact x))))
(fold (fxzero? tfixnum?) boolean? zero?)
(fold (fxnegative? tfixnum?) boolean? negative?)
(fold (fxpositive? tfixnum?) boolean? positive?)
(fold (fxeven? tfixnum?) boolean? even?)
(fold (fxodd? tfixnum?) boolean? odd?)
(fold (fxnonnegative? tfixnum?) boolean? nonnegative?)
(fold (fxnonpositive? tfixnum?) boolean? nonpositive?))
(let ()
(define target-wchar?
(lambda (x)
(and (char? x)
(constant-case wchar-bits
[(16) (< (char->integer x) #x10000)]
[(32) #t]))))
; NB: is this sufficiently tested by ftype.ms and record.ms?
(define-inline 2 $foreign-wchar?
[(x)
(and (okay-to-handle?)
(visit-and-maybe-extract* (lambda (x) #t) ([c x])
(residualize-seq '() (list x) ctxt)
`(quote ,(target-wchar? c))))]))
(let ()
(define $fold-bytevector-native-ref
(lambda (native-ref generic-ref align x y ctxt)
(and (okay-to-handle?)
(visit-and-maybe-extract* bytevector? ([dx x])
(visit-and-maybe-extract* (lambda (y)
(and (integer? y)
(exact? y)
(nonnegative? y)
(= (modulo y align) 0)))
([dy y])
(let ([val (guard (c [#t #f])
(generic-ref dx dy (constant native-endianness)))])
(and val
(begin
(residualize-seq '() (list x y) ctxt)
`(quote ,val)))))))))
(define-syntax fold-bytevector-native-ref
(syntax-rules ()
[(_ native-ref generic-ref align)
(define-inline 2 native-ref
[(x y) ($fold-bytevector-native-ref native-ref generic-ref align x y ctxt)])]))
(fold-bytevector-native-ref bytevector-u16-native-ref bytevector-u16-ref 2)
(fold-bytevector-native-ref bytevector-s16-native-ref bytevector-s16-ref 2)
(fold-bytevector-native-ref bytevector-u32-native-ref bytevector-u32-ref 4)
(fold-bytevector-native-ref bytevector-s32-native-ref bytevector-s32-ref 4)
(fold-bytevector-native-ref bytevector-u64-native-ref bytevector-u64-ref 8)
(fold-bytevector-native-ref bytevector-s64-native-ref bytevector-s64-ref 8))
(define-inline 2 expt
[(x y)
(let ([xval (nanopass-case (Lsrc Expr) (result-exp (value-visit-operand! x))
[(quote ,d) (and (or (fixnum? d) (flonum? d)) d)]
[else #f])]
[yval (nanopass-case (Lsrc Expr) (result-exp (value-visit-operand! y))
[(quote ,d) (and (or (and (fixnum? d) (fx< -1000 d 1000)) (flonum? d)) d)]
[else #f])])
(and xval
yval
(or (not (eq? xval 0)) (not (fixnum? yval)) (fx>= yval 0))
(begin
(residualize-seq '() (list x y) ctxt)
`(quote ,(expt xval yval)))))])
(define-inline 2 procedure?
[(x) (nanopass-case (Lsrc Expr) (result-exp/indirect-ref (value-visit-operand! x))
[(case-lambda ,preinfo ,cl ...)
(residualize-seq '() (list x) ctxt)
true-rec]
[,pr
(residualize-seq '() (list x) ctxt)
(if (all-set? (prim-mask proc) (primref-flags pr)) true-rec #f)]
[(quote ,d)
(residualize-seq '() (list x) ctxt)
(if (procedure? d) true-rec false-rec)]
[else #f])])
(define-inline 2 fixnum?
[(x) (visit-and-maybe-extract* (lambda (x) #t) ([dx x])
(residualize-seq '() (list x) ctxt)
`(quote ,(target-fixnum? dx)))])
(define-inline 2 bignum?
[(x) (visit-and-maybe-extract* (lambda (x) #t) ([dx x])
(residualize-seq '() (list x) ctxt)
`(quote ,(target-bignum? dx)))])
(let ()
(define do-inline-carry-op
(lambda (x y z base-op ctxt)
(and (okay-to-handle?)
(visit-and-maybe-extract* target-fixnum? ([dx x] [dy y] [dz z])
(residualize-seq '() (list x y z) ctxt)
(build-primcall 3 'values
(let ([s (base-op dx dy dz)])
(list
`(quote ,(mod0 s (expt 2 (constant fixnum-bits))))
`(quote ,(div0 s (expt 2 (constant fixnum-bits)))))))))))
(define-syntax define-inline-carry-op
(syntax-rules ()
[(_ op base-op)
(define-inline 2 op
[(x y z) (do-inline-carry-op x y z base-op ctxt)])]))
(define-inline-carry-op fx+/carry +)
(define-inline-carry-op fx-/carry -)
(define-inline-carry-op fx*/carry (lambda (x y z) (+ (* x y) z))))
(define-inline 3 fxdiv-and-mod
[(x y)
(and likely-to-be-compiled?
(cp0-constant? (result-exp (value-visit-operand! y)))
(cp0
(let ([tx (cp0-make-temp #t)] [ty (cp0-make-temp #t)])
(let ([refx (build-ref tx)] [refy (build-ref ty)])
(build-lambda (list tx ty)
(build-primcall 3 'values
(list
(build-primcall 3 'fxdiv (list refx refy))
(build-primcall 3 'fxmod (list refx refy)))))))
ctxt empty-env sc wd name moi))])
(define-inline 2 $top-level-value
[(x)
(nanopass-case (Lsrc Expr) (result-exp (value-visit-operand! x))
[(quote ,d)
(cond
[(and (symbol? d) (okay-to-handle?)
(assq ($target-machine) ($cte-optimization-info d))) =>
(lambda (as)
(let ([opt (cdr as)])
(nanopass-case (Lsrc Expr) opt
[(quote ,d)
(residualize-seq '() (list x) ctxt)
opt]
[,pr
(residualize-seq '() (list x) ctxt)
opt]
[(case-lambda ,preinfo ,cl* ...)
(context-case (app-ctxt ctxt)
[(test) (residualize-seq '() (list x) ctxt) true-rec]
; reprocess to complete inlining done in the same cp0 pass and, more
; importantly, to rewrite any prelexes so multiple call sites don't
; result in multiple bindings for the same prelexes
[(app) (residualize-seq '() (list x) ctxt)
(cp0 opt (app-ctxt ctxt) empty-env sc wd (app-name ctxt) moi)]
[else #f])]
[else #f])))]
[else #f])]
[else #f])])
(define-inline 2 $set-top-level-value!
[(x y) ; sets y's name to x if we know what symbol x is
(let ([x (result-exp (value-visit-operand! x))])
(nanopass-case (Lsrc Expr) x
[(quote ,d)
(when (symbol? d) (operand-name-set! y d))
#f]
[else #f]))])
(let ()
(define (get-prtd ?parent k)
(if ?parent
(nanopass-case (Lsrc Expr) (result-exp (value-visit-operand! ?parent))
[(record-type ,rtd ,e)
(and (not (record-type-sealed? rtd)) (k rtd))]
[(quote ,d)
(and (or (eq? d #f)
(and (record-type-descriptor? d)
(not (record-type-sealed? d))))
(k d))]
[else #f])
(k #f)))
(define (get-fields ?fields k)
(nanopass-case (Lsrc Expr) (result-exp (value-visit-operand! ?fields))
[(quote ,d) (k d)]
[else #f]))
(define (get-sealed x)
(nanopass-case (Lsrc Expr) (if x (result-exp (value-visit-operand! x)) false-rec)
[(quote ,d) (values (if d #t #f) ctrtd-sealed-known)]
[else (values #f 0)]))
; for opaque, it's a bit more complicated:
; no parent (parent #t) (parent #f) (parent ??)
; (child #t) #t #t #t #t
; (child #f) #f #t #f ??
; (child ??) ?? #t ?? ??
(define (get-opaque x prtd)
(if (and prtd (record-type-opaque? prtd))
(values #t ctrtd-opaque-known)
(nanopass-case (Lsrc Expr) (if x (result-exp (value-visit-operand! x)) false-rec)
[(quote ,d)
(if d
(values #t ctrtd-opaque-known)
(if (and (not d) (or (not prtd) (and (record-type-opaque-known? prtd) (not (record-type-opaque? prtd)))))
(values #f ctrtd-opaque-known)
(values #f 0)))]
[else (values #f 0)])))
(let ()
(define (mrt ?parent ?name ?fields maybe-?sealed maybe-?opaque ctxt level prim primname opnd*)
(or (nanopass-case (Lsrc Expr) (result-exp (value-visit-operand! ?name))
[(quote ,d)
(and (gensym? d)
(let ([objs (objs-if-constant (value-visit-operands! opnd*))])
(and objs
(let ([rtd (guard (c [#t #f]) (apply prim objs))])
(and rtd
(begin
(residualize-seq '() opnd* ctxt)
`(quote ,rtd)))))))]
[else #f])
(get-prtd ?parent
(lambda (prtd)
(get-fields ?fields
(lambda (fields)
(let-values ([(sealed? sealed-flag) (get-sealed maybe-?sealed)]
[(opaque? opaque-flag) (get-opaque maybe-?opaque prtd)])
(cond
[(guard (c [#t #f])
($make-record-type base-ctrtd prtd "tmp" fields
sealed? opaque? (fxlogor sealed-flag opaque-flag))) =>
(lambda (ctrtd)
(residualize-seq opnd* '() ctxt)
`(record-type ,ctrtd
,(build-primcall (app-preinfo ctxt) level primname
(value-visit-operands! opnd*))))]
[else #f]))))))))
(define-inline 2 make-record-type
[(?name ?fields)
(mrt #f ?name ?fields #f #f ctxt level make-record-type 'make-record-type
(list ?name ?fields))]
[(?parent ?name ?fields)
(mrt ?parent ?name ?fields #f #f ctxt level make-record-type 'make-record-type
(list ?parent ?name ?fields))])
(define-inline 2 $make-record-type
[(?base-id ?parent ?name ?fields ?sealed ?opaque . ?extras)
(mrt ?parent ?name ?fields ?sealed ?opaque ctxt level $make-record-type '$make-record-type
(list* ?base-id ?parent ?name ?fields ?sealed ?opaque ?extras))]))
(let ()
(define (mrtd ?parent ?uid ?fields ?sealed ?opaque ctxt level prim primname opnd*)
(or (nanopass-case (Lsrc Expr) (result-exp (value-visit-operand! ?uid))
[(quote ,d)
(and d
(let ([objs (objs-if-constant (value-visit-operands! opnd*))])
(and objs
(let ([rtd (guard (c [#t #f]) (apply prim objs))])
(and rtd
(begin
(residualize-seq '() opnd* ctxt)
`(quote ,rtd)))))))]
[else #f])
(get-prtd ?parent
(lambda (prtd)
(get-fields ?fields
(lambda (fields)
(let-values ([(sealed? sealed-flag) (get-sealed ?sealed)]
[(opaque? opaque-flag) (get-opaque ?opaque prtd)])
(cond
[(guard (c [#t #f])
($make-record-type-descriptor base-ctrtd 'tmp prtd #f
sealed? opaque? fields 'cp0 (fxlogor sealed-flag opaque-flag))) =>
(lambda (rtd)
(residualize-seq opnd* '() ctxt)
`(record-type ,rtd
; can't use level 3 unconditionally because we're missing checks for
; ?base-rtd, ?name, ?uid, ?who, and ?extras
,(build-primcall (app-preinfo ctxt) level primname
(value-visit-operands! opnd*))))]
[else #f]))))))))
(define-inline 2 make-record-type-descriptor
[(?name ?parent ?uid ?sealed ?opaque ?fields)
(mrtd ?parent ?uid ?fields ?sealed ?opaque ctxt level
make-record-type-descriptor 'make-record-type-descriptor
(list ?name ?parent ?uid ?sealed ?opaque ?fields))])
(define-inline 2 $make-record-type-descriptor
[(?base-rtd ?name ?parent ?uid ?sealed ?opaque ?fields ?who . ?extras)
(mrtd ?parent ?uid ?fields ?sealed ?opaque ctxt level
$make-record-type-descriptor '$make-record-type-descriptor
(list* ?base-rtd ?name ?parent ?uid ?sealed ?opaque ?fields ?who ?extras))])))
(let ()
; if you update this, also update duplicate in record.ss
(define-record-type rcd
(fields (immutable rtd) (immutable prcd) (immutable protocol))
(nongenerative #{rcd qh0yzh5qyrxmz2l-a})
(sealed #t))
(define-record-type ctrcd
(fields (immutable rtd) (immutable ctprcd) (immutable protocol-expr))
(nongenerative)
(sealed #t))
(define (rcd->ctrcd rcd)
(make-ctrcd (rcd-rtd rcd)
(let ([prcd (rcd-prcd rcd)]) (and prcd (rcd->ctrcd prcd)))
`(quote ,(rcd-protocol rcd))))
(define finish
(lambda (ctxt sc wd moi expr)
(and expr
; in app context, keep the inlining ball rolling.
(context-case (app-ctxt ctxt)
[(app) (cp0 expr (app-ctxt ctxt) empty-env sc wd (app-name ctxt) moi)]
[else expr]))))
(let ()
(define (get-rtd ?rtd k)
(let ([expr (result-exp (value-visit-operand! ?rtd))])
(nanopass-case (Lsrc Expr) expr
[(quote ,d)
(and (record-type-descriptor? d)
(eqv? (rtd-pm d) -1) ; all ptrs
(k d expr))]
[(record-type ,rtd (ref ,maybe-src ,x))
(and (eqv? (rtd-pm rtd) -1) ; all ptrs
(k rtd `(ref ,maybe-src ,x)))]
[else #f])))
(define (get-prcd ?prcd rtd k)
(nanopass-case (Lsrc Expr) (result-exp (value-visit-operand! ?prcd))
[(record-cd ,rcd ,rtd-expr ,e)
(and (eq? (ctrcd-rtd rcd) (record-type-parent rtd))
(nanopass-case (Lsrc Expr) (ctrcd-protocol-expr rcd)
[(ref ,maybe-src ,x) #t]
[(quote ,d) (or (eq? d #f) (procedure? d))]
[else #f])
(k rcd))]
[(quote ,d)
(if (eq? d #f)
(k #f)
(and (record-constructor-descriptor? d)
(eq? (rcd-rtd d) (record-type-parent rtd))
(k (rcd->ctrcd d))))]
[else #f]))
; record-cd form contains:
; - compile-time rcd
; - expression to access run-time rtd (quote or ref)
; - expression to create run-time rcd (primcall)
; compile-time rcd contains:
; - compile- or run-time rtd
; - compile-time parent rcd or #f
; - protocol expression (quote or ref)
(define (mrcd ?rtd ?prcd ?protocol ctxt sc wd name moi level prim primname opnd*)
(or (let ([objs (objs-if-constant (value-visit-operands! opnd*))])
(and objs
(let ([rcd (guard (c [#t #f]) (apply prim objs))])
(and rcd
(begin
(residualize-seq '() opnd* ctxt)
`(quote ,rcd))))))
(get-rtd ?rtd
(lambda (rtd rtd-expr)
(get-prcd ?prcd rtd
(lambda (pctrcd)
(define (opnd-lambda? opnd)
(and opnd
(nanopass-case (Lsrc Expr) (if (operand-value opnd)
(result-exp (operand-value opnd))
(operand-exp opnd))
[(case-lambda ,preinfo ,cl* ...) #t]
[(seq (profile ,src) (case-lambda ,preinfo ,cl* ...)) #t]
[else #f])))
(let* ([whole-protocol-expr (value-visit-operand! ?protocol)]
[result-protocol-expr (result-exp whole-protocol-expr)])
(cond
[(nanopass-case (Lsrc Expr) result-protocol-expr
[(quote ,d) (and (or (eq? d #f) (procedure? d)) 3)]
[(ref ,maybe-src ,x)
(and (not (prelex-was-assigned x))
(if (opnd-lambda? (prelex-operand x)) 3 level))]
[else #f]) =>
(lambda (level)
(residualize-seq opnd* '() ctxt)
`(record-cd
,(make-ctrcd rtd pctrcd result-protocol-expr)
,rtd-expr
,(build-primcall (app-preinfo ctxt) level primname
(value-visit-operands! opnd*))))]
[(nanopass-case (Lsrc Expr) result-protocol-expr
[(case-lambda ,preinfo ,cl* ...) #t]
[else #f])
; if the protocol expression is a lambda expression, we
; pull it out into an enclosing let, which can then be
; assimilated outward, by value-visit-operand!, into
; the form binding a variable to the rcd, if any, making
; it visible and available for inlining wherever the rcd
; is used.
(residualize-seq opnd* '() ctxt)
(let ([t (cp0-make-temp #t)])
(build-let (list t) (list whole-protocol-expr)
`(record-cd
,(make-ctrcd rtd pctrcd (build-ref t))
,rtd-expr
,(build-primcall (app-preinfo ctxt) 3 primname
(map
(lambda (opnd) (if (eq? opnd ?protocol) (build-ref t) (value-visit-operand! opnd)))
opnd*)))))]
[else #f]))))))))
(define-inline 2 make-record-constructor-descriptor
[(?rtd ?prcd ?protocol)
(mrcd ?rtd ?prcd ?protocol ctxt sc wd name moi level
make-record-constructor-descriptor 'make-record-constructor-descriptor
(list ?rtd ?prcd ?protocol))])
(define-inline 2 $make-record-constructor-descriptor
[(?rtd ?prcd ?protocol ?who)
(mrcd ?rtd ?prcd ?protocol ctxt sc wd name moi level
$make-record-constructor-descriptor '$make-record-constructor-descriptor
(list ?rtd ?prcd ?protocol ?who))]))
(let ()
(define (get-rtd ?rtd k1 k2)
(let ([expr (result-exp (value-visit-operand! ?rtd))])
(nanopass-case (Lsrc Expr) expr
[(quote ,d) (and (record-type-descriptor? d) (k1 d expr))]
[(record-type ,rtd ,e)
(nanopass-case (Lsrc Expr) e
[(ref ,maybe-src ,x) (k1 rtd e)]
[else (k2 rtd e)])]
[else #f])))
(define-inline 2 record-predicate
[(?rtd)
(and likely-to-be-compiled?
(get-rtd ?rtd
; k1: no let needed
(lambda (rtd rtd-expr)
(residualize-seq '() (list ?rtd) ctxt)
(finish ctxt sc wd moi
(let ([t (cp0-make-temp #f)])
(build-lambda (preinfo-call->preinfo-lambda (app-preinfo ctxt)) (list t)
(build-primcall 3
(if (record-type-sealed? rtd) '$sealed-record? 'record?)
(list (build-ref t) rtd-expr))))))
; k2: let needed
(lambda (rtd rtd-expr)
(residualize-seq (list ?rtd) '() ctxt)
(finish ctxt sc wd moi
(let ([rtd-t (cp0-make-temp #f)] [t (cp0-make-temp #f)])
(build-let (list rtd-t) (list (operand-value ?rtd))
(build-lambda (preinfo-call->preinfo-lambda (app-preinfo ctxt)) (list t)
(build-primcall 3
(if (record-type-sealed? rtd) '$sealed-record? 'record?)
(list (build-ref t) (build-ref rtd-t))))))))))]))
(let ()
(define type->pred
(lambda (who real-type val-t)
(define-syntax pred
(lambda (x)
(syntax-case x ()
[(_ type bytes pred)
(if (memq (datum type) '(scheme-object boolean))
#'($oops who "unexpected type ~s" 'type)
#'(build-primcall 3 'pred
(list (build-ref val-t))))])))
(record-datatype cases real-type pred
($oops who "unrecognized type ~s" real-type))))
(let ()
(define (go safe? rtd rtd-e ctxt)
(let* ([fld* (rtd-flds rtd)]
[t* (map (lambda (x) (cp0-make-temp #t)) fld*)]
[check* (if safe?
(fold-right
(lambda (fld t check*)
(let* ([type (fld-type fld)]
[real-type (filter-foreign-type type)])
(if (memq real-type '(scheme-object boolean))
check*
(cons
`(if ,(type->pred 'record-constructor real-type t)
,void-rec
,(build-primcall 3 'assertion-violationf
(list `(moi)
`(quote ,(format "invalid value ~~s for foreign type ~s" type))
(build-ref t))))
check*))))
'() fld* t*)
'())])
(build-lambda (preinfo-call->preinfo-lambda (app-preinfo ctxt)) t*
(let ([expr `(record ,rtd ,rtd-e ,(map build-ref t*) ...)])
(if (null? check*)
expr
(make-seq 'value (make-seq* 'effect check*) expr))))))
(let ()
; When the record type is a base type, r6rs:record-constructor produces:
; (prot ; protocol
; (lambda (X1 ... Xn) ; n = # fields
; (record rtd X1 ... Xn)))
; This presents no problems for the inliner. When the record type is a not
; a base type (no parent), however, it produces:
; (cprot ; child protocol
; (lambda pp-args ; number of pp-args is unknown, hence rest interface ...
; (lambda (C1 ... Cc) ; c = #child fields
; (apply ; ... and apply
; (pprot
; (lambda (P1 ... Pp) ; p = #parent fields
; (record rtd P1 ... Pp C1 ... Cc)))
; pp-args))))
; with the inner part replicated for the grandparent, great-grandparent, etc.
;
; We could try to analyze pprot to figure out how many arguments the
; procedure returned by pprot takes. We might not be able to do so, and it
; might turn out it's a case-lambda with several interfaces. Even if we do
; determine the exact number(s) of arguments, the (lambda pp-args ---)
; procedure must still accept any number of arguments, since an
; argument-count error signaled by the (lambda pp-args ---) procedure would
; come too early. Similarly, we could try to figure out how many arguments
; cprot passes to the (lambda pp-args ---) procedure, but this would also be
; difficult and possibly not helpful. Instead, we mark pp-args as
; containing an immutable value. If (as is typical) the call to (lambda
; pp-args procedure) becomes evident during inlining, the operand of pp-args
; becomes an "immutable list" record (in find-call-lambda-clause). If (as
; again is typical) the apply of the procedure returned by pprot also
; becomes evident during inlining, it is expanded as usual into a series of
; car/cdr calls, which are folded when car and cdr see that the argument is
; an immutable list record.
(define (try-rcd level ?rcd ctxt sc wd name moi)
(define (get-rcd ?rcd k)
(nanopass-case (Lsrc Expr) (result-exp (value-visit-operand! ?rcd))
[(record-cd ,rcd ,rtd-expr ,e) (k rcd rtd-expr)]
[(quote ,d)
(and (record-constructor-descriptor? d)
(k (rcd->ctrcd d) `(quote ,(rcd-rtd d))))]
[else #f]))
(get-rcd ?rcd
(lambda (ctrcd rtd-e)
; Convert call to r6rs:record-constructor into a call to a lambda
; expression that ignores its argument...this causes the code we
; generate to be processed before it is set up as a potential
; operand value for inlining. In particular, if the protocol
; expr is a variable bound to a procedure, this allows the protocol
; call we generate to be inlined, exposing the lambda expression
; within it for use in inlining calls to the resulting constructor.
(cp0
(build-lambda (list (make-prelex*)) ; unreferenced temp
(let ([rtd (ctrcd-rtd ctrcd)]
[protocol-expr (ctrcd-protocol-expr ctrcd)])
(if (cp0-constant? (lambda (x) (eq? x #f)) protocol-expr)
(go (< level 3) rtd rtd-e ctxt)
`(call ,(app-preinfo ctxt) ,protocol-expr
,(cond
[(record-type-parent rtd) =>
(lambda (prtd)
(let f ([ctprcd (ctrcd-ctprcd ctrcd)] [crtd rtd] [prtd prtd] [vars '()])
(let ([pp-args (cp0-make-temp #f)]
[new-vars (map (lambda (x) (cp0-make-temp #f))
(vector->list (record-type-field-names crtd)))])
(set-prelex-immutable-value! pp-args #t)
`(case-lambda ,(make-preinfo-lambda)
(clause (,pp-args) -1
,(build-lambda new-vars
(let ([vars (append new-vars vars)])
(build-primcall level 'apply
(list
(cond
[(and ctprcd
(let ([protocol-expr (ctrcd-protocol-expr ctprcd)])
(and (not (cp0-constant?
(lambda (x) (eq? x #f))
protocol-expr))
protocol-expr))) =>
(lambda (protocol-expr)
`(call ,(app-preinfo ctxt) ,protocol-expr
,(cond
[(rtd-parent prtd) =>
(lambda (pprtd)
(f (ctrcd-ctprcd ctprcd) prtd pprtd vars))]
[else
(let ([new-vars (map (lambda (x) (cp0-make-temp #f))
(csv7:record-type-field-names prtd))])
(build-lambda new-vars
`(call ,(app-preinfo ctxt) ,(go (< level 3) rtd rtd-e ctxt)
,(map build-ref (append new-vars vars))
...)))])))]
[else
(let ([new-vars (map (lambda (x) (cp0-make-temp #f))
(csv7:record-type-field-names prtd))])
(build-lambda new-vars
`(call ,(app-preinfo ctxt) ,(go (< level 3) rtd rtd-e ctxt)
,(map build-ref (append new-vars vars)) ...)))])
(build-ref pp-args))))))))))]
[else (go (< level 3) rtd rtd-e ctxt)])))))
ctxt empty-env sc wd name moi))))
(define-inline 2 record-constructor
[(?rtd/rcd)
(and likely-to-be-compiled?
(cond
[(let ([x (result-exp (value-visit-operand! ?rtd/rcd))])
(nanopass-case (Lsrc Expr) x
[(quote ,d) (and (record-type-descriptor? d) (cons d x))]
[(record-type ,rtd (ref ,maybe-src ,x)) (cons rtd `(ref ,maybe-src ,x))]
[else #f])) =>
(lambda (rtd.rtd-e)
(residualize-seq '() (list ?rtd/rcd) ctxt)
(finish ctxt sc wd moi (go (< level 3) (car rtd.rtd-e) (cdr rtd.rtd-e) ctxt)))]
[(nanopass-case (Lsrc Expr) (result-exp (operand-value ?rtd/rcd))
[(record-type ,rtd ,e) rtd]
[else #f]) =>
(lambda (rtd)
(residualize-seq (list ?rtd/rcd) '() ctxt)
(let ([rtd-t (cp0-make-temp #f)])
(build-let (list rtd-t) (list (operand-value ?rtd/rcd))
(finish ctxt sc wd moi (go (< level 3) rtd (build-ref rtd-t) ctxt)))))]
[else (try-rcd level ?rtd/rcd ctxt sc wd name moi)]))])
(define-inline 2 r6rs:record-constructor
[(?rcd)
(and likely-to-be-compiled?
(try-rcd level ?rcd ctxt sc wd name moi))])))
(let ()
(define (find-fld ?field rtd-e rtd k)
(nanopass-case (Lsrc Expr) (result-exp (value-visit-operand! ?field))
[(quote ,d)
(cond
[(symbol? d)
; reverse order to check child's fields first
(let loop ([flds (reverse (rtd-flds rtd))] [index (length (rtd-flds rtd))])
(let ([index (fx- index 1)])
(and (not (null? flds))
(let ([fld (car flds)])
(if (eq? d (fld-name fld))
(k rtd-e rtd fld index)
(loop (cdr flds) index))))))]
[(fixnum? d)
(let ((flds (rtd-flds rtd)))
(and ($fxu< d (length flds))
(k rtd-e rtd (list-ref flds d) d)))]
[else #f])]
[else #f]))
(define (r6rs:find-fld ?field rtd-e rtd k)
(nanopass-case (Lsrc Expr) (result-exp (value-visit-operand! ?field))
[(quote ,d)
(let ([flds (rtd-flds rtd)] [prtd (rtd-parent rtd)])
(let ([index (if prtd (+ d (length (rtd-flds prtd))) d)])
(and ($fxu< index (length flds))
(k rtd-e rtd (list-ref flds index) index))))]
[else #f]))
(define (find-rtd-and-field ?rtd ?field find-fld k)
(let ([x (result-exp (value-visit-operand! ?rtd))])
(nanopass-case (Lsrc Expr) x
[(quote ,d)
(and (record-type-descriptor? d) (find-fld ?field x d k))]
[(record-type ,rtd ,e)
(find-fld ?field e rtd k)]
[else #f])))
(let ()
(define (rfa ?rtd ?field level ctxt find-fld)
(and likely-to-be-compiled?
(find-rtd-and-field ?rtd ?field find-fld
(lambda (rtd-e rtd fld index)
; assuming all fields are accessible
(let ([rec-t (cp0-make-temp #t)])
(let ([expr `(record-ref ,rtd ,(fld-type fld) ,index (ref #f ,rec-t))])
(cond
[(fx= level 3)
(residualize-seq '() (list ?rtd ?field) ctxt)
(build-lambda (preinfo-call->preinfo-lambda (app-preinfo ctxt)) (list rec-t) expr)]
[(nanopass-case (Lsrc Expr) rtd-e
[(quote ,d) #t]
[(ref ,maybe-src ,x) #t]
[else #f])
(residualize-seq '() (list ?rtd ?field) ctxt)
(build-lambda (preinfo-call->preinfo-lambda (app-preinfo ctxt)) (list rec-t)
`(seq
(if ,(build-primcall 3 'record?
(list (build-ref rec-t) rtd-e))
,void-rec
,(build-primcall 3 '$record-oops
(list (let ([name (app-name ctxt)])
(if name `(quote ,name) `(moi)))
(build-ref rec-t)
rtd-e)))
,expr))]
[else
(let ([rtd-t (cp0-make-temp #t)])
(residualize-seq (list ?rtd) (list ?field) ctxt)
(build-let (list rtd-t) (list (operand-value ?rtd))
(build-lambda (preinfo-call->preinfo-lambda (app-preinfo ctxt)) (list rec-t)
`(seq
(if ,(build-primcall 3 'record?
(list (build-ref rec-t) (build-ref rtd-t)))
,void-rec
,(build-primcall 3 '$record-oops
(list (let ([name (app-name ctxt)])
(if name `(quote ,name) `(moi)))
(build-ref rec-t)
(build-ref rtd-t))))
,expr))))])))))))
(define-inline 2 csv7:record-field-accessor
[(?rtd ?field) (finish ctxt sc wd moi (rfa ?rtd ?field level ctxt find-fld))])
(define-inline 2 record-accessor
[(?rtd ?field) (finish ctxt sc wd moi (rfa ?rtd ?field level ctxt r6rs:find-fld))]))
(let ()
(define (rfm ?rtd ?field level ctxt who find-fld)
(and likely-to-be-compiled?
(find-rtd-and-field ?rtd ?field find-fld
(lambda (rtd-e rtd fld index)
(and (fld-mutable? fld)
(let* ([type (fld-type fld)]
[real-type (filter-foreign-type type)]
[rec-t (cp0-make-temp #t)]
[val-t (cp0-make-temp #t)])
(let ([expr `(record-set! ,rtd ,type ,index (ref #f ,rec-t) (ref #f ,val-t))]
[pred (and (not (memq real-type '(scheme-object boolean)))
(type->pred who real-type val-t))])
(cond
[(fx= level 3)
(residualize-seq '() (list ?rtd ?field) ctxt)
(build-lambda (preinfo-call->preinfo-lambda (app-preinfo ctxt))
(list rec-t val-t)
expr)]
[(nanopass-case (Lsrc Expr) rtd-e
[(quote ,d) #t]
[(ref ,maybe-src ,x) #t]
[else #f])
(residualize-seq '() (list ?rtd ?field) ctxt)
(build-lambda (preinfo-call->preinfo-lambda (app-preinfo ctxt))
(list rec-t val-t)
(make-seq 'value
`(if ,(build-primcall 3 'record?
(list (build-ref rec-t) rtd-e))
,void-rec
,(build-primcall 3 '$record-oops
(list (let ([name (app-name ctxt)])
(if name `(quote ,name) `(moi)))
(build-ref rec-t)
rtd-e)))
(if pred
(make-seq 'value
`(if ,pred ,void-rec
,(build-primcall 3 'assertion-violationf
(list (let ([name (app-name ctxt)])
(if name `(quote ,name) `(moi)))
`(quote ,(format "invalid value ~~s for foreign type ~s" type))
(build-ref val-t))))
expr)
expr)))]
[else
(let ([rtd-t (cp0-make-temp #t)])
(residualize-seq (list ?rtd) (list ?field) ctxt)
(build-let (list rtd-t) (list (operand-value ?rtd))
(build-lambda (preinfo-call->preinfo-lambda (app-preinfo ctxt))
(list rec-t val-t)
(make-seq 'value
`(if ,(build-primcall 3 'record?
(list (build-ref rec-t) (build-ref rtd-t)))
,void-rec
,(build-primcall 3 '$record-oops
(list (let ([name (app-name ctxt)])
(if name `(quote ,name) `(moi)))
(build-ref rec-t)
(build-ref rtd-t))))
(if pred
(make-seq 'value
`(if ,pred ,void-rec
,(build-primcall 3 'assertion-violationf
(list (let ([name (app-name ctxt)])
(if name `(quote ,name) `(moi)))
`(quote ,(format "invalid value ~~s for foreign type ~s" type))
(build-ref val-t))))
expr)
expr)))))]))))))))
(define-inline 2 csv7:record-field-mutator
[(?rtd ?field) (finish ctxt sc wd moi (rfm ?rtd ?field level ctxt 'record-field-mutator find-fld))])
(define-inline 2 record-mutator
[(?rtd ?field) (finish ctxt sc wd moi (rfm ?rtd ?field level ctxt 'record-mutator r6rs:find-fld))]))
(define-inline 2 csv7:record-field-accessible?
[(?rtd ?field)
; always true, but first verify that rtd & field are valid to avoid suppressing run-time errors
(find-rtd-and-field ?rtd ?field find-fld
(lambda (rtd-e rtd fld index)
(residualize-seq '() (list ?rtd ?field) ctxt)
true-rec))])
(let ()
(define (rfm? ?rtd ?field ctxt find-fld)
(find-rtd-and-field ?rtd ?field find-fld
(lambda (rtd-e rtd fld index)
(residualize-seq '() (list ?rtd ?field) ctxt)
`(quote ,(fld-mutable? fld)))))
(define-inline 2 csv7:record-field-mutable?
[(?rtd ?field) (rfm? ?rtd ?field ctxt find-fld)])
(define-inline 2 record-field-mutable?
[(?rtd ?field) (rfm? ?rtd ?field ctxt r6rs:find-fld)]))))
)
(define-inline 2 (csv7:record-type-descriptor record-rtd)
[(?record)
(let ([x (value-visit-operand! ?record)])
(nanopass-case (Lsrc Expr) (result-exp/indirect-ref x)
; could handle record-type forms if ctrtd recorded rtdrtd (a ctrtd's rtd is always base-ctrtd)
[(record ,rtd ,rtd-expr ,e* ...)
(and (not (record-type-opaque? rtd))
(if (ctrtd? rtd)
(begin
(residualize-seq (list ?record) '() ctxt)
`(record-type ,rtd
,(build-primcall (app-preinfo ctxt) level prim-name
(list x))))
(begin
(residualize-seq '() (list ?record) ctxt)
`(quote ,rtd))))]
[(quote ,d)
(and (record? d)
(begin
(residualize-seq '() (list ?record) ctxt)
`(quote ,(record-rtd d))))]
[else #f]))])
(define-inline 2 record-type-descriptor?
[(?x)
(cond
[(nanopass-case (Lsrc Expr) (result-exp (value-visit-operand! ?x))
[(record-type ,rtd ,e) #t]
[(quote ,d) (record-type-descriptor? d)]
[else #f])
(residualize-seq '() (list ?x) ctxt)
true-rec]
[else #f])])
(define-inline 2 record-constructor-descriptor?
[(?x)
(cond
[(nanopass-case (Lsrc Expr) (result-exp (value-visit-operand! ?x))
[(record-cd ,rcd ,rtd-expr ,e) #t]
[(quote ,d) (record-constructor-descriptor? d)]
[else #f])
(residualize-seq '() (list ?x) ctxt)
true-rec]
[else #f])])
(define-inline 2 record-type-sealed?
[(?rtd)
(nanopass-case (Lsrc Expr) (result-exp (value-visit-operand! ?rtd))
[(record-type ,rtd ,e)
(and (record-type-sealed-known? rtd)
(begin
(residualize-seq '() (list ?rtd) ctxt)
(if (record-type-sealed? rtd) true-rec false-rec)))]
[(quote ,d)
(and (record-type-descriptor? d)
(begin
(residualize-seq '() (list ?rtd) ctxt)
`(quote ,(record-type-sealed? d))))]
[else #f])])
(define-inline 2 record-type-opaque?
[(?rtd)
(nanopass-case (Lsrc Expr) (result-exp (value-visit-operand! ?rtd))
[(record-type ,rtd ,e)
(and (record-type-opaque-known? rtd)
(begin
(residualize-seq '() (list ?rtd) ctxt)
(if (record-type-opaque? rtd) true-rec false-rec)))]
[(quote ,d)
(and (record-type-descriptor? d)
(begin
(residualize-seq '() (list ?rtd) ctxt)
`(quote ,(record-type-opaque? d))))]
[else #f])])
(let ()
(define definitely-not-a-record?
(lambda (xres)
(nanopass-case (Lsrc Expr) xres
[(case-lambda ,preinfo ,cl ...) #t]
[,pr (all-set? (prim-mask proc) (primref-flags pr))]
[(foreign (,conv* ...) ,name ,e (,arg-type* ...) ,result-type) #t]
[(fcallable (,conv* ...) ,e (,arg-type* ...) ,result-type) #t]
[(record-set! ,rtd ,type ,index ,e1 ,e2) #t]
[(immutable-list (,e* ...) ,e) #t]
[else #f])))
(define one-arg-case
(lambda (?x ctxt)
(let ([xres (result-exp/indirect-ref (value-visit-operand! ?x))])
(nanopass-case (Lsrc Expr) xres
[(quote ,d)
(residualize-seq '() (list ?x) ctxt)
(if (record? d) true-rec false-rec)]
; could handle record-type forms if ctrtd recorded rtdrtd so we can check opacity (a ctrtd's rtd is always base-ctrtd)
[(record ,rtd ,rtd-expr ,e* ...)
(and (record-type-opaque-known? rtd)
(begin
(residualize-seq '() (list ?x) ctxt)
(if (record-type-opaque? rtd) false-rec true-rec)))]
[else (and (definitely-not-a-record? xres)
(begin
(residualize-seq '() (list ?x) ctxt)
false-rec))]))))
(define-inline 2 r6rs:record?
[(?x) (one-arg-case ?x ctxt)])
(define-inline 2 record?
[(?x) (one-arg-case ?x ctxt)]
[(?x ?rtd)
(let ([rtdval (value-visit-operand! ?rtd)])
(define abandon-ship
(lambda (xval xres maybe-rtd)
(if (definitely-not-a-record? xres)
(begin
(residualize-seq '() (list ?x ?rtd) ctxt)
false-rec)
(and maybe-rtd
(begin
(residualize-seq (list ?x ?rtd) '() ctxt)
(build-primcall (app-preinfo ctxt) 3
(if (record-type-sealed? maybe-rtd)
'$sealed-record?
'record?)
(list xval rtdval)))))))
(define obviously-incompatible?
(lambda (instance-rtd rtd)
(let f ([ls1 (rtd-flds instance-rtd)] [ls2 (rtd-flds rtd)])
(if (null? ls2)
(if (record-type-parent instance-rtd)
; could work harder here, though it gets trickier (so not obvious)...
#f
; instance has no parent, so rtds are compatible only if they are the same modulo incomplete info if one or both are ctrtds
(or (not (null? ls1))
(and (record-type-parent rtd) #t)
(and (and (record-type-sealed-known? rtd) (record-type-sealed-known? instance-rtd))
(not (eq? (record-type-sealed? instance-rtd) (record-type-sealed? rtd))))
(and (and (record-type-opaque-known? rtd) (record-type-opaque-known? instance-rtd))
(not (eq? (record-type-opaque? instance-rtd) (record-type-opaque? rtd))))))
(or (null? ls1)
(not (equal? (car ls1) (car ls2)))
(f (cdr ls1) (cdr ls2)))))))
(nanopass-case (Lsrc Expr) (result-exp rtdval)
[(quote ,d0)
(and (record-type-descriptor? d0)
(let ([xval (value-visit-operand! ?x)])
(let ([xres (result-exp/indirect-ref xval)])
(nanopass-case (Lsrc Expr) xres
[(quote ,d1)
; could also return #f here and let folding happen
(residualize-seq '() (list ?x ?rtd) ctxt)
(if (record? d1 d0) true-rec false-rec)]
; could handle record-type forms if ctrtd recorded rtdrtd (a ctrtd's rtd is always base-ctrtd)
[(record ,rtd ,rtd-expr ,e* ...)
(guard (let f ([rtd rtd])
(or (eq? rtd d0)
(let ([rtd (record-type-parent rtd)])
(and rtd (f rtd))))))
(residualize-seq '() (list ?x ?rtd) ctxt)
true-rec]
[else (abandon-ship xval xres d0)]))))]
[(record-type ,rtd ,e)
(cond
[(nanopass-case (Lsrc Expr) (result-exp/indirect-ref (value-visit-operand! ?x))
[(record ,rtd2 ,rtd-expr ,e* ...)
(let f ([rtd2 rtd2])
(or (eq? rtd2 rtd)
(let ([rtd2 (record-type-parent rtd2)])
(and rtd2 (f rtd2)))))]
[else #f])
(residualize-seq '() (list ?x ?rtd) ctxt)
true-rec]
[(nanopass-case (Lsrc Expr) (result-exp/indirect-ref (value-visit-operand! ?x))
[(quote ,d1)
(and (record? d1) (obviously-incompatible? (record-rtd d1) rtd))]
; could handle record-type forms if ctrtd recorded rtdrtd (a ctrtd's rtd is always base-ctrtd)
[(record ,rtd2 ,rtd-expr ,e* ...)
(obviously-incompatible? rtd2 rtd)]
[else #f])
(residualize-seq '() (list ?x ?rtd) ctxt)
false-rec]
[else
(let ([xval (value-visit-operand! ?x)])
(abandon-ship xval (result-exp/indirect-ref xval) rtd))])]
[else
(and (fx= level 3)
(let ([xval (value-visit-operand! ?x)])
(abandon-ship xval (result-exp/indirect-ref xval) #f)))]))]))
(define-inline 2 csv7:record-type-field-names
[(?rtd)
(cond
[(nanopass-case (Lsrc Expr) (result-exp (value-visit-operand! ?rtd))
[(record-type ,rtd ,e) rtd]
[(quote ,d) (and (record-type-descriptor? d) d)]
[else #f]) =>
(lambda (rtd)
(residualize-seq '() (list ?rtd) ctxt)
`(quote ,(csv7:record-type-field-names rtd)))]
[else #f])])
(define-inline 2 record-type-field-names
[(?rtd)
(cond
[(nanopass-case (Lsrc Expr) (result-exp (value-visit-operand! ?rtd))
[(record-type ,rtd ,e) rtd]
[(quote ,d) (and (record-type-descriptor? d) d)]
[else #f]) =>
(lambda (rtd)
(residualize-seq '() (list ?rtd) ctxt)
`(quote ,(record-type-field-names rtd)))]
[else #f])])
(define-inline 2 csv7:record-type-field-decls
[(?rtd)
(cond
[(nanopass-case (Lsrc Expr) (result-exp (value-visit-operand! ?rtd))
[(record-type ,rtd ,e) rtd]
[(quote ,d) (and (record-type-descriptor? d) d)]
[else #f]) =>
(lambda (rtd)
(residualize-seq '() (list ?rtd) ctxt)
`(quote ,(csv7:record-type-field-decls rtd)))]
[else #f])])
(define-inline 2 csv7:record-type-name
; don't look for record-type case, since rtd may be a temporary
; rtd cons'd up by cp0
[(?rtd)
(nanopass-case (Lsrc Expr) (result-exp (value-visit-operand! ?rtd))
[(quote ,d)
(and (record-type-descriptor? d)
(begin
(residualize-seq '() (list ?rtd) ctxt)
`(quote ,(csv7:record-type-name d))))]
[else #f])])
(define-inline 2 record-type-name
; don't look for record-type case, since rtd may be a temporary
; rtd cons'd up by cp0
[(?rtd)
(nanopass-case (Lsrc Expr) (result-exp (value-visit-operand! ?rtd))
[(quote ,d)
(and (record-type-descriptor? d)
(begin
(residualize-seq '() (list ?rtd) ctxt)
`(quote ,(record-type-name d))))]
[else #f])])
(define-inline 2 record-type-parent
; don't look for record-type case, since parent may be a temporary
; rtd cons'd up by cp0
[(?rtd)
(nanopass-case (Lsrc Expr) (result-exp (value-visit-operand! ?rtd))
[(quote ,d)
(and (record-type-descriptor? d)
(begin
(residualize-seq '() (list ?rtd) ctxt)
`(quote ,(record-type-parent d))))]
[else #f])])
(define-inline 2 (csv7:record-type-symbol record-type-uid)
; don't look for record-type case, since rtd may be a temporary
; rtd cons'd up by cp0
[(?rtd)
(nanopass-case (Lsrc Expr) (result-exp (value-visit-operand! ?rtd))
[(quote ,d)
(and (record-type-descriptor? d)
(begin
(residualize-seq '() (list ?rtd) ctxt)
`(quote ,(record-type-uid d))))]
[else #f])])
(define-inline 2 $record
[(?rtd . ?e*)
(let ([rtd-expr (value-visit-operand! ?rtd)])
(nanopass-case (Lsrc Expr) (result-exp rtd-expr)
[(quote ,d)
(and (record-type-descriptor? d)
(if (andmap (lambda (fld) (not (fld-mutable? fld))) (rtd-flds d))
(let ([e* (objs-if-constant (value-visit-operands! ?e*))])
(and e*
(begin
(residualize-seq '() (cons ?rtd ?e*) ctxt)
`(quote ,(apply $record d e*)))))
(begin
(residualize-seq (cons ?rtd ?e*) '() ctxt)
`(record ,d ,rtd-expr ,(map value-visit-operand! ?e*) ...))))]
[(record-type ,rtd ,e)
(begin
(residualize-seq (cons ?rtd ?e*) '() ctxt)
`(record ,rtd ,rtd-expr ,(map value-visit-operand! ?e*) ...))]
[else #f]))])
(let ()
(define null-rec?
(lambda (?ls)
(nanopass-case (Lsrc Expr) (result-exp (value-visit-operand! ?ls))
[(quote ,d) (null? d)]
[(call ,preinfo ,e ,e* ...)
; check also for `(list)`. It should have been reduced to `(quote ())` before,
; but cp0 isn't guaranteed to reach a fixed point.
(and (primref? e) (eq? (primref-name e) 'list) (null? e*))]
[else #f])))
(define inline-lists
(lambda (?p ?ls ?ls* lvl map? ctxt sc wd name moi)
; (map/for-each proc (list a11 a12 ... a1m) (list a21 a22 ... a2m) ... (list an1 an2 ... anm)) =>
; (let ([p proc])
; (if (procedure? p)
; (void)
; ($oops 'map/for-each "~s is not a procedure" p))
; (let ([t11 a11] ... [t1m a1m])
; ...
; (let ([tn1 an1] ... [tnm anm])
; (list/begin (p t11 ... tn1)
; (p t12 ... tn2)
; ...
; (p t1m ... tnm)))))
(let loop ([ls* (cons ?ls ?ls*)] [e** '()] [all-quoted? #t])
(if (null? ls*)
(and (apply = (map length e**))
(or (not all-quoted?) (fx<= (length (car e**)) 4))
(let ([p (cp0-make-temp (or (fx= lvl 2) (fx> (length (car e**)) 1)))]
[temp** (map (lambda (e*)
(map (lambda (x) (cp0-make-temp #f)) e*))
e**)])
(residualize-seq (list* ?p ?ls ?ls*) '() ctxt)
(build-let (list p) (list (value-visit-operand! ?p))
(let ([main
(let f ([t** temp**] [e** (reverse e**)] [ls* (cons ?ls ?ls*)])
(if (null? t**)
(let ([results
(let ([preinfo (app-preinfo ctxt)])
(let g ([t** temp**])
(if (null? (car t**))
'()
(cons `(call ,preinfo (ref #f ,p)
,(map (lambda (t*) (build-ref (car t*))) t**) ...)
(g (map cdr t**))))))])
(if (and map? (not (eq? (app-ctxt ctxt) 'effect)))
(if (null? results)
null-rec
(build-primcall lvl 'list results))
(if (null? results)
void-rec
(make-seq* (app-ctxt ctxt) results))))
(non-result-exp (value-visit-operand! (car ls*))
(build-let (car t**) (car e**)
(f (cdr t**) (cdr e**) (cdr ls*))))))])
(if (fx= lvl 2)
(make-seq (app-ctxt ctxt)
`(if ,(build-primcall 2 'procedure? (list `(ref #f ,p)))
,void-rec
,(build-primcall 3 '$oops (list `(quote ,(if map? 'map 'for-each))
`(quote "~s is not a procedure")
`(ref #f ,p))))
main)
main)))))
(nanopass-case (Lsrc Expr) (result-exp (value-visit-operand! (car ls*)))
[(quote ,d)
(and (list? d) (loop (cdr ls*) (cons (map (lambda (x) `(quote ,x)) d) e**) all-quoted?))]
[(call ,preinfo ,e ,e* ...)
(and (primref? e) (eq? (primref-name e) 'list) (loop (cdr ls*) (cons e* e**) #f))]
[else #f])))))
(define-inline 2 map
[(?p ?ls . ?ls*)
(inline-lists ?p ?ls ?ls* 2 #t ctxt sc wd name moi)])
(define-inline 3 map
[(?p ?ls . ?ls*)
(cond
[(ormap null-rec? (cons ?ls ?ls*))
(residualize-seq '() (list* ?p ?ls ?ls*) ctxt)
null-rec]
; could treat map in effect context as for-each, but don't because (our)
; map is guaranteed (even at optimization level 3) not to get sick if an
; input list is mutated, while for-each is not.
[(and (eq? (app-ctxt ctxt) 'effect)
(nanopass-case (Lsrc Expr) (result-exp (value-visit-operand! ?p))
[,pr (let ([flags (primref-flags pr)])
(and (if (all-set? (prim-mask unsafe) flags)
(all-set? (prim-mask discard) flags)
(all-set? (prim-mask (or discard unrestricted)) flags))
(arity-okay? (primref-arity pr) (fx+ (length ?ls*) 1))))]
[else #f]))
; discard effect-free calls to map in effect context
(residualize-seq '() (list* ?p ?ls ?ls*) ctxt)
void-rec]
[(inline-lists ?p ?ls ?ls* 3 #t ctxt sc wd name moi)]
[(ormap (lambda (?ls)
(nanopass-case (Lsrc Expr) (result-exp (value-visit-operand! ?ls))
[(quote ,d)
(and (list? d) (let ([n (length d)]) (and (fx<= n 4) n)))]
[(call ,preinfo ,e ,e* ...)
(and (primref? e) (eq? (primref-name e) 'list) (let ([n (length e*)]) (and (fx<= n 4) n)))]
[else #f]))
(cons ?ls ?ls*)) =>
(lambda (n)
(safe-assert (not (= n 0))) ; guaranteed before we get here
; (map proc e1 ... (begin e2 ... '(a b c d)) e3 ...) =>
; ((lambda (p ls ...)
; ; do all cdrs first to avoid mutation sickness
; (let ([t1 (cdr ls)] ...)
; (let ([t2 (cdr t1)] ...)
; (let ([t3 (cdr t2)] ...)
; (list (p (car ls) ...)
; (p (car t1) ...)
; (p (car t2) ...)
; (p (car t3) ...))))))
; proc e1 ... (begin e2 ... '(a b c d)) e3 ...)
(cp0
(let ([p (cp0-make-temp (fx> n 1))]
[ls* (cons (cp0-make-temp #t)
(map (lambda (x) (cp0-make-temp #t)) ?ls*))])
(build-lambda (cons p ls*)
(let f ([n n] [ls* ls*] [ropnd* '()])
(if (fx= n 1)
(let ([opnd*
(reverse
(cons
`(call ,(app-preinfo ctxt) (ref #f ,p)
,(map (lambda (x)
(build-primcall 3 'car
(list (build-ref x))))
ls*) ...)
ropnd*))])
(if (eq? ctxt 'effect)
(make-seq* ctxt opnd*)
(build-primcall 3 'list opnd*)))
(let ([tls* (map (lambda (x) (cp0-make-temp #t)) ls*)])
(build-let tls*
(map (lambda (x)
(build-primcall 3 'cdr
(list (build-ref x))))
ls*)
(f (fx- n 1) tls*
(cons `(call ,(app-preinfo ctxt) (ref #f ,p)
,(map (lambda (x)
(build-primcall 3 'car (list (build-ref x))))
ls*) ...)
ropnd*))))))))
ctxt empty-env sc wd name moi))]
[else #f])])
(define-inline 2 for-each
[(?p ?ls . ?ls*)
(inline-lists ?p ?ls ?ls* 2 #f ctxt sc wd name moi)])
(define-inline 3 for-each
[(?p ?ls . ?ls*)
(cond
[(ormap null-rec? (cons ?ls ?ls*))
; (for-each proc e1 ... (begin e2 ... '()) e3 ...) =>
; (begin e1 ... (begin e2 ... '()) e3 ... (void))
(begin
(residualize-seq '() (list* ?p ?ls ?ls*) ctxt)
void-rec)]
[(nanopass-case (Lsrc Expr) (result-exp (value-visit-operand! ?p))
[,pr (let ([flags (primref-flags pr)])
(and (if (all-set? (prim-mask unsafe) flags)
(all-set? (prim-mask discard) flags)
(all-set? (prim-mask (or discard unrestricted)) flags))
(arity-okay? (primref-arity pr) (fx+ (length ?ls*) 1))))]
[else #f])
(residualize-seq '() (list* ?p ?ls ?ls*) ctxt)
void-rec]
[(inline-lists ?p ?ls ?ls* 3 #f ctxt sc wd name moi)]
[(ormap (lambda (?ls)
(nanopass-case (Lsrc Expr) (result-exp (value-visit-operand! ?ls))
[(quote ,d)
(and (list? d) (let ([n (length d)]) (and (fx<= n 4) n)))]
[(call ,preinfo ,e ,e* ...)
(and (primref? e) (eq? (primref-name e) 'list) (let ([n (length e*)]) (and (fx<= n 4) n)))]
[else #f]))
(cons ?ls ?ls*)) =>
(lambda (n)
(safe-assert (not (= n 0))) ; guaranteed before we get here
; (for-each proc e1 ... (begin e2 ... '(a b c d)) e3 ...)
; ((lambda (p ls ...)
; (proc (car ls) ...)
; (let ([t1 (cdr ls)] ...)
; (proc (car t1) ...)
; (let ([t2 (cdr t1)] ...)
; (proc (car t2) ...)
; (proc (cadr t2) ...))))
; proc e1 ... (begin e2 ... '(a b c d)) e3 ...)
(cp0
(let ([p (cp0-make-temp (fx> n 1))]
[ls* (cons (cp0-make-temp #t)
(map (lambda (x) (cp0-make-temp #t)) ?ls*))])
(build-lambda (cons p ls*)
(cond
[(fx= n 1)
`(call ,(app-preinfo ctxt) (ref #f ,p)
,(map (lambda (x)
(build-primcall 3 'car (list (build-ref x))))
ls*) ...)]
[else
(let f ([n n] [ls* ls*])
(if (fx= n 2)
(make-seq 'value
`(call ,(app-preinfo ctxt) (ref #f ,p)
,(map (lambda (x)
(build-primcall 3 'car (list (build-ref x))))
ls*) ...)
`(call ,(app-preinfo ctxt) (ref #f ,p)
,(map (lambda (x)
(build-primcall 3 'cadr (list (build-ref x))))
ls*) ...))
(make-seq 'value
`(call ,(app-preinfo ctxt) (ref #f ,p)
,(map (lambda (x)
(build-primcall 3 'car (list (build-ref x))))
ls*) ...)
(let ([tls* (map (lambda (x) (cp0-make-temp #t)) ls*)])
(build-let tls*
(map (lambda (x)
(build-primcall 3 'cdr (list (build-ref x))))
ls*)
(f (fx- n 1) tls*))))))])))
ctxt empty-env sc wd name moi))]
[else
(and likely-to-be-compiled?
(cp0
(let ([?ls* (cons ?ls ?ls*)])
(let ([p (cp0-make-temp #t)]
[r (cp0-make-temp #t)]
[do (cp0-make-temp #t)]
[tls* (map (lambda (x) (cp0-make-temp #t)) ?ls*)]
[ls* (map (lambda (x) (cp0-make-temp #t)) ?ls*)])
(build-lambda (cons p tls*)
`(if ,(build-primcall 3 'null?
(list (build-ref (car tls*))))
,void-rec
,(build-named-let do ls*
(map build-ref tls*)
(build-let (list r)
(list (build-primcall 3 'cdr (list (build-ref (car ls*)))))
`(if ,(build-primcall 3 'null? (list (build-ref r)))
(call ,(app-preinfo ctxt) (ref #f ,p)
,(map (lambda (x)
(build-primcall 3 'car (list (build-ref x))))
ls*) ...)
,(make-seq 'value
`(call ,(app-preinfo ctxt) (ref #f ,p)
,(map (lambda (x)
(build-primcall 3 'car (list (build-ref x))))
ls*) ...)
`(call ,(make-preinfo) (ref #f ,do) (ref #f ,r)
,(map (lambda (x)
(build-primcall 3 'cdr (list (build-ref x))))
(cdr ls*)) ...)))))))))
ctxt empty-env sc wd name moi))])])
)
(define-inline 3 vector-map
[(?p ?v . ?v*)
(cond
[(eq? (app-ctxt ctxt) 'effect)
; treat vector-map in effect context as vector-for-each
(cp0 (lookup-primref 3 'vector-for-each) ctxt empty-env sc wd name moi)]
[(ormap (lambda (?v)
(nanopass-case (Lsrc Expr) (result-exp (value-visit-operand! ?v))
[(quote ,d)
(and (vector? d)
(let ([n (vector-length d)]) (and (fx<= n 4) n)))]
[else #f]))
(cons ?v ?v*)) =>
(lambda (n)
(cond
[(fx= n 0)
; (vector-map proc e1 ... (begin e2 ... '#()) e3 ...) =>
; (begin proc e1 ... (begin e2 ...'#()) e3 ... '#())
(residualize-seq '() (list* ?p ?v ?v*) ctxt)
`(quote #())]
[else
; (vector-map proc (begin e1 ... '#(a b c d)) e2 ...)
; ((lambda (p v1 v2 ...)
; (vector (proc 'a (vector-ref v2 0) ...)
; (proc 'b (vector-ref v2 1) ...)
; (proc 'c (vector-ref v2 2) ...)
; (proc 'd (vector-ref v2 3) ...)))
; proc (begin e1 ... '#(a b c d)) e2 ...)
(cp0
(let ([p (cp0-make-temp (fx> n 1))]
[v* (cons (cp0-make-temp #t)
(map (lambda (x) (cp0-make-temp #t)) ?v*))])
(build-lambda (cons p v*)
(build-primcall 3 'vector
(map (lambda (i)
`(call ,(app-preinfo ctxt) (ref #f ,p)
,(map (lambda (x)
(build-primcall 3 'vector-ref
(list (build-ref x) `(quote ,i))))
v*) ...))
(iota n)))))
ctxt empty-env sc wd name moi)]))]
[else #f])])
(define-inline 3 vector-for-each
[(?p ?v . ?v*)
(cond
[(nanopass-case (Lsrc Expr) (result-exp (value-visit-operand! ?p))
[,pr (all-set? (prim-mask discard) (primref-flags pr))]
[else #f])
(residualize-seq '() (list* ?p ?v ?v*) ctxt)
void-rec]
[(ormap (lambda (?v)
(nanopass-case (Lsrc Expr) (result-exp (value-visit-operand! ?v))
[(quote ,d)
(and (vector? d)
(let ([n (vector-length d)]) (and (fx<= n 4) n)))]
[else #f]))
(cons ?v ?v*)) =>
(lambda (n)
(cond
[(fx= n 0)
; (for-each proc (begin e1 ... '()) e2 ...) =>
; (begin (begin e1 ... '()) e2 ... (void))
(residualize-seq '() (list* ?p ?v ?v*) ctxt)
void-rec]
[else
; (for-each proc (begin e1 ... '#(a b c d)) e2 ...)
; ((lambda (p ls1 ls2 ...)
; (proc 'a (vector-ref ls2 0) ...)
; (proc 'b (vector-ref ls2 1) ...)
; (proc 'c (vector-ref ls2 2) ...)
; (proc 'd (vector-ref ls2 3) ...))
; proc (begin e1 ... '(a b c d)) e2 ...)
(cp0
(let ([p (cp0-make-temp (fx> n 1))]
[v* (cons (cp0-make-temp #t)
(map (lambda (x) (cp0-make-temp #t)) ?v*))])
(build-lambda (cons p v*)
(make-seq* 'value
(map (lambda (i)
`(call ,(app-preinfo ctxt) (ref #f ,p)
,(map (lambda (x)
(build-primcall 3 'vector-ref
(list (build-ref x) `(quote ,i))))
v*) ...))
(iota n)))))
ctxt empty-env sc wd name moi)]))]
[else
(and likely-to-be-compiled?
(cp0
(let ([p (cp0-make-temp #t)]
[n (cp0-make-temp #t)]
[i (cp0-make-temp #t)]
[j (cp0-make-temp #t)]
[do (cp0-make-temp #t)]
[v (cp0-make-temp #t)]
[v* (map (lambda (x) (cp0-make-temp #f)) ?v*)])
(build-lambda (cons* p v v*)
(build-let (list n) (list (build-primcall 3 'vector-length (list (build-ref v))))
`(if ,(build-primcall 3 'fx= (list (build-ref n) `(quote 0)))
,void-rec
,(build-named-let do (list i) (list `(quote 0))
(build-let (list j) (list (build-primcall 3 'fx1+ (list (build-ref i))))
`(if ,(build-primcall 3 'fx= (list (build-ref j) (build-ref n)))
(call ,(app-preinfo ctxt) (ref #f ,p)
,(map (lambda (x)
(build-primcall 3 'vector-ref
(list (build-ref x) (build-ref i))))
(cons v v*)) ...)
,(make-seq 'value
`(call ,(app-preinfo ctxt) (ref #f ,p)
,(map (lambda (x)
(build-primcall 3 'vector-ref
(list (build-ref x) (build-ref i))))
(cons v v*)) ...)
`(call ,(make-preinfo) (ref #f ,do) (ref #f ,j))))))))))
ctxt empty-env sc wd name moi))])])
(define-inline 3 string-for-each ; should combine with vector-for-each
[(?p ?s . ?s*)
(cond
[(nanopass-case (Lsrc Expr) (result-exp (value-visit-operand! ?p))
[,pr (all-set? (prim-mask discard) (primref-flags pr))]
[else #f])
(residualize-seq '() (list* ?p ?s ?s*) ctxt)
void-rec]
[(ormap (lambda (?s)
(nanopass-case (Lsrc Expr) (result-exp (value-visit-operand! ?s))
[(quote ,d)
(and (string? d)
(let ([n (string-length d)]) (and (fx<= n 4) n)))]
[else #f]))
(cons ?s ?s*)) =>
(lambda (n)
(cond
[(fx= n 0)
; (for-each proc (begin e1 ... '()) e2 ...) =>
; (begin (begin e1 ... '()) e2 ... (void))
(residualize-seq '() (list* ?p ?s ?s*) ctxt)
void-rec]
[else
; (for-each proc (begin e1 ... '#(a b c d)) e2 ...)
; ((lambda (p ls1 ls2 ...)
; (proc 'a (string-ref ls2 0) ...)
; (proc 'b (string-ref ls2 1) ...)
; (proc 'c (string-ref ls2 2) ...)
; (proc 'd (string-ref ls2 3) ...))
; proc (begin e1 ... '(a b c d)) e2 ...)
(cp0
(let ([p (cp0-make-temp (fx> n 1))]
[s* (cons (cp0-make-temp #t)
(map (lambda (x) (cp0-make-temp #t)) ?s*))])
(build-lambda (cons p s*)
(make-seq* 'value
(map (lambda (i)
`(call ,(app-preinfo ctxt) (ref #f ,p)
,(map (lambda (x)
(build-primcall 3 'string-ref
(list (build-ref x) `(quote ,i))))
s*) ...))
(iota n)))))
ctxt empty-env sc wd name moi)]))]
[else
(and likely-to-be-compiled?
(cp0
(let ([p (cp0-make-temp #t)]
[n (cp0-make-temp #t)]
[i (cp0-make-temp #t)]
[j (cp0-make-temp #t)]
[do (cp0-make-temp #t)]
[s (cp0-make-temp #t)]
[s* (map (lambda (x) (cp0-make-temp #f)) ?s*)])
(build-lambda (cons* p s s*)
(build-let (list n) (list (build-primcall 3 'string-length (list (build-ref s))))
`(if ,(build-primcall 3 'fx= (list (build-ref n) `(quote 0)))
,void-rec
,(build-named-let do (list i) (list `(quote 0))
(build-let (list j) (list (build-primcall 3 'fx1+ (list (build-ref i))))
`(if ,(build-primcall 3 'fx= (list (build-ref j) (build-ref n)))
(call ,(app-preinfo ctxt) (ref #f ,p)
,(map (lambda (x)
(build-primcall 3 'string-ref
(list (build-ref x) (build-ref i))))
(cons s s*)) ...)
,(make-seq 'value
`(call ,(app-preinfo ctxt) (ref #f ,p)
,(map (lambda (x)
(build-primcall 3 'string-ref
(list (build-ref x) (build-ref i))))
(cons s s*)) ...)
`(call ,(make-preinfo) (ref #f ,do) (ref #f ,j))))))))))
ctxt empty-env sc wd name moi))])])
(define-inline 3 fold-right
[(?combine ?nil ?ls . ?ls*)
(and (ormap
(lambda (?ls) (cp0-constant? null? (result-exp (value-visit-operand! ?ls))))
(cons ?ls ?ls*))
(let ([nilval (value-visit-operand! ?nil)])
(residualize-seq (list ?nil) (list* ?combine ?ls ?ls*) ctxt)
nilval))])
(define-inline 3 fold-left
[(?combine ?nil ?ls . ?ls*)
(if (ormap
(lambda (?ls) (cp0-constant? null? (result-exp (value-visit-operand! ?ls))))
(cons ?ls ?ls*))
(let ([nilval (value-visit-operand! ?nil)])
(residualize-seq (list ?nil) (list* ?combine ?ls ?ls*) ctxt)
nilval)
(and likely-to-be-compiled?
(cp0
(let ([?ls* (cons ?ls ?ls*)])
(let ([p (cp0-make-temp #t)]
[nil (cp0-make-temp #t)]
[tls* (map (lambda (x) (cp0-make-temp #t)) ?ls*)]
[do (cp0-make-temp #t)]
[acc (cp0-make-temp #t)]
[ls* (map (lambda (x) (cp0-make-temp #t)) ?ls*)]
[r (cp0-make-temp #t)]
[carls* (map (lambda (x) (cp0-make-temp #t)) ?ls*)])
(build-lambda (cons* p nil tls*)
`(if ,(build-primcall 3 'null? (list (build-ref (car tls*))))
(ref #f ,nil)
,(build-named-let do (cons acc ls*)
(map build-ref (cons nil tls*))
(build-let (cons r carls*)
(cons
(build-primcall 3 'cdr (list (build-ref (car ls*))))
(map (lambda (x) (build-primcall 3 'car (list (build-ref x)))) ls*))
`(if ,(build-primcall 3 'null? (list (build-ref r)))
(call ,(app-preinfo ctxt) (ref #f ,p)
(ref #f ,acc)
,(map build-ref carls*)
...)
(call ,(make-preinfo) (ref #f ,do)
(call ,(app-preinfo ctxt) (ref #f ,p)
(ref #f ,acc)
,(map build-ref carls*)
...)
(ref #f ,r)
,(map (lambda (x) (build-primcall 3 'cdr (list (build-ref x)))) (cdr ls*))
...))))))))
ctxt empty-env sc wd name moi)))])
(define-inline 3 (andmap for-all)
[(?p ?ls . ?ls*)
(if (ormap
(lambda (?ls) (cp0-constant? null? (result-exp (value-visit-operand! ?ls))))
(cons ?ls ?ls*))
(begin
(residualize-seq '() (list* ?p ?ls ?ls*) ctxt)
true-rec)
(and likely-to-be-compiled?
(cp0
(let ([?ls* (cons ?ls ?ls*)])
(let ([p (cp0-make-temp #t)]
[r (cp0-make-temp #t)]
[do (cp0-make-temp #t)]
[tls* (map (lambda (x) (cp0-make-temp #t)) ?ls*)]
[ls* (map (lambda (x) (cp0-make-temp #t)) ?ls*)])
(build-lambda (cons p tls*)
`(if ,(build-primcall 3 'null?
(list (build-ref (car tls*))))
,true-rec
,(build-named-let do ls*
(map build-ref tls*)
(build-let (list r)
(list (build-primcall 3 'cdr
(list (build-ref (car ls*)))))
`(if ,(build-primcall 3 'null?
(list (build-ref r)))
(call ,(app-preinfo ctxt) (ref #f ,p)
,(map (lambda (x)
(build-primcall 3 'car
(list (build-ref x))))
ls*) ...)
(if (call ,(app-preinfo ctxt) (ref #f ,p)
,(map (lambda (x)
(build-primcall 3 'car
(list (build-ref x))))
ls*) ...)
(call ,(make-preinfo) (ref #f ,do) (ref #f ,r)
,(map (lambda (x)
(build-primcall 3 'cdr
(list (build-ref x))))
(cdr ls*)) ...)
,false-rec))))))))
ctxt empty-env sc wd name moi)))])
(define-inline 3 (ormap exists)
[(?p ?ls . ?ls*)
(if (ormap
(lambda (?ls) (cp0-constant? null? (result-exp (value-visit-operand! ?ls))))
(cons ?ls ?ls*))
(begin
(residualize-seq '() (list* ?p ?ls ?ls*) ctxt)
false-rec)
(and likely-to-be-compiled?
(cp0
(let ([?ls* (cons ?ls ?ls*)])
(let ([p (cp0-make-temp #t)]
[r (cp0-make-temp #t)]
[t (cp0-make-temp #t)]
[do (cp0-make-temp #t)]
[tls* (map (lambda (x) (cp0-make-temp #t)) ?ls*)]
[ls* (map (lambda (x) (cp0-make-temp #t)) ?ls*)])
(build-lambda (cons p tls*)
`(if ,(build-primcall 3 'null?
(list (build-ref (car tls*))))
,false-rec
,(build-named-let do ls*
(map build-ref tls*)
(build-let (list r)
(list (build-primcall 3 'cdr
(list (build-ref (car ls*)))))
`(if ,(build-primcall 3 'null?
(list (build-ref r)))
(call ,(app-preinfo ctxt) (ref #f ,p)
,(map (lambda (x)
(build-primcall 3 'car
(list (build-ref x))))
ls*) ...)
,(build-let (list t)
(list `(call ,(app-preinfo ctxt) (ref #f ,p)
,(map (lambda (x)
(build-primcall 3 'car
(list (build-ref x))))
ls*) ...))
`(if (ref #f ,t)
(ref #f ,t)
(call ,(make-preinfo) (ref #f ,do) (ref #f ,r)
,(map (lambda (x)
(build-primcall 3 'cdr
(list (build-ref x))))
(cdr ls*)) ...))))))))))
ctxt empty-env sc wd name moi)))])
(define-inline 2 car
[(?x)
(nanopass-case (Lsrc Expr) (result-exp (value-visit-operand! ?x))
[(immutable-list (,e* ...) ,e)
(and (not (null? e*))
(begin
(residualize-seq '() (list ?x) ctxt)
(car e*)))]
[(call ,preinfo ,pr ,e1 ,e2)
(guard (eq? (primref-name pr) 'cons))
(residualize-seq (list ?x) '() ctxt)
(non-result-exp (operand-value ?x)
(make-seq (app-ctxt ctxt) e2 e1))]
[(call ,preinfo ,pr ,e* ...)
(guard (memq (primref-name pr) '(list list* cons*)) (not (null? e*)))
(residualize-seq (list ?x) '() ctxt)
(non-result-exp (operand-value ?x)
(fold-right
(lambda (e1 e2) (make-seq (app-ctxt ctxt) e1 e2))
(car e*)
(cdr e*)))]
[else #f])])
(define-inline 2 cdr
[(?x)
(nanopass-case (Lsrc Expr) (result-exp (value-visit-operand! ?x))
[(immutable-list (,e* ...) ,e)
(and (not (null? e*))
(begin
(residualize-seq '() (list ?x) ctxt)
`(immutable-list (,(cdr e*) ...)
,(build-primcall (app-preinfo ctxt) 3 'cdr
(list e)))))]
[(call ,preinfo ,pr ,e1 ,e2)
(guard (eq? (primref-name pr) 'cons))
(residualize-seq (list ?x) '() ctxt)
(non-result-exp (operand-value ?x)
(make-seq (app-ctxt ctxt) e1 e2))]
[(call ,preinfo ,pr ,e* ...)
(guard (eq? (primref-name pr) 'list) (not (null? e*)))
(residualize-seq (list ?x) '() ctxt)
(non-result-exp (operand-value ?x)
(make-seq (app-ctxt ctxt) (car e*)
(build-call (app-preinfo ctxt) pr (cdr e*))))]
[(call ,preinfo ,pr ,e* ...)
(guard (memq (primref-name pr) '(list* cons*)) (>= (length e*) 2))
(residualize-seq (list ?x) '() ctxt)
(non-result-exp (operand-value ?x)
(make-seq (app-ctxt ctxt) (car e*)
(build-call (app-preinfo ctxt) pr (cdr e*))))]
[else #f])])
(let ()
(define doref
(lambda (ctxt ?x ?i e* d edok?)
(let ([e (let f ([e* e*] [d d] [ed #f])
(if (null? e*)
ed
(if (fx= d 0)
(let ([ed (car e*)])
(and (edok? (result-exp ed)) (f (cdr e*) (fx- d 1) ed)))
(let ([e (f (cdr e*) (fx- d 1) ed)])
(and e (make-seq (app-ctxt ctxt) (car e*) e))))))])
(and e (begin
(residualize-seq (list ?x ?i) '() ctxt)
(non-result-exp (operand-value ?i) ; do first ...
(non-result-exp (operand-value ?x) ; ... so we keep ?x related side effects together
e)))))))
(define tryref
(lambda (ctxt ?x ?i seqprim maybe-pred)
(nanopass-case (Lsrc Expr) (result-exp (value-visit-operand! ?x))
[(call ,preinfo ,pr ,e* ...)
(guard (eq? (primref-name pr) seqprim))
(nanopass-case (Lsrc Expr) (result-exp (value-visit-operand! ?i))
[(quote ,d)
(guard (fixnum? d) (#%$fxu< d (length e*)))
(doref ctxt ?x ?i e* d
(if (and maybe-pred (not (all-set? (prim-mask unsafe) (primref-flags pr))))
(lambda (x) (cp0-constant? maybe-pred x))
true))]
[else #f])]
[else #f])))
(define true (lambda (x) #t))
(define-inline 2 vector-ref
[(?x ?i) (tryref ctxt ?x ?i 'vector #f)])
(define-inline 2 string-ref
[(?x ?i) (tryref ctxt ?x ?i 'string char?)])
(define-inline 2 fxvector-ref
[(?x ?i) (tryref ctxt ?x ?i 'fxvector target-fixnum?)])
; skipping bytevector-u8-ref and bytevector-s8-ref, which generally need to adjust the result.
(define-inline 2 list-ref
[(?x ?i)
(nanopass-case (Lsrc Expr) (result-exp (value-visit-operand! ?x))
[(call ,preinfo ,pr ,e* ...)
(guard (memq (primref-name pr) '(list list* cons*)))
(nanopass-case (Lsrc Expr) (result-exp (value-visit-operand! ?i))
[(quote ,d)
(guard (fixnum? d)
(and (fx>= d 0)
(let ([n (length e*)])
(if (eq? pr 'list) (fx< d n) (fx< d (fx- n 1))))))
(doref ctxt ?x ?i e* d true)]
[else #f])]
[else #f])]))
(let ()
(define maybe-add-procedure-check
(lambda (?p level who p e)
(define (opnd-proc? opnd)
(nanopass-case (Lsrc Expr) (result-exp/indirect-ref (value-visit-operand! opnd))
[(case-lambda ,preinfo ,cl ...) #t]
[,pr (all-set? (prim-mask proc) (primref-flags pr))]
[(quote ,d) (procedure? d)]
[else #f]))
(if (or (fx= level 3) (opnd-proc? ?p))
e
`(seq
(if ,(build-primcall 3 'procedure? (list (build-ref p)))
,void-rec
,(build-primcall 2 '$oops
(list
`(quote ,who)
`(quote "~s is not a procedure")
(build-ref p))))
,e))))
(let ()
(define mp
(lambda (ctxt empty-env sc wd name moi ?p level)
(and likely-to-be-compiled?
(cp0
(let ([x (cp0-make-temp #f)] [v (cp0-make-temp #f)])
(set-prelex-assigned! x #t)
(if ?p
(let ([orig-x (cp0-make-temp #f)] [p (cp0-make-temp #t)])
(build-lambda (list orig-x p)
(maybe-add-procedure-check ?p level "make-parameter" p
(build-let (list x) (list `(call ,(make-preinfo) (ref #f ,p) (ref #f ,orig-x)))
(build-case-lambda (preinfo-call->preinfo-lambda (app-preinfo ctxt))
(list
(list '() (build-ref x))
(list (list v) `(set! #f ,x (call ,(make-preinfo) (ref #f ,p) (ref #f ,v))))))))))
(build-lambda (list x)
(build-case-lambda (preinfo-call->preinfo-lambda (app-preinfo ctxt))
(list
(list '() (build-ref x))
(list (list v) `(set! #f ,x (ref #f ,v))))))))
ctxt empty-env sc wd name moi))))
(define-inline 2 make-parameter
[(?x) (mp ctxt empty-env sc wd name moi #f 2)]
[(?x ?p) (mp ctxt empty-env sc wd name moi ?p 2)])
(define-inline 3 make-parameter
[(?x) (mp ctxt empty-env sc wd name moi #f 3)]
[(?x ?p) (mp ctxt empty-env sc wd name moi ?p 3)]))
(when-feature pthreads
(let ()
(define (mtp-ref x)
(build-primcall 3 'vector-ref
(list
(build-primcall 3 '$tc-field
(list
`(quote parameters)
(build-primcall 3 '$tc '())))
(build-primcall 3 'car
(list (build-ref x))))))
(define (mtp-set x e)
(build-primcall 3 '$set-thread-parameter!
(list (build-ref x) e)))
(define mtp
(lambda (ctxt empty-env sc wd name moi ?p level)
(and likely-to-be-compiled?
(cp0
(let ([orig-x (cp0-make-temp #f)] [x (cp0-make-temp #t)] [v (cp0-make-temp #f)])
(if ?p
(let ([p (cp0-make-temp #t)])
(build-lambda (list orig-x p)
(maybe-add-procedure-check ?p level "make-thread-parameter" p
(build-let (list x)
(list (build-primcall 3 '$allocate-thread-parameter
(list `(call ,(make-preinfo) (ref #f ,p) (ref #f ,orig-x)))))
(build-case-lambda (preinfo-call->preinfo-lambda (app-preinfo ctxt))
(list
(list '() (mtp-ref x))
(list (list v) (mtp-set x `(call ,(make-preinfo) (ref #f ,p) (ref #f ,v))))))))))
(build-lambda (list orig-x)
(build-let (list x)
(list (build-primcall 3 '$allocate-thread-parameter
(list (build-ref orig-x))))
(build-case-lambda (preinfo-call->preinfo-lambda (app-preinfo ctxt))
(list
(list '() (mtp-ref x))
(list (list v) (mtp-set x (build-ref v)))))))))
ctxt empty-env sc wd name moi))))
(define-inline 2 make-thread-parameter
[(?x) (mtp ctxt empty-env sc wd name moi #f 2)]
[(?x ?p) (mtp ctxt empty-env sc wd name moi ?p 2)])
(define-inline 3 make-thread-parameter
[(?x) (mtp ctxt empty-env sc wd name moi #f 3)]
[(?x ?p) (mtp ctxt empty-env sc wd name moi ?p 3)]))))
(let ()
(define inline-make-guardian
(lambda (ctxt empty-env sc wd name moi formal* make-setter-clauses)
(and likely-to-be-compiled?
(cp0
(let* ([tc (cp0-make-temp #t)] [ref-tc (build-ref tc)])
; if the free variables of the closure created for a guardian changes, the code
; for unregister-guardian in prims.ss might also need to be updated
(build-lambda formal*
(build-let (list tc)
(list (let* ([x (cp0-make-temp #t)] [ref-x (build-ref x)])
(let ([zero `(quote 0)])
(build-let (list x) (list (build-primcall 3 'cons (list zero zero)))
(build-primcall 3 'cons (list ref-x ref-x))))))
(build-case-lambda (let ([preinfo (app-preinfo ctxt)])
(make-preinfo-lambda (preinfo-src preinfo) (preinfo-sexpr preinfo) #f #f
(constant code-flag-guardian)))
(cons
(list '()
(let* ([x (cp0-make-temp #t)] [ref-x (build-ref x)])
(let ([y (cp0-make-temp #f)])
(build-let (list x) (list (build-primcall 3 'car (list ref-tc)))
`(if ,(build-primcall 3 'eq?
(list ref-x
(build-primcall 3 'cdr (list ref-tc))))
,false-rec
,(build-let (list y) (list (build-primcall 3 'car (list ref-x)))
`(seq
(seq
(seq
,(build-primcall 3 'set-car! (list ref-tc
(build-primcall 3 'cdr (list ref-x))))
,(build-primcall 3 'set-car! (list ref-x false-rec)))
,(build-primcall 3 'set-cdr! (list ref-x false-rec)))
(ref #f ,y))))))))
(make-setter-clauses ref-tc))))))
ctxt empty-env sc wd name moi))))
(define-inline 2 make-guardian
[() (inline-make-guardian ctxt empty-env sc wd name moi '()
(lambda (ref-tc)
(list
(let* ([obj (cp0-make-temp #t)] [ref-obj (build-ref obj)])
(list (list obj)
(build-primcall 3 '$install-guardian
(list ref-obj ref-obj ref-tc))))
(let ([obj (cp0-make-temp #f)] [rep (cp0-make-temp #f)])
(list (list obj rep)
(build-primcall 3 '$install-guardian
(list (build-ref obj) (build-ref rep) ref-tc)))))))])
(define-inline 2 $make-ftype-guardian
[(?ftd)
(let ([ftd (cp0-make-temp #f)])
(inline-make-guardian ctxt empty-env sc wd name moi
(list ftd)
(lambda (ref-tc)
(list
(let* ([obj (cp0-make-temp #t)] [ref-obj (build-ref obj)])
(list (list obj)
(let ([e (build-primcall 3 '$install-ftype-guardian
(list ref-obj ref-tc))])
(if (fx= level 3)
e
(let ([ref-ftd (build-ref ftd)])
`(seq
(if ,(build-primcall 3 'record? (list ref-obj ref-ftd))
,void-rec
,(build-primcall 3 '$ftype-guardian-oops (list ref-ftd ref-obj)))
,e))))))))))])))
) ; with-output-language
(define-pass cp0 : Lsrc (ir ctxt env sc wd name moi) -> Lsrc ()
(Expr : Expr (ir ctxt env sc wd name moi) -> Expr ()
[(quote ,d) ir]
[(ref ,maybe-src ,x)
(context-case ctxt
[(effect) void-rec]
[else
(let ((new-id (lookup x env)))
(when (eq? new-id x)
; id is a free variable of a lambda we're attempting to integrate,
; so we conservatively set it multiply-referenced in case we try to
; integrate the lambda more than once.
(set-prelex-multiply-referenced! new-id #t))
(let ((opnd (prelex-operand new-id)))
; a scorer in place of an operand means that we've found a
; recursive reference that we're not permitted to residualize
(if (scorer? opnd)
(bug-out! opnd)
(if (and opnd (not (inner-cyclic? opnd)))
(cond
[(and (app? ctxt)
; find a lambda expression starting with (operand-exp opnd) and
; following along through singly referenced unassigned variable
; references---a sort of source-level copy propagation. we should
; traverse a chain of references at most once here since we only
; propagate along singly referenced identifiers
(let loop ((new-id new-id) (opnd opnd))
(and (not (operand-value opnd))
(not (prelex-was-assigned new-id))
(not (prelex-was-multiply-referenced new-id))
(nanopass-case (Lsrc Expr) (operand-exp opnd)
[(case-lambda ,preinfo ,cl* ...) opnd]
[(ref ,maybe-src ,x)
(let ((new-rhs-id (lookup x (operand-env opnd))))
(and (not (eq? new-rhs-id x))
(let ((opnd (prelex-operand new-rhs-id)))
(and (operand? opnd)
(loop new-rhs-id opnd)))))]
[else #f])))) =>
(lambda (x-opnd)
; yea-raw, singly referenced id with rhs a lambda
; skip value-visit operand and, therefore, don't alert the watchdog
(with-values (find-lambda-clause (operand-exp x-opnd) ctxt)
(case-lambda
[(ids body)
(let ((sc (new-scorer)))
(let ((e (cp0-let
(nanopass-case (Lsrc Expr) (operand-exp x-opnd)
[(case-lambda ,preinfo ,cl* ...) preinfo])
ids body ctxt (operand-env x-opnd) sc (operand-wd x-opnd) name moi)))
(operand-singly-referenced-score-set! x-opnd (scorer-score sc))
e))]
[()
; had been visiting x-opnd, leaving intermediate
; opnds in chain unvisited
(value-visit-operand! opnd)
; could call copy here, as below, but this
; leads to more misleading incorrect argument
; count errors
#;(copy maybe-src new-id opnd ctxt sc wd)
(residualize-ref maybe-src new-id sc)])))]
[else
(value-visit-operand! opnd)
(if (prelex-was-assigned new-id)
(residualize-ref maybe-src new-id sc)
(copy maybe-src new-id opnd ctxt sc wd name moi))])
(residualize-ref maybe-src new-id sc)))))])]
[(seq ,[cp0 : e1 'effect env sc wd #f moi -> e1] ,e2)
(make-seq ctxt e1 (cp0 e2 ctxt env sc wd name moi))]
[(if ,[cp0 : e1 'test env sc wd #f moi -> e1] ,e2 ,e3)
(nanopass-case (Lsrc Expr) (result-exp e1)
[(quote ,d)
(make-seq ctxt e1 (cp0 (if d e2 e3) ctxt env sc wd name moi))]
[else
(let ((noappctxt (if (app? ctxt) 'value ctxt)))
(let ([e2 (cp0 e2 noappctxt env sc wd name moi)]
[e3 (cp0 e3 noappctxt env sc wd name moi)])
(make-if ctxt sc e1 e2 e3)))])]
[(set! ,maybe-src ,x ,e)
(let ((new-id (lookup x env)))
(if (prelex-was-referenced new-id)
(begin
(bump sc 1)
(let ((e (cp0 e 'value env sc wd (prelex-name x) moi)))
(set-prelex-assigned! new-id #t)
`(set! ,maybe-src ,new-id ,e)))
(make-seq ctxt (cp0 e 'effect env sc wd (prelex-name x) moi) void-rec)))]
[(call ,preinfo ,e ,e* ...)
(let ()
(define lift-let
(lambda (e args)
(nanopass-case (Lsrc Expr) e
[(case-lambda ,preinfo0 (clause (,x* ...) ,interface ,body))
(guard (fx= interface (length args)))
(let loop ([ids x*] [args args] [new-ids '()] [new-args '()] [xids '()] [xargs '()])
(if (null? ids)
(if (null? xids)
(values
(build-lambda preinfo0 (reverse new-ids) body)
(reverse new-args))
(values
(build-lambda preinfo0 (reverse xids)
(build-let (reverse new-ids) (reverse new-args) body))
(reverse xargs)))
(nanopass-case (Lsrc Expr) (car args)
[(call ,preinfo1
(case-lambda ,preinfo2
(clause (,x2* ...) ,interface2
(case-lambda ,preinfo3 ,cl3* ...)))
,e1* ...)
(guard (fx= (length e1*) 1) (fx= interface2 1)
(not (prelex-assigned (car x*))))
(loop (cdr ids) (cdr args) (cons (car ids) new-ids)
(cons `(case-lambda ,preinfo3 ,cl3* ...) new-args) (cons (car x2*) xids)
(cons (car e1*) xargs))]
[else (loop (cdr ids)
(cdr args)
(cons (car ids) new-ids)
(cons (car args) new-args)
xids xargs)])))]
[else (values e args)])))
(let-values ([(e args) (lift-let e e*)])
(cp0-call preinfo e (build-operands args env wd moi) ctxt env sc wd name moi)))]
[(case-lambda ,preinfo ,cl* ...)
(when (and (symbol? name)
;; Avoid replacing a name from an optimized-away `let` pattern:
(not (preinfo-lambda-name preinfo)))
(preinfo-lambda-name-set! preinfo
(let ([x ($symbol-name name)])
(if (pair? x) (cdr x) x))))
(context-case ctxt
[(value)
(bump sc 1)
`(case-lambda ,preinfo
,(let f ([cl* cl*] [mask 0])
(if (null? cl*)
'()
(nanopass-case (Lsrc CaseLambdaClause) (car cl*)
[(clause (,x* ...) ,interface ,body)
(let ([new-mask (logor mask (if (fx< interface 0) (ash -1 (fx- -1 interface)) (ash 1 interface)))])
(if (= new-mask mask)
(f (cdr cl*) new-mask)
(cons
(with-extended-env ((env x*) (env x* #f))
`(clause (,x* ...) ,interface ,(cp0 body 'value env sc wd #f name)))
(f (cdr cl*) new-mask))))])))
...)]
[(effect) void-rec]
[(test) true-rec]
[(app)
(with-values (find-lambda-clause ir ctxt)
(case-lambda
[(ids body)
; looking for or pattern first
(or (and (fx= (length ids) 1)
(nanopass-case (Lsrc Expr) body
[(if (ref ,maybe-src1 ,x1) (ref ,maybe-src2 ,x2) ,e3)
(guard (let ([id (car ids)]) (and (eq? x1 id) (eq? x2 id))))
(let ()
(define (finish e1)
(define (do-e3)
(with-extended-env ((env ids) (env ids (list (make-operand false-rec env wd moi))))
(let ([e3 (cp0 e3 (app-ctxt ctxt) env sc wd (app-name ctxt) moi)])
(if (or (prelex-referenced (car ids)) (prelex-assigned (car ids)))
(build-let ids (list false-rec) e3)
e3))))
(nanopass-case (Lsrc Expr) (result-exp e1)
[(quote ,d)
(residualize-seq '() (app-opnds ctxt) ctxt)
(if d true-rec (do-e3))]
[else
; converting (let ([x e1]) (if x x e3)) => (if e1 #t (let ([x #f]) e3))
; i.e., handling or pattern.
(residualize-seq (app-opnds ctxt) '() ctxt)
(make-if ctxt sc e1
true-rec
(do-e3))]))
(if (eq? (app-ctxt ctxt) 'value)
(let ([e1 (value-visit-operand! (car (app-opnds ctxt)))])
(and (boolean-valued? e1) (finish e1)))
(and (eq? (app-ctxt ctxt) 'test)
(finish (test-visit-operand! (car (app-opnds ctxt)))))))]
[else #f]))
(cp0-let preinfo ids body ctxt env sc wd name moi))]
[() (cp0 ir 'value env sc wd name moi)]))])]
[(letrec ([,x* ,e*] ...) ,body)
(cp0-rec-let #f x* e* body ctxt env sc wd name moi)]
[(letrec* ([,x* ,e*] ...) ,body)
(cp0-rec-let #t x* e* body ctxt env sc wd name moi)]
[,pr (context-case ctxt
[(value) (bump sc 1) pr]
[(effect) void-rec]
[(test)
(if (all-set? (prim-mask proc) (primref-flags pr))
true-rec
(begin (bump sc 1) pr))]
[(app) (fold-primref pr ctxt sc wd name moi)])]
[(foreign (,conv* ...) ,name ,e (,arg-type* ...) ,result-type)
(context-case ctxt
[(value app) (bump sc 1) `(foreign (,conv* ...) ,name ,(cp0 e 'value env sc wd #f moi) (,arg-type* ...) ,result-type)]
[(effect test) (cp0 `(seq ,e ,true-rec) ctxt env sc wd #f moi)])]
[(fcallable (,conv* ...) ,e (,arg-type* ...) ,result-type)
(context-case ctxt
[(value app) (bump sc 1) `(fcallable (,conv* ...) ,(cp0 e 'value env sc wd #f moi) (,arg-type* ...) ,result-type)]
[(effect) (cp0 e 'effect env sc wd #f moi)]
[(test) (make-seq ctxt (cp0 e 'effect env sc wd #f moi) true-rec)])]
[(record ,rtd ,rtd-expr ,e* ...)
(context-case ctxt
[(value app)
(let ([rtd-expr (cp0 rtd-expr 'value env sc wd #f moi)]
[e* (map (lambda (e) (cp0 e 'value env sc wd #f moi)) e*)])
(or (nanopass-case (Lsrc Expr) (result-exp rtd-expr)
[(quote ,d)
(and (record-type-descriptor? d)
(andmap (lambda (fld)
(and (not (fld-mutable? fld))
(eq? (filter-foreign-type (fld-type fld)) 'scheme-object)))
(rtd-flds d))
(let ([d* (objs-if-constant e*)])
(and d*
(make-seq ctxt
(make-seq* 'effect (cons rtd-expr e*))
`(quote ,(apply $record d d*))))))]
[else #f])
`(record ,rtd ,rtd-expr ,e* ...)))]
[(effect)
(make-seq* ctxt
(cons
(cp0 rtd-expr 'effect env sc wd #f moi)
(map (lambda (e) (cp0 e 'effect env sc wd #f moi)) e*)))]
[(test)
(make-seq ctxt
(make-seq* 'effect
(cons
(cp0 rtd-expr 'effect env sc wd #f moi)
(map (lambda (e) (cp0 e 'effect env sc wd #f moi)) e*)))
true-rec)])]
[(record-ref ,rtd ,type ,index ,e0)
(context-case ctxt
[(effect) (cp0 e0 'effect env sc wd name moi)]
[else
(let ([e0 (cp0 e0 'value env sc wd name moi)])
(or (nanopass-case (Lsrc Expr) (result-exp e0)
[(quote ,d)
(and (record? d rtd)
(make-seq ctxt e0 `(quote ,((csv7:record-field-accessor rtd index) d))))]
[(record ,rtd1 ,rtd-expr ,e* ...)
(let loop ([e* e*] [re* '()] [index index])
(and (not (null? e*))
(if (fx= index 0)
(let ([e (car e*)] [e* (rappend re* (cdr e*))])
(non-result-exp e0
(if (null? e*)
e
(make-seq ctxt (make-seq* 'effect e*) e))))
(loop (cdr e*) (cons (car e*) re*) (fx- index 1)))))]
[else #f])
(nanopass-case (Lsrc Expr) (result-exp/indirect-ref e0)
[(record ,rtd1 ,rtd-expr ,e* ...)
(and (> (length e*) index)
(not (fld-mutable? (list-ref (rtd-flds rtd) index)))
(let ([e (list-ref e* index)])
(and (nanopass-case (Lsrc Expr) e
[(quote ,d) #t]
[(ref ,maybe-src ,x) (not (prelex-assigned x))]
[,pr (all-set? (prim-mask proc) (primref-flags pr))]
[else #f])
; recur to cp0 to get inlining, folding, etc.
(non-result-exp e0 (cp0 e ctxt env sc wd name moi)))))]
[else #f])
(begin (bump sc 1) `(record-ref ,rtd ,type ,index ,e0))))])]
[(record-set! ,rtd ,type ,index ,[cp0 : e1 'value env sc wd #f moi -> e1] ,[cp0 : e2 'value env sc wd #f moi -> e2])
`(record-set! ,rtd ,type ,index ,e1 ,e2)]
[(record-type ,rtd ,e) (cp0 e ctxt env sc wd name moi)]
[(record-cd ,rcd ,rtd-expr ,e) (cp0 e ctxt env sc wd name moi)]
[(immutable-list (,[cp0 : e* 'value env sc wd #f moi -> e*] ...) ,[cp0 : e ctxt env sc wd name moi -> e])
`(immutable-list (,e* ...) ,e)]
[(moi) (if moi `(quote ,moi) ir)]
[(pariah) ir]
[(cte-optimization-loc ,box ,[cp0 : e ctxt env sc wd name moi -> e])
(when (enable-cross-library-optimization)
(let ()
(define update-box!
(lambda (box e)
(set-box! box
(cons
(cons ($target-machine) e)
(remp (lambda (as) (eq? (car as) ($target-machine))) (unbox box))))))
(nanopass-case (Lsrc Expr) e
[(quote ,d) (and (okay-to-copy? d) (update-box! box e))]
[,pr (update-box! box pr)]
[(ref ,maybe-src ,x)
(and (not (prelex-was-assigned x))
(let ([rhs (result-exp (operand-value (prelex-operand x)))])
(nanopass-case (Lsrc Expr) rhs
[(case-lambda ,preinfo ,cl* ...)
(when (andmap externally-inlinable? cl*)
(update-box! box rhs))]
[else #f])))]
[else (void)])))
`(cte-optimization-loc ,box ,e)]
[(cpvalid-defer ,e) (sorry! who "cpvalid leaked a cpvalid-defer form ~s" ir)]
[(profile ,src) ir]
[else ($oops who "unrecognized record ~s" ir)])
(begin
(bump wd 1)
(Expr ir ctxt env sc wd name moi)))
(rec $cp0
(case-lambda
[(x) ($cp0 x #t)]
[(x ltbc?)
(fluid-let ([likely-to-be-compiled? ltbc?]
[opending-list '()]
[cp0-info-hashtable (make-weak-eq-hashtable)])
(cp0 x 'value empty-env (new-scorer) (new-watchdog) #f #f))]))))
; check to make sure all required handlers were seen, after expansion of the
; expression above has been completed
(let-syntax ([a (lambda (x)
(for-each
(lambda (sym)
(let ([flags ($sgetprop sym '*flags* 0)])
(when (all-set? (prim-mask cp02) flags)
(if (getprop sym 'cp02 #f)
(remprop sym 'cp02)
($oops #f "no cp02 handler for ~s" sym)))
(when (all-set? (prim-mask cp03) flags)
(if (getprop sym 'cp03 #f)
(remprop sym 'cp03)
($oops #f "no cp03 handler for ~s" sym)))))
(oblist))
#'(void))])
a)