1 ;;;; This file implements some optimisations at the IR2 level.
2 ;;;; Currently, the pass converts branches to conditional moves,
3 ;;;; deletes subsequently dead blocks and then reoptimizes jumps.
5 ;;;; This software is part of the SBCL system. See the README file for
8 ;;;; This software is derived from the CMU CL system, which was
9 ;;;; written at Carnegie Mellon University and released into the
10 ;;;; public domain. The software is in the public domain and is
11 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
12 ;;;; files for more information.
16 ;;; We track pred/succ info at the IR2-block level, extrapolating
17 ;;; most of the data from IR1 to initialise.
18 (declaim (type hash-table *2block-pred* *2block-succ* *label-2block*))
19 (defvar *2block-pred*)
20 (defvar *2block-succ*)
21 (defvar *label-2block*)
23 (defun initialize-ir2-blocks-flow-info (component)
24 (labels ((block-last-2block (block)
25 (declare (type cblock block))
26 (do ((2block (block-info block)
27 (ir2-block-next 2block)))
29 (let ((next (ir2-block-next 2block)))
31 (neq block (ir2-block-block next)))
33 (link-2blocks (pred succ)
34 (declare (type ir2-block pred succ))
35 (pushnew pred (gethash succ *2block-pred*))
36 (pushnew succ (gethash pred *2block-succ*))))
37 (do-blocks (block component :both)
38 (let ((succ (block-succ block))
39 (last (block-last-2block block)))
41 (link-2blocks last (block-info succ)))
42 (do ((2block (block-info block)
43 (ir2-block-next 2block)))
45 (link-2blocks 2block (ir2-block-next 2block)))))
46 (do-ir2-blocks (2block component)
47 (awhen (ir2-block-%label 2block)
48 (setf (gethash it *label-2block*) 2block)))))
50 (defun update-block-succ (2block succ)
51 (declare (type ir2-block 2block)
55 (label (or (gethash x *label-2block*)
56 (error "Unknown label: ~S" x)))
58 (setf succ (mapcar #'blockify succ)))
59 (dolist (old (gethash 2block *2block-succ*))
60 (setf (gethash old *2block-pred*)
61 (remove 2block (gethash old *2block-pred*))))
62 (setf (gethash 2block *2block-succ*) succ)
64 (pushnew 2block (gethash new *2block-pred*))))
66 ;;;; Conditional move insertion support code
67 #!-sb-fluid (declaim (inline vop-name))
68 (defun vop-name (vop &optional default)
69 (declare (type vop vop))
70 (let ((vop-info (vop-info vop)))
72 (vop-info-name vop-info)
75 (defun move-value-target (2block)
76 (declare (type ir2-block 2block))
77 (let* ((first (or (ir2-block-start-vop 2block)
78 (return-from move-value-target)))
79 (second (vop-next first)))
80 (when (and (eq (vop-name first) 'move)
82 (eq (vop-name second) 'branch)))
83 (values (tn-ref-tn (vop-args first))
84 (tn-ref-tn (vop-results first))))))
86 ;; A conditional jump may be converted to a conditional move if
87 ;; both branches move a value to the same TN and then continue
88 ;; execution in the same successor block.
90 ;; The label argument is used to return possible value TNs in
91 ;; the right order (first TN if the branch would have been taken,
93 (defun cmovp (label a b)
94 (declare (type label label)
96 (cond ((eq label (ir2-block-%label (block-info a))))
97 ((eq label (ir2-block-%label (block-info b)))
99 (t (return-from cmovp)))
100 (let ((succ-a (block-succ a))
101 (succ-b (block-succ b)))
102 (unless (and (singleton-p succ-a)
104 (eq (car succ-a) (car succ-b)))
106 (multiple-value-bind (value-a target)
107 (move-value-target (block-info a))
108 (multiple-value-bind (value-b targetp)
109 (move-value-target (block-info b))
110 (and value-a value-b (eq target targetp)
111 (values (block-label (car succ-a))
112 target value-a value-b))))))
114 ;; To convert a branch to a conditional move:
115 ;; 1. Convert both possible values to the chosen common representation
116 ;; 2. Execute the conditional VOP
117 ;; 3. Execute the chosen conditional move VOP
118 ;; 4. Convert the result from the common representation
119 ;; 5. Jump to the successor
120 #!-sb-fluid (declaim (inline convert-one-cmov))
121 (defun convert-one-cmov (cmove-vop
129 (flet ((load-and-coerce (dst src)
130 (when (and dst (neq dst src))
131 (let ((end (ir2-block-last-vop 2block))
132 (move (template-or-lose 'move)))
133 (multiple-value-bind (first last)
134 (funcall (template-emit-function move) node 2block
135 move (reference-tn src nil)
136 (reference-tn dst t))
137 (insert-vop-sequence first last 2block end))))))
138 (load-and-coerce arg-if value-if)
139 (load-and-coerce arg-else value-else))
140 (emit-template node 2block (template-or-lose cmove-vop)
141 (reference-tn-list (remove nil (list arg-if arg-else))
145 (emit-move node 2block res target)
146 (vop branch node 2block label)
147 (update-block-succ 2block (list label)))
149 ;; Since conditional branches are always at the end of blocks,
150 ;; it suffices to look at the last VOP in each block.
151 (defun maybe-convert-one-cmov (2block)
152 (let* ((block (ir2-block-block 2block))
153 (succ (block-succ block))
156 (vop (or (ir2-block-last-vop 2block)
157 (return-from maybe-convert-one-cmov)))
158 (node (vop-node vop)))
159 (unless (eq (vop-name vop) 'branch-if)
160 (return-from maybe-convert-one-cmov))
161 (destructuring-bind (jump-target flags not-p) (vop-codegen-info vop)
162 (multiple-value-bind (label target value-a value-b)
163 (cmovp jump-target a b)
165 (return-from maybe-convert-one-cmov))
166 (multiple-value-bind (cmove-vop arg-a arg-b res info)
167 (convert-conditional-move-p node target value-a value-b)
169 (return-from maybe-convert-one-cmov))
171 (rotatef value-a value-b)
172 (rotatef arg-a arg-b))
173 (convert-one-cmov cmove-vop value-a arg-a
177 label vop node 2block))))))
179 (defun convert-cmovs (component)
180 (do-ir2-blocks (2block component (values))
181 (maybe-convert-one-cmov 2block)))
183 (defun delete-unused-ir2-blocks (component)
184 (declare (type component component))
185 (let ((live-2blocks (make-hash-table)))
186 (labels ((mark-2block (2block)
187 (declare (type ir2-block 2block))
188 (when (gethash 2block live-2blocks)
189 (return-from mark-2block))
190 (setf (gethash 2block live-2blocks) t)
191 (map nil #'mark-2block (gethash 2block *2block-succ*))))
192 (mark-2block (block-info (component-head component))))
194 (flet ((delete-2block (2block)
195 (declare (type ir2-block 2block))
196 (do ((vop (ir2-block-start-vop 2block)
200 (do-ir2-blocks (2block component (values))
201 (unless (gethash 2block live-2blocks)
202 (delete-2block 2block))))))
204 (defun delete-fall-through-jumps (component)
205 (flet ((jump-falls-through-p (2block)
206 (let* ((last (or (ir2-block-last-vop 2block)
207 (return-from jump-falls-through-p nil)))
208 (target (first (vop-codegen-info last))))
209 (unless (eq (vop-name last) 'branch)
210 (return-from jump-falls-through-p nil))
211 (do ((2block (ir2-block-next 2block)
212 (ir2-block-next 2block)))
214 (cond ((ir2-block-%trampoline-label 2block)
216 ((eq target (ir2-block-%label 2block))
218 ((ir2-block-start-vop 2block)
220 ;; Walk the blocks in reverse emission order to catch jumps
221 ;; that fall-through only once another jump is deleted
223 (do-ir2-blocks (2block component (aver nil))
224 (when (null (ir2-block-next 2block))
226 (do ((2block last-2block
227 (ir2-block-prev 2block)))
230 (when (jump-falls-through-p 2block)
231 (delete-vop (ir2-block-last-vop 2block)))))))
233 (defun ir2-optimize (component)
234 (let ((*2block-pred* (make-hash-table))
235 (*2block-succ* (make-hash-table))
236 (*label-2block* (make-hash-table)))
237 (initialize-ir2-blocks-flow-info component)
239 (convert-cmovs component)
240 (delete-unused-ir2-blocks component)
241 (delete-fall-through-jumps component))