1 ;;;; This file contains the implementation-independent code for the
2 ;;;; representation selection phase in the compiler. Representation
3 ;;;; selection decides whether to use non-descriptor representations
4 ;;;; for objects and emits the appropriate representation-specific move
7 ;;;; This software is part of the SBCL system. See the README file for
10 ;;;; This software is derived from the CMU CL system, which was
11 ;;;; written at Carnegie Mellon University and released into the
12 ;;;; public domain. The software is in the public domain and is
13 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
14 ;;;; files for more information.
20 ;;;; Problems in the VM definition often show up here, so we try to be
21 ;;;; as implementor-friendly as possible.
23 ;;; Given a TN ref for a VOP argument or result, return these values:
24 ;;; 1. True if the operand is an argument, false otherwise.
25 ;;; 2. The ordinal position of the operand.
26 ;;; 3. True if the operand is a more operand, false otherwise.
27 ;;; 4. The costs for this operand.
28 ;;; 5. The load-scs vector for this operand (NIL if more-p.)
29 ;;; 6. True if the costs or SCs in the VOP-INFO are inconsistent with the
30 ;;; currently record ones.
31 (defun get-operand-info (ref)
32 (declare (type tn-ref ref))
33 (let* ((arg-p (not (tn-ref-write-p ref)))
34 (vop (tn-ref-vop ref))
35 (info (vop-info vop)))
36 (flet ((frob (refs costs load more-cost)
37 (do ((refs refs (tn-ref-across refs))
38 (costs costs (cdr costs))
39 (load load (cdr load))
45 (or (position-in #'tn-ref-across ref refs)
46 (error "couldn't find REF?"))
53 (let ((parse (vop-parse-or-lose (vop-info-name info))))
54 (multiple-value-bind (ccosts cscs)
55 (compute-loading-costs
57 (vop-parse-args parse)
58 (vop-parse-results parse))
68 (not (and (equalp ccosts (car costs))
69 (equalp cscs (car load))))))))))))
71 (frob (vop-args vop) (vop-info-arg-costs info)
72 (vop-info-arg-load-scs info)
73 (vop-info-more-arg-costs info))
74 (frob (vop-results vop) (vop-info-result-costs info)
75 (vop-info-result-load-scs info)
76 (vop-info-more-result-costs info))))))
78 ;;; Convert a load-costs vector to the list of SCs allowed by the operand
80 (defun listify-restrictions (restr)
81 (declare (type sc-vector restr))
83 (dotimes (i sc-number-limit)
84 (when (eq (svref restr i) t)
85 (res (svref *backend-sc-numbers* i))))
88 ;;; Try to give a helpful error message when Ref has no cost specified for
89 ;;; some SC allowed by the TN's primitive-type.
90 (defun bad-costs-error (ref)
91 (declare (type tn-ref ref))
92 (let* ((tn (tn-ref-tn ref))
93 (ptype (tn-primitive-type tn)))
94 (multiple-value-bind (arg-p pos more-p costs load-scs incon)
95 (get-operand-info ref)
97 (dolist (scn (primitive-type-scs ptype))
98 (unless (svref costs scn)
99 (losers (svref *backend-sc-numbers* scn))))
102 (error "Representation selection flamed out for no obvious reason.~@
103 Try again after recompiling the VM definition."))
105 (error "~S is not valid as the ~:R ~:[result~;argument~] to the~@
106 ~S VOP, since the TN's primitive type ~S allows SCs:~% ~S~@
107 ~:[which cannot be coerced or loaded into the allowed SCs:~
109 Current cost info inconsistent with that in effect at compile ~
110 time. Recompile.~%Compilation order may be incorrect.~]"
112 (template-name (vop-info (tn-ref-vop ref)))
113 (primitive-type-name ptype)
114 (mapcar #'sc-name (losers))
117 (mapcar #'sc-name (listify-restrictions load-scs)))
120 ;;; Try to give a helpful error message when we fail to do a coercion
122 (defun bad-coerce-error (op)
123 (declare (type tn-ref op))
124 (let* ((op-tn (tn-ref-tn op))
125 (op-sc (tn-sc op-tn))
126 (op-scn (sc-number op-sc))
127 (ptype (tn-primitive-type op-tn))
128 (write-p (tn-ref-write-p op)))
129 (multiple-value-bind (arg-p pos more-p costs load-scs incon)
130 (get-operand-info op)
131 (declare (ignore costs more-p))
132 (collect ((load-lose)
135 (dotimes (i sc-number-limit)
136 (let ((i-sc (svref *backend-sc-numbers* i)))
137 (when (eq (svref load-scs i) t)
138 (cond ((not (sc-allowed-by-primitive-type i-sc ptype))
140 ((not (find-move-vop op-tn write-p i-sc ptype
142 (let ((vops (if write-p
143 (svref (sc-move-vops op-sc) i)
144 (svref (sc-move-vops i-sc) op-scn))))
146 (dolist (vop vops) (move-lose (template-name vop)))
147 (no-move-scs i-sc))))
149 (error "Representation selection flamed out for no ~
150 obvious reason."))))))
152 (unless (or (load-lose) (no-move-scs) (move-lose))
153 (error "Representation selection flamed out for no obvious reason.~@
154 Try again after recompiling the VM definition."))
156 (error "~S is not valid as the ~:R ~:[result~;argument~] to VOP:~
157 ~% ~S~%Primitive type: ~S~@
158 SC restrictions:~% ~S~@
159 ~@[The primitive type disallows these loadable SCs:~% ~S~%~]~
160 ~@[No move VOPs are defined to coerce to these allowed SCs:~
162 ~@[These move VOPs couldn't be used due to operand type ~
163 restrictions:~% ~S~%~]~
165 Current cost info inconsistent with that in effect at compile ~
166 time. Recompile.~%Compilation order may be incorrect.~]"
168 (template-name (vop-info (tn-ref-vop op)))
169 (primitive-type-name ptype)
170 (mapcar #'sc-name (listify-restrictions load-scs))
171 (mapcar #'sc-name (load-lose))
172 (mapcar #'sc-name (no-move-scs))
176 (defun bad-move-arg-error (val pass)
177 (declare (type tn val pass))
178 (error "no :MOVE-ARGUMENT VOP defined to move ~S (SC ~S) to ~
180 val (sc-name (tn-sc val))
181 pass (sc-name (tn-sc pass))))
183 ;;;; VM consistency checking
185 ;;;; We do some checking of the consistency of the VM definition at load
188 ;;; FIXME: should probably be conditional on #!+SB-SHOW
189 (defun check-move-function-consistency ()
190 (dotimes (i sc-number-limit)
191 (let ((sc (svref *backend-sc-numbers* i)))
193 (let ((moves (sc-move-functions sc)))
194 (dolist (const (sc-constant-scs sc))
195 (unless (svref moves (sc-number const))
196 (warn "no move function defined to load SC ~S from constant ~
198 (sc-name sc) (sc-name const))))
200 (dolist (alt (sc-alternate-scs sc))
201 (unless (svref moves (sc-number alt))
202 (warn "no move function defined to load SC ~S from alternate ~
204 (sc-name sc) (sc-name alt)))
205 (unless (svref (sc-move-functions alt) i)
206 (warn "no move function defined to save SC ~S to alternate ~
208 (sc-name sc) (sc-name alt)))))))))
210 ;;;; representation selection
212 ;;; VOPs that we ignore in initial cost computation. We ignore SET in the
213 ;;; hopes that nobody is setting specials inside of loops. We ignore
214 ;;; TYPE-CHECK-ERROR because we don't want the possibility of error to bias the
215 ;;; result. Notes are suppressed for T-C-E as well, since we don't need to
216 ;;; worry about the efficiency of that case.
217 (defconstant ignore-cost-vops '(set type-check-error))
218 (defconstant suppress-note-vops '(type-check-error))
220 ;;; We special-case the move VOP, since using this costs for the normal MOVE
221 ;;; would spuriously encourage descriptor representations. We won't actually
222 ;;; need to coerce to descriptor and back, since we will replace the MOVE with
223 ;;; a specialized move VOP. What we do is look at the other operand. If its
224 ;;; representation has already been chosen (e.g. if it is wired), then we use
225 ;;; the appropriate move costs, otherwise we just ignore the references.
226 (defun add-representation-costs (refs scs costs
227 ops-slot costs-slot more-costs-slot
229 (do ((ref refs (tn-ref-next ref)))
231 (flet ((add-costs (cost)
233 (let ((res (svref cost scn)))
235 (bad-costs-error ref))
236 (incf (svref costs scn) res)))))
237 (let* ((vop (tn-ref-vop ref))
238 (info (vop-info vop)))
239 (case (vop-info-name info)
246 (vop-results vop))))))
250 (let ((res (svref (sc-move-costs
251 (svref *backend-sc-numbers* scn))
254 (incf (svref costs scn) res))))
256 (let ((res (svref (sc-move-costs rep) scn)))
258 (incf (svref costs scn) res))))))))
260 (do ((cost (funcall costs-slot info) (cdr cost))
261 (op (funcall ops-slot vop) (tn-ref-across op)))
263 (add-costs (funcall more-costs-slot info)))
265 (add-costs (car cost))
269 ;;; Return the best representation for a normal TN. SCs is a list
270 ;;; of the SC numbers of the SCs to select from. Costs is a scratch
273 ;;; What we do is sum the costs for each reference to TN in each of
274 ;;; the SCs, and then return the SC having the lowest cost. A second
275 ;;; value is returned which is true when the selection is unique which
276 ;;; is often not the case for the MOVE VOP.
277 (defun select-tn-representation (tn scs costs)
278 (declare (type tn tn) (type sc-vector costs)
279 (inline add-representation-costs))
281 (setf (svref costs scn) 0))
283 (add-representation-costs (tn-reads tn) scs costs
284 #'vop-args #'vop-info-arg-costs
285 #'vop-info-more-arg-costs
287 (add-representation-costs (tn-writes tn) scs costs
288 #'vop-results #'vop-info-result-costs
289 #'vop-info-more-result-costs
292 (let ((min most-positive-fixnum)
296 (let ((cost (svref costs scn)))
303 (values (svref *backend-sc-numbers* min-scn) unique)))
305 ;;; Prepare for the possibility of a TN being allocated on the number stack by
306 ;;; setting NUMBER-STACK-P in all functions that TN is referenced in and in all
307 ;;; the functions in their tail sets. Refs is a TN-Refs list of references to
309 (defun note-number-stack-tn (refs)
310 (declare (type (or tn-ref null) refs))
312 (do ((ref refs (tn-ref-next ref)))
314 (let* ((lambda (block-home-lambda
316 (vop-block (tn-ref-vop ref)))))
317 (tails (lambda-tail-set lambda)))
319 (setf (ir2-environment-number-stack-p
321 (lambda-environment fun)))
325 (dolist (fun (tail-set-functions tails))
330 ;;; If TN is a variable, return the name. If TN is used by a VOP emitted
331 ;;; for a return, then return a string indicating this. Otherwise, return NIL.
332 (defun get-operand-name (tn arg-p)
333 (declare (type tn tn))
334 (let* ((actual (if (eq (tn-kind tn) :alias) (tn-save-tn tn) tn))
335 (reads (tn-reads tn))
336 (leaf (tn-leaf actual)))
337 (cond ((lambda-var-p leaf) (leaf-name leaf))
338 ((and (not arg-p) reads
339 (return-p (vop-node (tn-ref-vop reads))))
344 ;;; If policy indicates, give an efficiency note for doing the coercion
345 ;;; Vop, where Op is the operand we are coercing for and Dest-TN is the
346 ;;; distinct destination in a move.
347 (defun do-coerce-efficiency-note (vop op dest-tn)
348 (declare (type vop-info vop) (type tn-ref op) (type (or tn null) dest-tn))
349 (let* ((note (or (template-note vop) (template-name vop)))
350 (cost (template-cost vop))
351 (op-vop (tn-ref-vop op))
352 (op-node (vop-node op-vop))
353 (op-tn (tn-ref-tn op))
354 (*compiler-error-context* op-node))
355 (cond ((eq (tn-kind op-tn) :constant))
356 ((policy op-node (<= speed brevity) (<= space brevity)))
357 ((member (template-name (vop-info op-vop)) suppress-note-vops))
359 (let* ((op-info (vop-info op-vop))
360 (op-note (or (template-note op-info)
361 (template-name op-info)))
362 (arg-p (not (tn-ref-write-p op)))
363 (name (get-operand-name op-tn arg-p))
364 (pos (1+ (or (position-in #'tn-ref-across op
367 (vop-results op-vop)))
368 (error "couldn't find op? bug!")))))
370 "doing ~A (cost ~D)~:[~2*~; ~:[to~;from~] ~S~], for:~%~6T~
371 the ~:R ~:[result~;argument~] of ~A"
372 note cost name arg-p name
375 (compiler-note "doing ~A (cost ~D)~@[ from ~S~]~@[ to ~S~]"
376 note cost (get-operand-name op-tn t)
377 (get-operand-name dest-tn nil)))))
380 ;;; Find a move VOP to move from the operand OP-TN to some other
381 ;;; representation corresponding to OTHER-SC and OTHER-PTYPE. Slot is the SC
382 ;;; slot that we grab from (move or move-argument). Write-P indicates that OP
383 ;;; is a VOP result, so OP is the move result and other is the arg, otherwise
384 ;;; OP is the arg and other is the result.
386 ;;; If an operand is of primitive type T, then we use the type of the other
387 ;;; operand instead, effectively intersecting the argument and result type
388 ;;; assertions. This way, a move VOP can restrict whichever operand makes more
389 ;;; sense, without worrying about which operand has the type info.
390 (defun find-move-vop (op-tn write-p other-sc other-ptype slot)
391 (declare (type tn op-tn) (type sc other-sc)
392 (type primitive-type other-ptype)
393 (type function slot))
394 (let* ((op-sc (tn-sc op-tn))
395 (op-scn (sc-number op-sc))
396 (other-scn (sc-number other-sc))
397 (any-ptype *backend-t-primitive-type*)
398 (op-ptype (tn-primitive-type op-tn)))
399 (let ((other-ptype (if (eq other-ptype any-ptype) op-ptype other-ptype))
400 (op-ptype (if (eq op-ptype any-ptype) other-ptype op-ptype)))
401 (dolist (info (if write-p
402 (svref (funcall slot op-sc) other-scn)
403 (svref (funcall slot other-sc) op-scn))
405 (when (and (operand-restriction-ok
406 (first (template-arg-types info))
407 (if write-p other-ptype op-ptype)
409 (operand-restriction-ok
410 (first (template-result-types info))
411 (if write-p op-ptype other-ptype)
415 ;;; Emit a coercion VOP for Op Before the specifed VOP or die trying. SCS
416 ;;; is the operand's LOAD-SCS vector, which we use to determine what SCs the
417 ;;; VOP will accept. We pick any acceptable coerce VOP, since it practice it
418 ;;; seems uninteresting to have more than one applicable.
420 ;;; On the X86 port, stack SCs may be placed in the list of operand
421 ;;; preferred SCs, and to prevent these stack SCs being selected when
422 ;;; a register SC is available the non-stack SCs are searched first.
424 ;;; What we do is look at each SC allowed by both the operand restriction
425 ;;; and the operand primitive-type, and see whether there is a move VOP
426 ;;; which moves between the operand's SC and load SC. If we find such a
427 ;;; VOP, then we make a TN having the load SC as the representation.
429 ;;; Dest-TN is the TN that we are moving to, for a move or move-arg. This
430 ;;; is only for efficiency notes.
432 ;;; If the TN is an unused result TN, then we don't actually emit the move;
433 ;;; we just change to the right kind of TN.
434 (defun emit-coerce-vop (op dest-tn scs before)
435 (declare (type tn-ref op) (type sc-vector scs) (type (or vop null) before)
436 (type (or tn null) dest-tn))
437 (let* ((op-tn (tn-ref-tn op))
438 (ptype (tn-primitive-type op-tn))
439 (write-p (tn-ref-write-p op))
440 (vop (tn-ref-vop op))
441 (node (vop-node vop))
442 (block (vop-block vop)))
443 (flet ((check-sc (scn sc)
444 (when (sc-allowed-by-primitive-type sc ptype)
445 (let ((res (find-move-vop op-tn write-p sc ptype
448 (when (>= (vop-info-cost res)
449 *efficiency-note-cost-threshold*)
450 (do-coerce-efficiency-note res op dest-tn))
451 (let ((temp (make-representation-tn ptype scn)))
452 (change-tn-ref-tn op temp)
455 (emit-move-template node block res op-tn temp before))
456 ((and (null (tn-reads op-tn))
457 (eq (tn-kind op-tn) :normal)))
459 (emit-move-template node block res temp op-tn
462 ;; Search the non-stack load SCs first.
463 (dotimes (scn sc-number-limit)
464 (let ((sc (svref *backend-sc-numbers* scn)))
465 (when (and (eq (svref scs scn) t)
466 (not (eq (sb-kind (sc-sb sc)) :unbounded))
468 (return-from emit-coerce-vop))))
469 ;; Search the stack SCs if the above failed.
470 (dotimes (scn sc-number-limit (bad-coerce-error op))
471 (let ((sc (svref *backend-sc-numbers* scn)))
472 (when (and (eq (svref scs scn) t)
473 (eq (sb-kind (sc-sb sc)) :unbounded)
477 ;;; Scan some operands and call EMIT-COERCE-VOP on any for which we can't
478 ;;; load the operand. The coerce VOP is inserted Before the specified VOP.
479 ;;; Dest-TN is the destination TN if we are doing a move or move-arg, and is
480 ;;; NIL otherwise. This is only used for efficiency notes.
481 #!-sb-fluid (declaim (inline coerce-some-operands))
482 (defun coerce-some-operands (ops dest-tn load-scs before)
483 (declare (type (or tn-ref null) ops) (list load-scs)
484 (type (or tn null) dest-tn) (type (or vop null) before))
485 (do ((op ops (tn-ref-across op))
486 (scs load-scs (cdr scs)))
488 (unless (svref (car scs)
489 (sc-number (tn-sc (tn-ref-tn op))))
490 (emit-coerce-vop op dest-tn (car scs) before)))
493 ;;; Emit coerce VOPs for the args and results, as needed.
494 (defun coerce-vop-operands (vop)
495 (declare (type vop vop))
496 (let ((info (vop-info vop)))
497 (coerce-some-operands (vop-args vop) nil (vop-info-arg-load-scs info) vop)
498 (coerce-some-operands (vop-results vop) nil (vop-info-result-load-scs info)
502 ;;; Iterate over the more operands to a call VOP, emitting move-arg VOPs and
503 ;;; any necessary coercions. We determine which FP to use by looking at the
504 ;;; MOVE-ARGS annotation. If the vop is a :LOCAL-CALL, we insert any needed
505 ;;; coercions before the ALLOCATE-FRAME so that lifetime analysis doesn't get
506 ;;; confused (since otherwise, only passing locations are written between A-F
508 (defun emit-arg-moves (vop)
509 (let* ((info (vop-info vop))
510 (node (vop-node vop))
511 (block (vop-block vop))
512 (how (vop-info-move-args info))
513 (args (vop-args vop))
514 (fp-tn (tn-ref-tn args))
515 (nfp-tn (if (eq how :local-call)
516 (tn-ref-tn (tn-ref-across args))
518 (pass-locs (first (vop-codegen-info vop)))
519 (prev (vop-prev vop)))
520 (do ((val (do ((arg args (tn-ref-across arg))
521 (req (template-arg-types info) (cdr req)))
524 (pass pass-locs (cdr pass)))
526 (assert (null pass)))
527 (let* ((val-tn (tn-ref-tn val))
528 (pass-tn (first pass))
529 (pass-sc (tn-sc pass-tn))
530 (res (find-move-vop val-tn nil pass-sc
531 (tn-primitive-type pass-tn)
532 #'sc-move-arg-vops)))
534 (bad-move-arg-error val-tn pass-tn))
536 (change-tn-ref-tn val pass-tn)
538 (cond ((not (sc-number-stack-p pass-sc)) fp-tn)
541 (assert (eq how :known-return))
542 (setq nfp-tn (make-number-stack-pointer-tn))
544 (svref *backend-sc-numbers*
545 (first (primitive-type-scs
546 (tn-primitive-type nfp-tn)))))
547 (emit-context-template
549 (template-or-lose 'compute-old-nfp)
551 (assert (not (sc-number-stack-p (tn-sc nfp-tn))))
553 (new (emit-move-arg-template node block res val-tn this-fp
556 (cond ((eq how :local-call)
557 (assert (eq (vop-info-name (vop-info prev))
560 (prev (vop-next prev))
562 (ir2-block-start-vop block)))))
563 (coerce-some-operands (vop-args new) pass-tn
564 (vop-info-arg-load-scs res)
568 ;;; Scan the IR2 looking for move operations that need to be replaced with
569 ;;; special-case VOPs and emitting coercion VOPs for operands of normal VOPs.
570 ;;; We delete moves to TNs that are never read at this point, rather than
571 ;;; possibly converting them to some expensive move operation.
572 (defun emit-moves-and-coercions (block)
573 (declare (type ir2-block block))
574 (do ((vop (ir2-block-start-vop block)
577 (let ((info (vop-info vop))
578 (node (vop-node vop))
579 (block (vop-block vop)))
581 ((eq (vop-info-name info) 'move)
582 (let* ((args (vop-args vop))
584 (y (tn-ref-tn (vop-results vop)))
585 (res (find-move-vop x nil (tn-sc y) (tn-primitive-type y)
587 (cond ((and (null (tn-reads y))
588 (eq (tn-kind y) :normal))
592 (when (>= (vop-info-cost res)
593 *efficiency-note-cost-threshold*)
594 (do-coerce-efficiency-note res args y))
595 (emit-move-template node block res x y vop)
598 (coerce-vop-operands vop)))))
599 ((vop-info-move-args info)
600 (emit-arg-moves vop))
602 (coerce-vop-operands vop))))))
604 ;;; If TN is in a number stack SC, make all the right annotations. Note
605 ;;; that this should be called after TN has been referenced, since it must
606 ;;; iterate over the referencing environments.
607 #!-sb-fluid (declaim (inline note-if-number-stack))
608 (defun note-if-number-stack (tn 2comp restricted)
609 (declare (type tn tn) (type ir2-component 2comp))
611 (eq (sb-name (sc-sb (tn-sc tn))) 'non-descriptor-stack)
612 (sc-number-stack-p (tn-sc tn)))
613 (unless (ir2-component-nfp 2comp)
614 (setf (ir2-component-nfp 2comp) (make-nfp-tn)))
615 (note-number-stack-tn (tn-reads tn))
616 (note-number-stack-tn (tn-writes tn)))
619 ;;; Entry to representation selection. First we select the representation for
620 ;;; all normal TNs, setting the TN-SC. After selecting the TN representations,
621 ;;; we set the SC for all :ALIAS TNs to be the representation chosen for the
622 ;;; original TN. We then scan all the IR2, emitting any necessary coerce and
623 ;;; move-arg VOPs. Finally, we scan all TNs looking for ones that might be
624 ;;; placed on the number stack, noting this so that the number-FP can be
625 ;;; allocated. This must be done last, since references in new environments may
626 ;;; be introduced by MOVE-ARG insertion.
627 (defun select-representations (component)
628 (let ((costs (make-array sc-number-limit))
629 (2comp (component-info component)))
631 ;; First pass; only allocate SCs where there is a distinct choice.
632 (do ((tn (ir2-component-normal-tns 2comp)
635 (assert (tn-primitive-type tn))
637 (let* ((scs (primitive-type-scs (tn-primitive-type tn))))
639 (multiple-value-bind (sc unique)
640 (select-tn-representation tn scs costs)
642 (setf (tn-sc tn) sc))))
645 (svref *backend-sc-numbers* (first scs))))))))
647 (do ((tn (ir2-component-normal-tns 2comp)
650 (assert (tn-primitive-type tn))
652 (let* ((scs (primitive-type-scs (tn-primitive-type tn)))
654 (select-tn-representation tn scs costs)
655 (svref *backend-sc-numbers* (first scs)))))
657 (setf (tn-sc tn) sc))))
659 (do ((alias (ir2-component-alias-tns 2comp)
662 (setf (tn-sc alias) (tn-sc (tn-save-tn alias))))
664 (do-ir2-blocks (block component)
665 (emit-moves-and-coercions block))
667 (macrolet ((frob (slot restricted)
668 `(do ((tn (,slot 2comp) (tn-next tn)))
670 (note-if-number-stack tn 2comp ,restricted))))
671 (frob ir2-component-normal-tns nil)
672 (frob ir2-component-wired-tns t)
673 (frob ir2-component-restricted-tns t)))