1 ;;;; This file implements the stack analysis phase in the compiler. We
2 ;;;; determine which unknown-values and stack continuations are on the
3 ;;;; stack at each point in the program, and then we insert cleanup
4 ;;;; code to remove unused values.
6 ;;;; This software is part of the SBCL system. See the README file for
9 ;;;; This software is derived from the CMU CL system, which was
10 ;;;; written at Carnegie Mellon University and released into the
11 ;;;; public domain. The software is in the public domain and is
12 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
13 ;;;; files for more information.
17 ;;; Scan through BLOCK looking for uses of :UNKNOWN lvars that have
18 ;;; their DEST outside of the block. We do some checking to verify the
19 ;;; invariant that all pushes come after the last pop.
20 (defun find-pushed-lvars (block)
21 (let* ((2block (block-info block
))
22 (popped (ir2-block-popped 2block
))
24 (lvar-dest (car (last popped
)))
28 (do-nodes (node lvar block
)
29 (when (eq node last-pop
)
33 (let ((dest (lvar-dest lvar
))
34 (2lvar (lvar-info lvar
)))
36 (or (and (not (eq (node-block dest
) block
))
37 (eq (ir2-lvar-kind 2lvar
) :unknown
))
38 (eq (ir2-lvar-kind 2lvar
) :stack
)))
39 (aver (or saw-last
(not last-pop
)))
42 (setf (ir2-block-pushed 2block
) (pushed))))
45 ;;;; Computation of live lvar sets
47 ;;; Add LVARs from LATE to EARLY; use EQ to check whether EARLY has
49 (defun merge-lvar-live-sets (early late
)
50 (declare (type list early late
))
52 (dolist (e late early
)
55 ;;; Update information on stacks of unknown-values and stack LVARs on
56 ;;; the boundaries of BLOCK. Return true if the start stack has been
59 ;;; An LVAR is live at the end iff it is live at any of blocks which
60 ;;; BLOCK can transfer control to, or it is a stack lvar kept live
61 ;;; within the extent of its cleanup. There are two kind of control
62 ;;; transfers: normal, expressed with BLOCK-SUCC, and NLX. We also
63 ;;; preserve any stack lvars on the stack when an lvar in a
64 ;;; PRESERVE-INFO set (representing a stack allocated object, not
65 ;;; necessarily a stack lvar) gets pushed on the stack. PUSHED does
66 ;;; not track this set because it only tracks stack lvars to make
67 ;;; liveness analysis feasible with conditional stack allocation. We
68 ;;; must do this because stack allocated objects can't move; object
69 ;;; identity must be preserved and we can't in general track all
71 (defun update-lvar-live-sets (block)
72 (declare (type cblock block
))
73 (let* ((2block (block-info block
))
74 (original-start (ir2-block-start-stack 2block
))
75 (end (ir2-block-end-stack 2block
))
77 (dolist (succ (block-succ block
))
78 (setq new-end
(merge-lvar-live-sets new-end
79 (ir2-block-start-stack (block-info succ
)))))
80 (do-nested-cleanups (cleanup block
)
81 (case (cleanup-kind cleanup
)
82 ((:block
:tagbody
:catch
:unwind-protect
)
83 (dolist (nlx-info (cleanup-nlx-info cleanup
))
84 (let* ((target (nlx-info-target nlx-info
))
85 (target-start-stack (ir2-block-start-stack
87 (exit-lvar (nlx-info-lvar nlx-info
))
88 (next-stack (if exit-lvar
89 (remove exit-lvar target-start-stack
)
91 (setq new-end
(merge-lvar-live-sets
92 new-end next-stack
)))))
94 (let* ((dynamic-extent (cleanup-mess-up cleanup
))
95 (info (dynamic-extent-info dynamic-extent
)))
97 (pushnew info new-end
))
98 (dolist (preserve (dynamic-extent-preserve-info dynamic-extent
))
99 (when (memq preserve
(ir2-block-stack-mess-up 2block
))
100 (pushnew preserve new-end
)))))))
102 (setf (ir2-block-end-stack 2block
) new-end
)
104 (let ((start new-end
))
105 (setq start
(set-difference start
(ir2-block-pushed 2block
)))
106 (setq start
(merge-lvar-live-sets start
(ir2-block-popped 2block
)))
108 (when *check-consistency
*
109 (aver (subsetp original-start start
)))
110 (cond ((subsetp start original-start
)
113 (setf (ir2-block-start-stack 2block
) start
)
117 ;;;; Ordering of live lvar stacks
119 ;;; Do a forward walk in the flow graph and put LVARs on the start/end
120 ;;; stacks of BLOCK in the right order. STACK is an already sorted
121 ;;; stack coming from a predecessor of BLOCK. Because all LVARs live
122 ;;; at the start of BLOCK are on STACK, we just need to remove dead
123 ;;; LVARs. As an optimization we only do this above the top-most stack
124 ;;; LVAR, since nothing allocated before it can be dead.
125 (defun order-lvar-sets-walk (block stack
)
126 (unless (block-flag block
)
127 (setf (block-flag block
) t
)
128 (let* ((2block (block-info block
))
129 (start (ir2-block-start-stack 2block
))
132 (do ((tail stack
(cdr tail
)))
133 ((null tail
) (prefix))
134 (let ((lvar (car tail
)))
135 (when (memq lvar start
)
136 (when (eq (ir2-lvar-kind (lvar-info lvar
)) :stack
)
137 (return (append (prefix) tail
)))
139 (aver (subsetp start start-stack
))
140 (setf (ir2-block-start-stack 2block
) start-stack
)
142 (let* ((last (block-last block
))
143 (tailp-lvar (if (and (basic-combination-p last
)
146 (end-stack start-stack
))
147 (dolist (pop (ir2-block-popped 2block
))
148 (aver (eq pop
(car end-stack
)))
150 (dolist (push (ir2-block-pushed 2block
))
151 (aver (not (memq push end-stack
)))
152 (push push end-stack
))
153 (aver (subsetp (ir2-block-end-stack 2block
) end-stack
))
154 (when (and tailp-lvar
155 (eq (ir2-lvar-kind (lvar-info tailp-lvar
)) :unknown
))
156 (aver (eq tailp-lvar
(first end-stack
)))
158 (setf (ir2-block-end-stack 2block
) end-stack
)
159 (do-nested-cleanups (cleanup block
)
160 (dolist (nlx-info (cleanup-nlx-info cleanup
))
161 (order-lvar-sets-walk (nlx-info-target nlx-info
)
163 (dolist (succ (block-succ block
))
164 (order-lvar-sets-walk succ end-stack
)))))
168 ;;; Do a forward walk in the flow graph and insert calls to
169 ;;; %DYNAMIC-EXTENT-START whenever we mess up the run-time stack by
170 ;;; allocating a dynamic extent object. BLOCK is the block that is
171 ;;; currently being walked and STACK is the stack of :STACK
172 ;;; lvars. This allows cleanup code inserted by DISCARD-UNUSED-VALUES
173 ;;; to reset the stack to the right place.
174 (defun stack-mess-up-walk (block stack
)
175 (declare (type cblock block
) (list stack
))
176 (unless (block-flag block
)
177 (setf (block-flag block
) t
)
178 (setf (ir2-block-stack-mess-up (block-info block
)) stack
)
179 (let ((2comp (component-info (block-component block
))))
180 (do-nodes (node lvar block
)
181 (let ((dynamic-extent
183 (enclose (enclose-dynamic-extent node
))
184 (cdynamic-extent node
)
185 (t (and lvar
(lvar-dynamic-extent lvar
))))))
187 (let ((info (dynamic-extent-info dynamic-extent
)))
190 ((eq info
(first stack
)))
191 ;; Preserve any intervening dynamic-extents.
193 (do ((cleanup (node-enclosing-cleanup node
)
194 (node-enclosing-cleanup
195 (cleanup-mess-up cleanup
))))
197 (when (eq (cleanup-kind cleanup
) :dynamic-extent
)
198 (let ((mess-up (cleanup-mess-up cleanup
)))
199 (when (eq dynamic-extent mess-up
)
201 (let ((preserve (dynamic-extent-info mess-up
)))
203 (dynamic-extent-preserve-info dynamic-extent
)))))))
205 (pushnew block
(ir2-component-stack-mess-ups 2comp
))
206 (setf (ctran-next (node-prev node
)) nil
)
207 (let ((ctran (make-ctran)))
208 (with-ir1-environment-from-node node
209 (ir1-convert (node-prev node
) ctran info
210 '(%dynamic-extent-start
)))
211 (link-node-to-previous-ctran node ctran
))
212 (push info stack
)))))))
214 (dolist (nlx-info (cleanup-nlx-info (entry-cleanup node
)))
215 (stack-mess-up-walk (nlx-info-target nlx-info
) stack
)))))
216 (dolist (succ (block-succ block
))
217 (stack-mess-up-walk succ stack
)))
221 ;;; This is called when we discover that the stack-top unknown-values
222 ;;; or stack lvar at the end of BLOCK1 is different from that at the
223 ;;; start of BLOCK2 (its successor).
225 ;;; We insert a call to a funny function in a new cleanup block
226 ;;; introduced between BLOCK1 and BLOCK2. Since control analysis and
227 ;;; LTN have already run, we must do make an IR2 block, then do
228 ;;; ADD-TO-EMIT-ORDER and LTN-ANALYZE-BELATED-BLOCK on the new
229 ;;; block. The new block is inserted after BLOCK1 in the emit order.
231 ;;; If the control transfer between BLOCK1 and BLOCK2 represents a
232 ;;; tail-recursive return or a non-local exit, then the cleanup code
233 ;;; will never actually be executed. It doesn't seem to be worth the
234 ;;; risk of trying to optimize this, since this rarely happens and
235 ;;; wastes only space.
236 (defun discard-unused-values (block1 block2
)
237 (declare (type cblock block1 block2
))
238 (let* ((end-stack (ir2-block-end-stack (block-info block1
)))
239 (start-stack (ir2-block-start-stack (block-info block2
))))
240 (collect ((cleanup-code))
241 (labels ((find-popped (before after
)
242 ;; Return (VALUES last-popped rest), where
243 ;; (EQ (FIRST rest) (FIRST after)) and
244 ;; (CDR (MEMBER last-popped BEFORE) = rest
245 (do ((first-preserved (car after
))
246 (rest before
(rest rest
))
247 (last-popped nil
(first rest
)))
248 ((or (eq (first rest
) first-preserved
)
252 (values last-popped rest
))))
253 (nip-values (before after qmoved
)
254 (unless (equal before after
)
255 (aver (eq (car before
) (car after
)))
256 (do ((before before
(rest before
))
257 (after after
(rest after
))
258 (last-moved nil
(first before
)))
259 ((neq (first before
) (first after
))
260 (multiple-value-bind (last-nipped rest
)
261 (find-popped before after
)
263 `(%nip-values
',last-nipped
',last-moved
,@qmoved
))
264 (nip-values rest after qmoved
)))
265 (aver (first before
))
266 (push `',(first before
) qmoved
)))))
267 (multiple-value-bind (last-popped rest
)
268 (find-popped end-stack start-stack
)
270 (cleanup-code `(%pop-values
',last-popped
)))
272 (nip-values rest start-stack
'()))))
274 (let* ((block (insert-cleanup-code (list block1
) block2
275 (block-start-node block2
)
276 `(progn ,@(cleanup-code))))
277 (2block (make-ir2-block block
)))
278 (setf (block-info block
) 2block
)
279 (add-to-emit-order 2block
(block-info block1
))
280 (ltn-analyze-belated-block block
)
281 ;; Set the start and end stacks to make traces less
282 ;; confusing. Purely cosmetic.
283 (setf (ir2-block-start-stack 2block
) end-stack
)
284 (setf (ir2-block-end-stack 2block
) start-stack
)))))
290 ;;; Return a list of all the blocks containing genuine uses of one of
291 ;;; the RECEIVERS. Exits are excluded, since they don't drop through
293 (defun find-values-generators (receivers)
294 (declare (list receivers
))
295 (collect ((res nil adjoin
))
296 (dolist (rec receivers
)
297 (dolist (pop (ir2-block-popped (block-info rec
)))
300 (res (node-block use
))))))
303 ;;; Analyze the use of unknown-values and dynamic extents in
304 ;;; COMPONENT, inserting cleanup code to discard values that are
305 ;;; generated but never received and to set appropriate bounds for
306 ;;; stack allocated objects. This phase doesn't need to be run when
307 ;;; Values-Receivers is null and Stack-Allocates-P is false,
308 ;;; i.e. there are no unknown-values lvars used across block
309 ;;; boundaries and no stack allocated objects.
310 (defun stack-analyze (component)
311 (declare (type component component
))
312 (let* ((2comp (component-info component
))
313 (receivers (ir2-component-values-receivers 2comp
))
314 (generators (find-values-generators receivers
)))
316 (when (ir2-component-stack-allocates-p 2comp
)
317 (clear-flags component
)
318 (dolist (ep (block-succ (component-head component
)))
319 (let ((start (block-start-node ep
)))
320 (when (and (bind-p start
)
321 (some #'dynamic-extent-info
322 (lambda-dynamic-extents (bind-lambda start
))))
323 (stack-mess-up-walk ep
())))))
325 (dolist (block generators
)
326 (find-pushed-lvars block
))
328 (dolist (block (ir2-component-stack-mess-ups 2comp
))
329 (unless (ir2-block-pushed (block-info block
))
330 (find-pushed-lvars block
))))
332 ;; Compute sets of lvars.
333 (loop for did-something
= nil
334 do
(do-blocks-backwards (block component
)
335 (when (update-lvar-live-sets block
)
336 (setq did-something t
)))
339 (clear-flags component
)
340 (dolist (ep (block-succ (component-head component
)))
341 (when (bind-p (block-start-node ep
))
342 (order-lvar-sets-walk ep
())))
344 (do-blocks (block component
)
345 (let ((top (ir2-block-end-stack (block-info block
))))
346 (dolist (succ (block-succ block
))
347 (when (and (block-start succ
)
348 (not (eq (ir2-block-start-stack (block-info succ
))
350 ;; Return resets the stack, so no need to clean anything.
351 (let ((start (block-last succ
)))
352 (unless (and (return-p start
)
353 (eq (block-start succ
) (node-prev start
)))
354 (discard-unused-values block succ
)))))))