Skip to content

Commit c288421

Browse files
committed
Remove dups from multiway branch before deciding to use it
1 parent 636485a commit c288421

File tree

2 files changed

+60
-43
lines changed

2 files changed

+60
-43
lines changed

src/compiler/ir2opt.lisp

Lines changed: 45 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -556,54 +556,56 @@
556556
;;; e.g. (case x (1 :a) (2 :b) (3 :c) (zot 'y)) ; with any order of tests
557557
;;; could be expressed as (if (eq x 'zot) y [multiway-branch])
558558
(defun should-use-jump-table-p (chain &aux (choices (cadr chain)))
559-
;; Don't convert to a multiway branch if there are 3 or fewer comparisons.
560-
(unless (>= (length choices) 4)
561-
(return-from should-use-jump-table-p nil))
562-
(let ((values (mapcar #'car choices)))
563-
(cond ((every #'fixnump values)) ; ok
564-
((every #'characterp values)
565-
(setq values (mapcar #'char-code values)))
566-
(t
567-
(return-from should-use-jump-table-p nil)))
568-
(let* ((min (reduce #'min values))
569-
(max (reduce #'max values))
570-
(table-size (1+ (- max min )))
571-
(size-limit (* (length values) 2)))
572-
;; Don't waste too much space, e.g. {5,6,10,20} would require 16 words
573-
;; for 4 entries, which is excessive.
574-
(<= table-size size-limit))))
559+
;; Dup keys could exist. REMOVE-DUPLICATES from-end can handle that:
560+
;; "the one occurring earlier in sequence is discarded, unless from-end
561+
;; is true, in which case the one later in sequence is discarded."
562+
(let ((choices (remove-duplicates choices :key #'car :from-end t)))
563+
;; Convert to multiway only if at least 4 key comparisons would be needed.
564+
(unless (>= (length choices) 4)
565+
(return-from should-use-jump-table-p nil))
566+
(let ((values (mapcar #'car choices)))
567+
(cond ((every #'fixnump values)) ; ok
568+
((every #'characterp values)
569+
(setq values (mapcar #'char-code values)))
570+
(t
571+
(return-from should-use-jump-table-p nil)))
572+
(let* ((min (reduce #'min values))
573+
(max (reduce #'max values))
574+
(table-size (1+ (- max min )))
575+
(size-limit (* (length values) 2)))
576+
;; Don't waste too much space, e.g. {5,6,10,20} would require 16 words
577+
;; for 4 entries, which is excessive.
578+
(when (<= table-size size-limit)
579+
;; Recons the data to play things safe: (vop-name choices else)
580+
(list (first chain) choices (third chain)))))))
575581

576582
(defun convert-if-else-chains (component)
577583
(do-ir2-blocks (2block component)
578584
(let ((comparison (ends-in-branch-if-eq-imm-p 2block)))
579585
(when (and comparison (exactly-two-successors-p 2block))
580-
(multiple-value-bind (chain blocks-to-delete) (longest-if-else-chain comparison)
581-
(when (should-use-jump-table-p chain)
582-
(let ((node (vop-node comparison))
583-
(src (tn-ref-tn (vop-args comparison))))
584-
(delete-vop (vop-next comparison)) ; delete the BRANCH-IF
585-
(delete-vop comparison) ; delete the initial IF-EQ
586-
;; Delete vops that are bypassed
587-
(dolist (block blocks-to-delete)
588-
(delete-vop (ir2-block-last-vop block))
589-
(delete-vop (ir2-block-last-vop block))
590-
;; there had better be no vops remaining
591-
(aver (null (ir2-block-start-vop block))))
592-
;; Unzip the alist
593-
(destructuring-bind (test-vop-name clauses else-block) chain
594-
;; Dup keys could exist. REMOVE-DUPLICATES from-end can handle that:
595-
;; "the one occurring earlier in sequence is discarded, unless from-end
596-
;; is true, in which case the one later in sequence is discarded."
597-
(let* ((clauses (remove-duplicates clauses :key #'car :from-end t))
598-
(values (mapcar #'car clauses))
599-
(blocks (mapcar #'cdr clauses))
600-
(labels (mapcar #'ir2-block-%label blocks))
601-
(otherwise (ir2-block-%label else-block)))
602-
(emit-and-insert-vop node 2block
603-
(template-or-lose 'sb-vm::multiway-branch-if-eq)
604-
(reference-tn src nil) nil nil
605-
(list values labels otherwise test-vop-name))
606-
(update-block-succ 2block (cons else-block blocks)))))))))))
586+
(binding* (((chain delete-blocks) (longest-if-else-chain comparison))
587+
(culled-chain (should-use-jump-table-p chain) :exit-if-null)
588+
(node (vop-node comparison))
589+
(src (tn-ref-tn (vop-args comparison))))
590+
(delete-vop (vop-next comparison)) ; delete the BRANCH-IF
591+
(delete-vop comparison) ; delete the initial IF-EQ
592+
;; Delete vops that are bypassed
593+
(dolist (block delete-blocks)
594+
(delete-vop (ir2-block-last-vop block))
595+
(delete-vop (ir2-block-last-vop block))
596+
;; there had better be no vops remaining
597+
(aver (null (ir2-block-start-vop block))))
598+
;; Unzip the alist
599+
(destructuring-bind (test-vop-name clauses else-block) culled-chain
600+
(let* ((values (mapcar #'car clauses))
601+
(blocks (mapcar #'cdr clauses))
602+
(labels (mapcar #'ir2-block-%label blocks))
603+
(otherwise (ir2-block-%label else-block)))
604+
(emit-and-insert-vop node 2block
605+
(template-or-lose 'sb-vm::multiway-branch-if-eq)
606+
(reference-tn src nil) nil nil
607+
(list values labels otherwise test-vop-name))
608+
(update-block-succ 2block (cons else-block blocks)))))))))
607609

608610
(defun ir2-optimize (component)
609611
(let ((*2block-info* (make-hash-table :test #'eq)))

tests/x86-64-codegen.impure.lisp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,3 +372,18 @@
372372

373373
(with-test (:name :multiway-branch-char-eq)
374374
(expect-n-comparisons 'try-case-known-char 2)) ; widetag test and upper bound
375+
376+
(with-test (:name :multiway-branch-min-branch-factor)
377+
;; Test that multiway vop shows up in IR2
378+
(let ((s (with-output-to-string (sb-c::*compiler-trace-output*)
379+
(checked-compile '(lambda (b)
380+
(case b
381+
((0) :a) ((0) :b) ((0) :c) ((1) :d)
382+
((2) :e) ((3) :f)))
383+
:allow-style-warnings t))))
384+
(assert (search "MULTIWAY-BRANCH" s)))
385+
;; There are too few cases after duplicate removal to be considered multiway
386+
(let ((s (with-output-to-string (sb-c::*compiler-trace-output*)
387+
(checked-compile '(lambda (b) (case b ((0) :a) ((0) :b) ((0) :c) ((1) :d)))
388+
:allow-style-warnings t))))
389+
(assert (not (search "MULTIWAY-BRANCH" s)))))

0 commit comments

Comments
 (0)