File: join.scm

package info (click to toggle)
scheme48 1.8%2Bdfsg-1%2Bdeb7u1
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 14,984 kB
file content (223 lines) | stat: -rw-r--r-- 7,344 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
; Copyright (c) 1993-2008 by Richard Kelsey.  See file COPYING.


; Call JOIN-SUBSTITUTE on all variable/value pairs.

(define (substitute-join-arguments lambda-proc call)
  (let ((vec (call-args call))
        (vars (lambda-variables lambda-proc)))
    (do ((vars vars (cdr vars))
         (i    1   (+ i 1))
         (c?   #f  (or (join-substitute (car vars) (vector-ref vec i))
                        c?)))
        ((null? vars) c?))))

; Does VAL take only one argument and is that argument passed to $TEST?
; Is VAR applied to constants?
; Then two possiblities are checked for:
;   Does the tree rooted at the least-common-ancestor of VAR's references
;   contain no side-effects and necessarily passed control to VAR?
;  or
;   Does the join point contain no side-effects above the test?
;
; If so, make the transformation described below.

(define (join-substitute var val)
  (let ((ref (and (lambda-node? val)
		  (simple-test-procedure val))))
    (and ref
	 (applied-to-useful-value? var ref)
	 (let ((lca (least-common-ancestor (variable-refs var))))
	   (cond ((or (suitable-join-conditional? lca var)
		      (suitable-join-point? val (node-parent ref)))
		  (really-join-substitute var val lca (node-parent ref))
		  #t)
		 (else #f))))))

; Check that VAL (a lambda-node) takes one argument, is jumped to, tests its
; argument, and that all references to the argument are at or below the test.

(define (simple-test-procedure val)
  (let ((vars (lambda-variables val)))
    (if (or (null? vars)
	    (not (null? (cdr vars)))
	    (not (car vars))
	    (not (calls-known? val))
	    (neq? 'jump (lambda-type val)))
	#f
	(let* ((var (car vars))
	       (ref (any simple-cond-ref (variable-refs var))))
	  (if (and ref (all-refs-below? var (node-parent ref)))
	      ref
	      #f)))))

(define (simple-cond-ref ref)
  (if (primop-conditional? (call-primop (node-parent ref)))
      ref
      #f))

(define (all-refs-below? var node)
  (set-node-flag! node #t)
  (set-node-flag! (variable-binder var) #t)
  (let ((res (every? (lambda (r)
		       (eq? node (marked-ancestor r)))
		     (variable-refs var))))
    (set-node-flag! node #f)
    (set-node-flag! (variable-binder var) #f)
    res))

; Is VAR applied to something that can be used to simplify the conditional?

(define (applied-to-useful-value? var ref)
  (let ((call (node-parent ref))
	(index (node-index ref)))
    (any? (lambda (r)
	    (simplify-conditional? call index (call-arg (node-parent r) 1)))
	  (variable-refs var))))

; CALL is the least-common-ancestor of the references to VAR.  Check that
; the tree rooted at CALL contains no side-effects and that the control flow
; necessarily passes to VAR.  (Could check for undefined-effect here...)
; could do check that jumped-to proc if not VAR jumped to VAR eventually

(define (suitable-join-conditional? call var)
  (let label ((call call))
    (cond ((call-side-effects? call)
	   #f)
	  ((= 0 (call-exits call))
	   (and (eq? 'jump (primop-id (call-primop call)))
		(eq? var (reference-variable (called-node call)))))
	  (else
	   (let loop ((i 0))
	     (cond ((>= i (call-exits call))
		    #t)
		   ((not (label (lambda-body (call-arg call i))))
		    #f)
		   (else
		    (loop (+ i 1)))))))))

; #t if CALL performs side-effects.  The continuations to CALL are ignored.

(define (call-side-effects? call)
  (or (primop-side-effects (call-primop call))
      (let loop ((i (call-exits call)))
	(cond ((>= i (call-arg-count call))
	       #f)
	      ((side-effects? (call-arg call i))
	       #t)
	      (else
	       (loop (+ i 1)))))))

; The alternative to the above test: does the join point contain no side-effects
; above the test?

(define (suitable-join-point? join test)
  (let label ((call (lambda-body join)))
    (cond ((eq? call test)
	   #t)
	  ((call-side-effects? call)
	   #f)
	  (else
	   (let loop ((i 0))
	     (cond ((>= i (call-exits call))
		    #t)
		   ((not (label (lambda-body (call-arg call i))))
		    #f)
		   (else
		    (loop (+ i 1)))))))))
	     
; (let ((j (lambda (v)                  ; VAR VAL
;            .a.
;            ($test c1 c2 ... v ...)    ; TEST
;            .b.)))
;   .c.
;   (... (j x) ...)                     ; CALL
;   .d.)
; ==>
; .c.
; (.a.
;  (let ((v1 (lambda (x) c1[x/v]))
;        (v2 (lambda (x) c2[x/v])))
;    (... ((lambda (v)
;            ($test (lambda () (v1 v)) (lambda () (v2 v)) ... v ...))
;          x)
;     ...))
;  .b.)
; .d.
;
; CALL is the least common ancestor of the references to VAR, which is bound to
; VAL, a procedure.  TEST is a conditional that tests the argument passed to
; VAL.
;
; (lambda-body VAL) is moved to where CALL is.
; In the body of VAL, TEST is replaced by a LET that binds TEST's continuations
; and then executes CALL.  TEST's continuations are replaced by calls to
; the variables bound by the LET.
; Finally, references to VAR are replaced by a procedure whose body is TEST,
; which is the point of the whole exercise.

(define (really-join-substitute var val call test)
  (let ((value-var (car (lambda-variables val))))
    (receive (cont-call conts)
	(move-continuations test call value-var)
      (let ((test-parent (node-parent test))
	    (val-parent (node-parent val))
	    (val-index (node-index val)))
	(parameterize-continuations conts value-var)
	(detach-body test)
	(move-body cont-call
		   (lambda (cont-call)
		     (attach-body test-parent cont-call)
		     (detach-body (lambda-body val))))
	(attach-body val test)
	(mark-changed (call-arg test 1)) ; marks test as changed.
	(mark-changed cont-call)
	(substitute var val #t)
	(attach val-parent val-index (make-literal-node #f #f))
	(values)))))

; Move the continuations of CALL to a LET call just above TO.  Returns a list
; of the variables now bound to the continuations and the continuations
; themselves.

(define (move-continuations call to arg-var)
  (let ((count (call-exits call)))
    (let loop ((i (- count 1)) (vs '()) (es '()))
      (cond ((< i 0)
	     (let ((new-call (make-call-node (get-primop (enum primop let))
					     (+ count 1)
					     1))
		   (new-proc (make-lambda-node 'j 'cont vs)))
	       (attach-call-args new-call (cons new-proc es))
	       (insert-body new-call new-proc (node-parent to))
	       (values new-call es)))
	    (else
	     (let ((var (make-variable 'e (node-type (call-arg call i))))
		   (cont (detach (call-arg call i))))
	       (let-nodes ((new-cont () c1)
			   (c1 (jump 0 (* var) (* arg-var))))
		 (attach call i new-cont))
	       (change-lambda-type cont 'jump)
	       (loop (- i 1) (cons var vs) (cons cont es))))))))

; Add a new variable to each of CONTS and substitute a reference to the correct
; variable for each reference to VAR within CONTS.

(define (parameterize-continuations conts var)
  (for-each (lambda (n)
	      (let ((var (copy-variable var)))
		(set-lambda-variables! n (cons var (lambda-variables n)))
		(set-variable-binder! var n)
		(set-node-flag! n #t)))
	    conts)
  (let ((backstop (variable-binder var)))
    (set-node-flag! backstop #t)
    (walk-refs-safely
     (lambda (n)
       (let ((cont (marked-ancestor n)))
	 (if (not (eq? cont backstop))
	     (replace n (make-reference-node (car (lambda-variables cont)))))))
     var)
    (set-node-flag! backstop #f)
    (for-each (lambda (n) (set-node-flag! n #f)) conts)
    (values)))