|
556 | 556 | ;;; e.g. (case x (1 :a) (2 :b) (3 :c) (zot 'y)) ; with any order of tests |
557 | 557 | ;;; could be expressed as (if (eq x 'zot) y [multiway-branch]) |
558 | 558 | (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))))))) |
575 | 581 |
|
576 | 582 | (defun convert-if-else-chains (component) |
577 | 583 | (do-ir2-blocks (2block component) |
578 | 584 | (let ((comparison (ends-in-branch-if-eq-imm-p 2block))) |
579 | 585 | (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))))))))) |
607 | 609 |
|
608 | 610 | (defun ir2-optimize (component) |
609 | 611 | (let ((*2block-info* (make-hash-table :test #'eq))) |
|
0 commit comments