File: call.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 (252 lines) | stat: -rw-r--r-- 8,305 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
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
; Copyright (c) 1993-2008 by Richard Kelsey.  See file COPYING.


(define (simplify-jump call)
  (cond ((lambda-node? (call-arg call 0))
	 (set-call-primop! call (get-primop (enum primop 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 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 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 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 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)))))