home *** CD-ROM | disk | FTP | other *** search
- ;;; Following the expansion of the program, program optimization via
- ;;; transformation is done by this module. Boolean expressions are "short-
- ;;; circuited, and some applications of lambda expressions are rearranged. For
- ;;; more information on these transformations, consult section 3.4 of "ORBIT:
- ;;; An Optimizing Compiler for Scheme", 1986 ACM Compiler Conference.
- ;;;
-
- ;* Copyright 1989 Digital Equipment Corporation
- ;* All Rights Reserved
- ;*
- ;* Permission to use, copy, and modify this software and its documentation is
- ;* hereby granted only under the following terms and conditions. Both the
- ;* above copyright notice and this permission notice must appear in all copies
- ;* of the software, derivative works or modified versions, and any portions
- ;* thereof, and both notices must appear in supporting documentation.
- ;*
- ;* Users of this software agree to the terms and conditions set forth herein,
- ;* and hereby grant back to Digital a non-exclusive, unrestricted, royalty-free
- ;* right and license under any changes, enhancements or extensions made to the
- ;* core functions of the software, including but not limited to those affording
- ;* compatibility with other hardware or software environments, but excluding
- ;* applications which incorporate this software. Users further agree to use
- ;* their best efforts to return to Digital any such changes, enhancements or
- ;* extensions that they make and inform Digital of noteworthy uses of this
- ;* software. Correspondence should be provided to Digital at:
- ;*
- ;* Director of Licensing
- ;* Western Research Laboratory
- ;* Digital Equipment Corporation
- ;* 100 Hamilton Avenue
- ;* Palo Alto, California 94301
- ;*
- ;* This software may be distributed (but not offered for sale or transferred
- ;* for compensation) to third parties, provided such third parties agree to
- ;* abide by the terms and conditions of this notice.
- ;*
- ;* THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
- ;* WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
- ;* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
- ;* CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
- ;* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
- ;* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
- ;* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
- ;* SOFTWARE.
-
- (module transform)
-
- ;;; External and in-line declarations.
-
- (include "plist.sch")
- (include "expform.sch")
- (include "lambdaexp.sch")
- (include "miscexp.sch")
-
- ;;; Each form is transformed by calling the TRANSFORM function. The value
- ;;; returned is the new form.
-
- (define TRANSFORM-STACK '())
-
- (define (TRANSFORM exp)
- (let ((was transform-stack))
- (set! transform-stack (cons exp transform-stack))
- (let ((result (transformx exp)))
- (set! transform-stack was)
- result)))
-
- (define (TRANSFORMX exp)
- (cond (($call? exp)
- (set-$call-func! exp (transform ($call-func exp)))
- (set-$call-argl! exp (map transform ($call-argl exp)))
- (if ($lambda? ($call-func exp)) (transform-call-lambda exp) exp))
- (($lambda? exp)
- (let ((old-current-lambda-id current-lambda-id))
- (set! current-lambda-id ($lambda-id exp))
- (set-$lambda-body! exp (map transform ($lambda-body exp)))
- (set! current-lambda-id old-current-lambda-id)
- exp))
- (($if? exp)
- (or (transform-if1 exp)
- (begin (set-$if-test! exp (transform ($if-test exp)))
- (set-$if-true! exp (transform ($if-true exp)))
- (set-$if-false! exp (transform ($if-false exp)))
- (transform-if2 exp))))
- (($define? exp)
- (set-$define-exp! exp (transform ($define-exp exp)))
- exp)
- (($set? exp)
- (set-$set-exp! exp (transform ($set-exp exp)))
- exp)
- (else exp)))
-
- ;;; When a $IF is detected, the following function checks for possible
- ;;; transformations on the whole expression. If they can be made, then the
- ;;; resulting expression will be transformed and then returned. If no such
- ;;; transformations can be done, then #F will be returned.
-
- (define (TRANSFORM-IF1 exp)
- (let ((test ($if-test exp))
- (ift ($if-true exp))
- (iff ($if-false exp)))
- (set! transform-stack (cons (list 'if1 exp) transform-stack)) ;;; ***
- (cond ((and ($call? test) ($lambda? ($call-func test)))
- ; (if (let ((...))...a) b c) => (let ((...))...(if a b c))
- (log-before exp)
- (let ((last (last-pair ($lambda-body ($call-func test)))))
- (set-car! last `($if ,(car last) ,ift ,iff))
- (transform (log-after test))))
- ((and ($if? test) (eq? ($if-true test) true-alpha)
- (eq? ($if-false test) false-alpha))
- ; (if (if a #t #f) b c) => (if a b c)
- (log-before exp)
- (set-$if-test! exp ($if-test test))
- (transform (log-after exp)))
- ((and ($if? test) (eq? ($if-true test) false-alpha)
- (eq? ($if-false test) true-alpha))
- ; (if (if a #f #t) b c) => (if a c b)
- (log-before exp)
- (set-$if-test! exp ($if-test test))
- (set-$if-true! exp iff)
- (set-$if-false! exp ift)
- (transform (log-after exp)))
- (($if? test)
- ; (if (if a b c) d e) => (if a (if b d e) (if c d e))
- ; => ((lambda (x y) (if a
- ; (if b (x) (y))
- ; (if c (x) (y))))
- ; (lambda () d) (lambda () e))
- (log-before exp)
- (let* ((lxy (lambda-exp '(lambda (x y)) '()))
- (lxyid ($lambda-id lxy))
- (x (car (lambda-reqvars lxyid)))
- (y (cadr (lambda-reqvars lxyid)))
- (ld (lambda-exp '(lambda ()) '()))
- (le (lambda-exp '(lambda ()) '())))
- (set-$lambda-body! ld (list ift))
- (set-$lambda-body! le (list iff))
- (set-$lambda-body! lxy
- `(($if ,($if-test test)
- ($if ,($if-true test)
- ($call () ,x)
- ($call () ,y))
- ($if ,($if-false test)
- ($call () ,x)
- ($call () ,y)))))
- (name-a-lambda x ld)
- (name-a-lambda y le)
- (transform (log-after `($call () ,lxy ,ld ,le)))))
- ((and (symbol? test) (eq? (id-type test) 'boolean)
- (eq? ift test))
- ; (if a a b) => (if a #t y) when a is a boolean result
- (log-before exp)
- (set-$if-true! exp true-alpha)
- (transform (log-after exp)))
- ((and (symbol? test) (eq? (id-type test) 'boolean)
- (eq? iff test))
- ; (if a b a) => (if a b #f) when a is a boolean result
- (log-before exp)
- (set-$if-false! exp false-alpha)
- (transform (log-after exp)))
- ((and (eq? ($lap-type ($call-func test)) 'boolean)
- (or (and (eq? ift true-alpha) (eq? iff false-alpha))
- (and (eq? ift false-alpha) (eq? iff true-alpha))))
- ; (if (lap-boolean) #t #f) => (lap-boolean)
- ; (if (lap-boolean) #f #t) => (not (lap-boolean))
- (log-before exp)
- (if (eq? iff true-alpha)
- (let ((lap ($call-func test)))
- (set-$lap-body! lap
- `((boolean (not ,(cadar ($lap-body lap))))))))
- (transform (log-after test)))
- (else #f))))
-
- ;;; Simplifications on a transformed if form are done by the following
- ;;; function. The result will be the final transformed expression.
-
- (define (TRANSFORM-IF2 exp)
- (let ((test ($if-test exp))
- (ift ($if-true exp))
- (iff ($if-false exp)))
- (set! transform-stack (cons (list 'if2 exp) transform-stack)) ;;; ***
- (cond ((not ($if? exp)) exp)
- ((and (symbol? test) (eq? (id-use test) 'constant))
- ; test is a constant, so evaluate at compile time.
- (log-before exp)
- (transform-if2 (log-after (if (id-value test) ift iff))))
- ((or (eq? test true-alpha) (eq? test false-alpha))
- ; test is "#t" or "#f" whose values are known.
- (log-before exp)
- (transform-if2 (log-after (if (eq? test true-alpha) ift iff))))
- ((and (symbol? test) ($if? ift) (eq? ($if-test ift) test))
- ; (if a (if a b c) d) => (if a b d)
- (log-before exp)
- (set-$if-true! exp ($if-true ift))
- (transform-if2 (log-after exp)))
- ((and (symbol? test) ($if? iff) (eq? ($if-test iff) test))
- ; (if a b (if a c d)) => (if a b d)
- (log-before exp)
- (set-$if-false! exp ($if-false iff))
- (transform-if2 (log-after exp)))
- (else exp))))
-
- ;;; When a transformation is going to be made, the following routine is called
- ;;; to log the result.
-
- (define (LOG-BEFORE exp)
- (if (log? 'transform)
- (begin (pretty-print-$tree exp sc-icode)
- (format sc-icode " => ~%"))))
-
- ;;; Once a transformation has been made, the result is logged by the following
- ;;; function.
-
- (define (LOG-AFTER exp)
- (if (log? 'transform)
- (begin (pretty-print-$tree exp sc-icode)
- (format sc-icode "~%~%")))
- exp)
-
- ;;; When a LAMBDA expression is apply'ed, some of the lambda bindings may be
- ;;; eliminated by using the value being bound instead.
-
- (define (TRANSFORM-CALL-LAMBDA exp)
- (let* ((lid ($lambda-id ($call-func exp)))
- (alist (transform-lambda-bind (lambda-reqvars lid)
- ($call-argl exp)))
- (vars (map (lambda (var-value) (car var-value)) alist))
- (values (map (lambda (var-value) (cadr var-value)) alist))
- (body ($lambda-body ($call-func exp)))
- (redo #f)
- (newvars '())
- (newargl '())
- (sublis '()))
- (set! transform-stack (cons (list 'tcl exp) transform-stack)) ;;; ***
- (for-each (lambda (var-val)
- (let ((id (car var-val)))
- (set-id-refs! id 0)
- (set-id-calls! id 0)))
- alist)
- (if vars
- (for-each (lambda (exp) (count-lambda-var-uses vars exp)) body))
- (for-each
- (lambda (var)
- (let* ((value (car values))
- (old-new (transform-lambda-var var value body)))
- (cond ((eq? old-new 'no-value)
- (set! value old-new))
- ((eq? old-new 'no-change))
- ((eq? old-new 'boolean)
- (set! redo #t))
- ((eq? (car old-new) 'both)
- (set! value (cadr old-new))
- (set! sublis (cons (cddr old-new) sublis)))
- (else
- (set! sublis (cons old-new sublis))
- (set! var '())))
- (if var
- (begin (set! newvars (cons var newvars))
- (set! newargl (cons value newargl))))
- (set! values (cdr values))))
- vars)
- (if sublis (set! body (transform-var-to-value lid body sublis)))
- (cond ((and (null? newvars) (= (length body) 1))
- (if (log? 'transform)
- (format sc-icode "Lambda ~A collapsed~%" lid))
- (set-lambda-generate! lid 'inline)
- (set! exp (car body)))
- (else
- (set-lambda-reqvars! lid newvars)
- (set-$call-argl! exp newargl)
- exp))
- (if (or sublis redo) (transform exp) exp)))
-
- ;;; Build an a-list of the lambda variables and their initial bindings.
-
- (define (TRANSFORM-LAMBDA-BIND vars values)
- (cond ((null? vars)
- '())
- ((pair? vars)
- (cons (list (car vars) (car values))
- (transform-lambda-bind (cdr vars) (cdr values))))))
-
- ;;; Count the variable uses for a list of variables in an expression. The
- ;;; counts maintained are ID-REFS and ID-CALLS.
-
- (define (COUNT-LAMBDA-VAR-USES vars exp)
- (cond ((symbol? exp)
- (if (memq exp vars) (set-id-refs! exp (+ 1 (id-refs exp)))))
- (($define? exp)
- (count-lambda-var-uses vars ($define-exp exp)))
- (($call? exp)
- (let ((func ($call-func exp)))
- (for-each (lambda (a) (count-lambda-var-uses vars a))
- ($call-argl exp))
- (cond (($lambda? func)
- (count-lambda-var-uses vars ($call-func exp)))
- ((memq func vars)
- (set-id-calls! func (+ 1 (id-calls func)))))))
- (($set? exp)
- (count-lambda-var-uses vars ($set-exp exp)))
- (($lambda? exp)
- (for-each (lambda (e) (count-lambda-var-uses vars e))
- ($lambda-body exp)))
- (($if? exp)
- (count-lambda-var-uses vars ($if-test exp))
- (count-lambda-var-uses vars ($if-true exp))
- (count-lambda-var-uses vars ($if-false exp)))))
-
- ;;; Once the usage counts have been obtained, the following function is called
- ;;; to decide whether substitution is in order. If so, then it will return
- ;;; either "no-value" which indicates that the value is not needed, or a list
- ;;; of old and new values to be substitued for in the expression, or
- ;;; "no-change" indicating that nothing is to be changed.
-
- (define (TRANSFORM-LAMBDA-VAR var value exp)
- (let ((refs (id-refs var))
- (calls (id-calls var))
- (body ($lambda-body value))
- (memvarlist (lambda (var symbols)
- (do ((symbols symbols (cdr symbols))
- (found #f (or found (eq? (car symbols)
- var))))
- ((or (not symbols)
- (not (symbol? (car symbols))))
- (and found (null? symbols)))))))
- (cond ((or (id-set! var) (id-display var))
- ; If the lambda var is set or heap allocated, then it is best
- ; left alone.
- 'no-change)
- ((and ($lambda? value) (= calls 1) (zero? refs))
- ; A lambda expression which is called once should be moved to
- ; the point of call.
- (log-transform var " replaced by lambda " ($lambda-id value))
- (list var value))
- ((and body (zero? refs) (= 1 (length body)) (symbol? (car body))
- (not (id-display (car body))))
- ; A function with no arguments which returns the value of a
- ; symbol which is not heap allocated can have all calls to it
- ; replaced with the actual symbol.
- (log-transform `($call () ,var) " replaced by " (car body))
- (list `($call () ,var) (car body)))
- ((and (symbol? value)
- (or (eq? value true-alpha)
- (eq? value false-alpha)
- (eq? (id-use value) 'constant)
- (and (eq? (id-use value) 'lexical)
- (not (id-display value))
- (not (id-set! value)))))
- ; A constant or a lexical variable which is not set and not
- ; closed may be substituted for.
- (log-transform var " replaced by " value)
- (list var value))
- ((and ($if? (car exp)) (= refs 1) (zero? calls)
- (or (eq? ($if-test (car exp)) var)
- (memvarlist var ($call-argl ($if-test (car exp))))))
- ; An expression which is then used as the test in an initial IF
- ; can be substituted for. The test is either the variable, or
- ; a variable to a function which is the test which only has
- ; variables as arguments.
- (log-transform var " replaced by " value)
- (list var value))
- ((and ($call? (car exp)) (= refs 1) (zero? calls)
- (memvarlist var ($call-argl (car exp))))
- ; An expression which is used once as an argument to an inital
- ; function may be substituted for if the arguments to the
- ; function are all symbols.
- (log-transform var " replaced by " value)
- (list var value))
- ((and ($call? value) ($lap? ($call-func value))
- (not (id-type var))
- (pair? (car (last-pair ($lap-body ($call-func value)))))
- (eq? (caar (last-pair ($lap-body ($call-func value))))
- 'boolean))
- ; A variable which is bound to a logical boolean can have
- ; it's type noted.
- (set-id-type! var 'boolean)
- 'boolean)
- (else 'no-change))))
-
- ;;; Transformations done when lambda expressions are apply'ed are logged by the
- ;;; following function.
-
- (define (LOG-TRANSFORM . exp)
- (if (log? 'transform)
- (begin (for-each (lambda (e) (format sc-icode "~A" e)) exp)
- (newline sc-icode))))
-
- ;;; Once the transformations have been figured out, the actual substitutions
- ;;; can be made. Note the one special case where a lambda expression replaces
- ;;; its variable in a call. This will require that TRANSFORM-CALL-LAMBDA be
- ;;; recursively invoked as more transformations may be possible.
-
- (define (TRANSFORM-VAR-TO-VALUE lid exp sublis)
- (let ((old-new (assoc exp sublis)))
- (cond (old-new
- (transform-var-to-value lid (cadr old-new) sublis))
- (($call? exp)
- (let* ((old ($call-func exp))
- (new (transform-var-to-value lid old sublis)))
- (set-$call-func! exp new)
- (set-$call-argl! exp
- (transform-var-to-value lid ($call-argl exp) sublis))
- (if (or (eq? old new) (not ($lambda? new)))
- exp
- (transform-call-lambda exp))))
- ((and (pair? exp) (not ($lap? exp)))
- (if ($lambda? exp) (set! lid ($lambda-id exp)))
- (set-car! exp (transform-var-to-value lid (car exp) sublis))
- (set-cdr! exp (transform-var-to-value lid (cdr exp) sublis))
- exp)
- (else exp))))
-