123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277 |
- ;;; Ported from Scheme 48 1.9. See file COPYING for notices and license.
- ;;;
- ;;; Port Author: Andrew Whatson
- ;;;
- ;;; Original Authors: Richard Kelsey
- ;;;
- ;;; scheme48-1.9.2/ps-compiler/simp/call.scm
- (define-module (ps-compiler simp call)
- #:use-module (prescheme scheme48)
- #:use-module (ps-compiler node arch)
- #:use-module (ps-compiler node let-nodes)
- #:use-module (ps-compiler node node)
- #:use-module (ps-compiler node node-equal)
- #:use-module (ps-compiler node node-util)
- #:use-module (ps-compiler node primop)
- #:use-module (ps-compiler param)
- #:use-module (ps-compiler simp simplify)
- #:use-module (ps-compiler util util)
- #:export (simplify-allocation
- simplify-known-call
- simplify-known-tail-call
- simplify-unknown-call
- simplify-return
- simplify-jump
- ;; simplify-undefined-value
- simplify-test expand-test simplify-test?))
- (define (simplify-jump call)
- (cond ((lambda-node? (call-arg call 0))
- (set-call-primop! call (get-primop (enum primop-enum let)))
- (set-call-exits! call 1)
- (set-node-simplified?! call #f))
- (else
- (default-simplifier call))))
- (define simplify-return simplify-jump)
- ;; If the procedure is a lambda-node:
- ;; 1. note that we know where the continuation lambda is used (and turn any
- ;; tail-calls using it into regular calls)
- ;; 2. change the primop to LET
- ;; 3. the procedure is now the continuation
- ;; 4. the continuation is now a jump lambda
- ;; 5. change the primop used to call the continuation to jump
- ;; 6. swap the cont and proc.
- ;; (CALL <cont> (LAMBDA (c . vars) ...) . args))
- ;; =>
- ;; (LET (LAMBDA (c . vars) ...) <cont> . args)
- ;; If the continuation just returns somewhere else, replace UNKNOWN-CALL
- ;; with UNKNOWN-TAIL-CALL.
- (define (simplify-known-call call)
- (let ((proc (call-arg call 1))
- (cont (call-arg call 0)))
- (cond ((lambda-node? proc)
- (determine-continuation-protocol cont (list proc))
- (set-call-primop! call (get-primop (enum primop-enum let)))
- (change-lambda-type proc 'cont)
- (change-lambda-type cont 'jump)
- (for-each (lambda (ref)
- (set-call-primop! (node-parent ref)
- (get-primop (enum primop-enum jump))))
- (variable-refs (car (lambda-variables proc))))
- (move cont
- (lambda (cont)
- (detach proc)
- (attach call 1 cont)
- proc)))
- ((trivial-continuation? cont)
- (replace cont (detach (call-arg (lambda-body cont) 0)))
- (set-call-primop! call (get-primop (enum primop-enum tail-call)))
- (set-call-exits! call 0))
- (else
- (default-simplifier call)))))
- ;; (CALL (CONT (v1 ... vN) (RETURN c v1 ... vN)) ...args...)
- (define (trivial-continuation? cont)
- (let ((body (lambda-body cont)))
- (and (calls-this-primop? body 'return)
- (= (length (lambda-variables cont))
- (- (call-arg-count body ) 1))
- (let loop ((vars (lambda-variables cont)) (i 1))
- (cond ((null? vars)
- #t)
- ((and (reference-node? (call-arg body i))
- (eq? (car vars)
- (reference-variable (call-arg body i))))
- (loop (cdr vars) (+ i 1)))
- (else #f))))))
- ;; The same as the above, except that the continuation is a reference node
- ;; and not a lambda, so we substitute it for the proc's continuation variable.
- (define (simplify-known-tail-call call)
- (let ((proc (call-arg call 1))
- (cont (call-arg call 0)))
- (cond ((lambda-node? proc)
- (set-call-primop! call (get-primop (enum primop-enum let)))
- (change-lambda-type proc 'cont)
- (substitute (car (lambda-variables proc)) cont #t)
- (set-lambda-variables! proc (cdr (lambda-variables proc)))
- (remove-call-arg call 0)
- (set-call-exits! call 1) ;; must be after REMOVE-CALL-ARG
- (mark-changed proc))
- (else
- (default-simplifier call)))))
- (define (simplify-test call)
- (simplify-arg call 2)
- (let ((value (call-arg call 2)))
- (cond ((literal-node? value)
- (fold-conditional call (if (eq? false-value (literal-value value))
- 1
- 0)))
- ((reference-node? value)
- (simplify-variable-test call (reference-variable value)))
- ((collapse-multiple-zero-bit-tests call)
- )
- (else
- (default-simplifier call)))))
- (define (simplify-variable-test call var)
- (cond ((flag-assq 'test (variable-flags var))
- => (lambda (pair)
- (fold-conditional call (cdr pair))))
- (else
- (let ((pair (cons 'test 0))
- (flags (variable-flags var)))
- (set-variable-flags! var (cons pair flags))
- (simplify-arg call 0)
- (set-cdr! pair 1)
- (simplify-arg call 1)
- (set-variable-flags! var flags)))))
- (define (fold-conditional call index)
- (replace-body call (detach-body (lambda-body (call-arg call index)))))
- ;; (if (and (= 0 (bitwise-and 'j x))
- ;; (= 0 (bitwise-and 'j y)))
- ;; ...)
- ;; =>
- ;; (if (= 0 (bitwise-and (bitwise-or x y) 'j))
- ;; ...)
- ;; This comes up in the Scheme48 VM.
- (define (collapse-multiple-zero-bit-tests test)
- (receive (mask first-arg)
- (zero-bit-test (call-arg test 2))
- (if mask
- (let ((false-exit (call-arg test 1))
- (true-exit (call-arg test 0)))
- (simplify-lambda-body true-exit)
- (simplify-lambda-body false-exit)
- (let ((call (lambda-body true-exit)))
- (if (and (eq? 'test (primop-id (call-primop call)))
- (node-equal? false-exit (call-arg call 1)))
- (receive (new-mask second-arg)
- (zero-bit-test (call-arg call 2))
- (if (and new-mask (= mask new-mask))
- (fold-zero-bit-tests test first-arg second-arg
- (call-arg call 0))
- #f))
- #f)))
- #f)))
- ;; = and bitwise-and always have any literal node as arg1
- ;;
- ;; 1. call to =
- ;; 2. first arg is literal 0
- ;; 3. second arg is call to and
- ;; 4. first arg of and-call is numeric literal
- ;; 5. second arg of and-call has no side-effects (reads are okay)
- ;; Returns #f or the two arguments to bitwise-and.
- (define (zero-bit-test call)
- (if (eq? '= (primop-id (call-primop call)))
- (let ((literal-0 (call-arg call 0))
- (bitwise-and-call (call-arg call 1)))
- (if (and (literal-node? literal-0)
- (number? (literal-value literal-0))
- (= 0 (literal-value literal-0))
- (call-node? bitwise-and-call)
- (eq? 'bitwise-and (primop-id (call-primop bitwise-and-call)))
- (literal-node? (call-arg bitwise-and-call 0))
- (number? (literal-value (call-arg bitwise-and-call 0)))
- (not (side-effects? (call-arg bitwise-and-call 1) 'read)))
- (values (literal-value (call-arg bitwise-and-call 0))
- (call-arg bitwise-and-call 1))
- (values #f #f)))
- (values #f #f)))
- (define (fold-zero-bit-tests test first-arg second-arg true-cont)
- (detach second-arg)
- (replace (call-arg test 0) (detach true-cont))
- (move first-arg
- (lambda (first-arg)
- (let-nodes ((call (bitwise-ior 0 first-arg second-arg)))
- call))))
- (define (expand-test call)
- (bug "Trying to expand a call to TEST (~D) ~S"
- (node-hash (node-parent (nontrivial-ancestor call)))
- call))
- ;; TEST can be simplified using any literal value.
- ;; The check for reference nodes is a heuristic. It will only help if the
- ;; two tests end up being sequential.
- (define (simplify-test? call index value)
- (cond ((literal-node? value)
- #t)
- ((reference-node? value)
- (any? (lambda (r)
- (eq? 'test (primop-id (call-primop (node-parent r)))))
- (variable-refs (reference-variable value))))
- (else
- #f)))
- (define (simplify-unknown-call call)
- (simplify-args call 0)
- (let ((proc (call-arg call 1)))
- (cond ((lambda-node? proc)
- (determine-lambda-protocol proc (list proc))
- (mark-changed proc))
- ((and (reference-node? proc)
- (variable-simplifier (reference-variable proc)))
- => (lambda (proc)
- (proc call))))))
- ;; Simplify a cell. A set-once cell is one that is set only once and does
- ;; not escape. If such a cell is set to a value that can be hoisted (without
- ;; moving variables out of scope) to the point the cell is created the cell
- ;; is replace with the value.
- ;; This should make use of the type of the cell.
- (define (simplify-allocation call)
- (set-node-simplified?! call #t)
- (simplify-args call 0) ;; simplify all arguments, including continuation
- (let ((var (car (lambda-variables (call-arg call 0)))))
- (if (every? cell-use? (variable-refs var))
- (receive (uses sets)
- (partition-list (lambda (n)
- (eq? 'contents
- (primop-id (call-primop (node-parent n)))))
- (variable-refs var))
- (simplify-cell-part call uses sets)))))
- (define (cell-use? ref)
- (let ((call (node-parent ref)))
- (case (primop-id (call-primop call))
- ((contents)
- #t)
- ((set-contents)
- (= (node-index ref) set/owner))
- (else
- #f))))
- (define (simplify-cell-part call my-uses my-sets)
- (cond ((null? my-uses)
- (for-each (lambda (n) (remove-body (node-parent n)))
- my-sets))
- ((null? my-sets)
- (for-each (lambda (n)
- (replace-call-with-value
- (node-parent n)
- (make-undefined-literal)))
- my-uses))
- ;; ((null? (cdr my-sets))
- ;; (set-literal-value! (call-arg call 1) 'single-set)
- ;; (really-simplify-single-set call (car my-sets) my-uses))
- (else
- (if (neq? 'small (literal-value (call-arg call 1)))
- (set-literal-value! (call-arg call 1) 'small)))))
|