]> git.eshelyaron.com Git - emacs.git/commitdiff
* Allow for adding constraints targetting blocks with multiple predecessors
authorAndrea Corallo <akrl@sdf.org>
Sat, 12 Dec 2020 21:20:28 +0000 (22:20 +0100)
committerAndrea Corallo <akrl@sdf.org>
Sat, 12 Dec 2020 23:58:25 +0000 (00:58 +0100)
This commit remove the limitaiton we had not being able to add
constraints derived from conditional branches to basic blocks with
multiple predecessors.  When this condition is verified we add a new
dedicated basic block to hold the constraints.

* lisp/emacs-lisp/comp.el (comp-block, comp-edge): Better slot
type specifiers.
(comp-block-cstr): New struct specializing `comp-block'.
(make-comp-edge): New function.
(comp-func): Better test function + doc for `blocks' slot.
(comp-limple-lock-keywords): Update possible basic block names.
(comp-emit-assume): Recive directly the block instead of its name.
(comp-add-new-block-beetween): New function.
(comp-cond-cstr-target-block): Logic update and use
`comp-add-new-block-beetween'.
(comp-cond-cstr-func): Make use of the latter.
(comp-compute-edges): Make use of `make-comp-edge'.

lisp/emacs-lisp/comp.el

index b9a511ab8633b020b6b291d821e85baf7168d85d..2cff362cb9e91b51a4f8616084a58b863b356568 100644 (file)
@@ -313,6 +313,9 @@ Useful to hook into pass checkers.")
                             return)
   "All limple operators.")
 
+(defvar comp-func nil
+  "Bound to the current function by most passes.")
+
 (define-error 'native-compiler-error-dyn-func
   "can't native compile a non-lexically-scoped function"
   'native-compiler-error)
@@ -400,13 +403,13 @@ To be used when ncall-conv is nil."))
             :documentation "List of incoming edges.")
   (out-edges () :type list
              :documentation "List of out-coming edges.")
-  (dom nil :type comp-block
+  (dom nil :type (or null comp-block)
         :documentation "Immediate dominator.")
-  (df (make-hash-table) :type hash-table
+  (df (make-hash-table) :type (or null hash-table)
       :documentation "Dominance frontier set. Block-name -> block")
-  (post-num nil :type number
+  (post-num nil :type (or null number)
             :documentation "Post order number.")
-  (final-frame nil :type vector
+  (final-frame nil :type (or null vector)
              :documentation "This is a copy of the frame when leaving the block.
 Is in use to help the SSA rename pass."))
 
@@ -426,14 +429,26 @@ into it.")
                           (:include comp-block))
   "A basic block for a latch loop.")
 
+(cl-defstruct (comp-block-cstr (:copier nil)
+                               (:include comp-block))
+  "A basic block holding only constraints.")
+
 (cl-defstruct (comp-edge (:copier nil) (:constructor make--comp-edge))
   "An edge connecting two basic blocks."
-  (src nil :type comp-block)
-  (dst nil :type comp-block)
+  (src nil :type (or null comp-block))
+  (dst nil :type (or null comp-block))
   (number nil :type number
           :documentation "The index number corresponding to this edge in the
  edge hash."))
 
+(defun make-comp-edge (&rest args)
+  "Create a `comp-edge' with basic blocks SRC and DST."
+  (let ((n (funcall (comp-func-edge-cnt-gen comp-func))))
+    (puthash
+     n
+     (apply #'make--comp-edge :number n args)
+     (comp-func-edges-h comp-func))))
+
 (defun comp-block-preds (basic-block)
   "Given BASIC-BLOCK return the list of its predecessors."
   (mapcar #'comp-edge-src (comp-block-in-edges basic-block)))
@@ -463,8 +478,8 @@ into it.")
 Once in SSA form this *must* be set to 'dirty' every time the topology of the
 CFG is mutated by a pass.")
   (frame-size nil :type number)
-  (blocks (make-hash-table) :type hash-table
-          :documentation "Basic block name -> basic block.")
+  (blocks (make-hash-table :test #'eq) :type hash-table
+          :documentation "Basic block symbol -> basic block.")
   (lap-block (make-hash-table :test #'equal) :type hash-table
              :documentation "LAP label -> LIMPLE basic block name.")
   (edges-h (make-hash-table) :type hash-table
@@ -570,9 +585,6 @@ In use by the backend."
     (cons (comp-mvar-cons-p mvar))
     (fixnum (comp-mvar-fixnum-p mvar))))
 
-;; Special vars used by some passes
-(defvar comp-func)
-
 \f
 
 (defun comp-ensure-native-compiler ()
@@ -650,7 +662,7 @@ Assume allocation class 'd-default as default."
      (1 font-lock-variable-name-face))
     (,(rx (group-n 1 (or "entry"
                          (seq (or "entry_" "entry_fallback_" "bb_")
-                              (1+ num) (? "_latch")))))
+                              (1+ num) (? (or "_latch" "_cstrs"))))))
      (1 font-lock-constant-face))
     (,(rx-to-string
        `(seq "(" (group-n 1 (or ,@(mapcar #'symbol-name comp-limple-ops)))))
@@ -1841,12 +1853,11 @@ into the C code forwarding the compilation unit."
 \f
 ;;; conditional branches rewrite pass specific code.
 
-(defun comp-emit-assume (target-slot rhs bb-name kind)
+(defun comp-emit-assume (target-slot rhs bb kind)
   "Emit an assume of kind KIND for TARGET-SLOT being RHS.
-The assume is emitted at the beginning of the block named
-BB-NAME."
+The assume is emitted at the beginning of the block BB."
   (push `(assume ,(make-comp-mvar :slot target-slot) ,rhs ,kind)
-       (comp-block-insns (gethash bb-name (comp-func-blocks comp-func))))
+       (comp-block-insns bb))
   (setf (comp-func-ssa-status comp-func) 'dirty))
 
 (defun comp-cond-cstr-target-slot (slot-num exit-insn bb)
@@ -1867,34 +1878,67 @@ Return the corresponding rhs slot number."
            (setf res rhs)))
      finally (cl-assert nil))))
 
+(defun comp-add-new-block-beetween (bb-symbol bb-a bb-b)
+  "Create a new basic-block named BB-SYMBOL and add it between BB-A and BB-B."
+  (cl-loop
+   with new-bb = (make-comp-block-cstr :name bb-symbol
+                                       :insns `((jump ,(comp-block-name bb-b))))
+   with new-edge = (make-comp-edge :src bb-a :dst new-bb)
+   for ed in (comp-block-in-edges bb-b)
+   when (eq (comp-edge-src ed) bb-a)
+   do
+   ;; Connect `ed' to `new-bb' and disconnect it from `bb-a'.
+   (cl-assert (memq ed (comp-block-out-edges bb-a)))
+   (setf (comp-edge-src ed) new-bb
+         (comp-block-out-edges bb-a) (delq ed (comp-block-out-edges bb-a)))
+   (push ed (comp-block-out-edges new-bb))
+   ;; Connect `bb-a' `new-bb' with `new-edge'.
+   (push (comp-block-out-edges bb-a) new-edge)
+   (push (comp-block-in-edges new-bb) new-edge)
+   (setf (comp-func-ssa-status comp-func) 'dirty)
+   ;; Add `new-edge' to the current function and return it.
+   (cl-return (puthash bb-symbol new-bb (comp-func-blocks comp-func)))
+   finally (cl-assert nil)))
+
+(defun comp-cond-cstr-target-block (curr-bb target-bb-sym)
+  "Return the appropriate basic block to add constraint assumptions into.
+CURR-BB is the current basic block.
+TARGET-BB-SYM is the symbol name of the target block."
+  (let ((target-bb (gethash target-bb-sym
+                            (comp-func-blocks comp-func))))
+    (if (= (length (comp-block-in-edges target-bb)) 1)
+        ;; If block has only one predecessor is already suitable for
+        ;; adding constraint assumptions.
+        target-bb
+      (comp-add-new-block-beetween (intern (concat (symbol-name target-bb-sym)
+                                                   "_cstrs"))
+                                   curr-bb target-bb))))
+
 (defun comp-cond-cstr-func ()
   "`comp-cond-cstr' worker function for each selected function."
   (cl-loop
    for b being each hash-value of (comp-func-blocks comp-func)
-   do (cl-loop
-       named in-the-basic-block
-       for insns-seq on (comp-block-insns b)
-       do (pcase insns-seq
-            (`((set ,(and (pred comp-mvar-p) cond)
-                    (,(pred comp-call-op-p)
-                     ,(and (or 'eq 'eql '= 'equal) test-fn) ,op1 ,op2))
-              (comment ,_comment-str)
-              (cond-jump ,cond ,(pred comp-mvar-p) ,bb-1 ,_bb-2))
-             ;; FIXME We guard the target block against having more
-             ;; then one predecessor.  The right fix will be to add a
-             ;; new dedicated basic block for the assumptions so we
-             ;; can proceed always.
-             (when (= (length (comp-block-in-edges
-                                 (gethash bb-1
-                                          (comp-func-blocks comp-func))))
-                      1)
-               (when-let ((target-slot1 (comp-cond-cstr-target-slot
-                                         (comp-mvar-slot op1) (car insns-seq) b)))
-                 (comp-emit-assume target-slot1 op2 bb-1 test-fn))
-               (when-let ((target-slot2 (comp-cond-cstr-target-slot
-                                         (comp-mvar-slot op2) (car insns-seq) b)))
-                 (comp-emit-assume target-slot2 op1 bb-1 test-fn)))
-            (cl-return-from in-the-basic-block))))))
+   do
+   (cl-loop
+    named in-the-basic-block
+    for insns-seq on (comp-block-insns b)
+    do
+    (pcase insns-seq
+      (`((set ,(and (pred comp-mvar-p) cond)
+              (,(pred comp-call-op-p)
+               ,(and (or 'eq 'eql '= 'equal) test-fn) ,op1 ,op2))
+        (comment ,_comment-str)
+        (cond-jump ,cond ,(pred comp-mvar-p) . ,blocks))
+       (let* ((bb-1 (car blocks))
+              (bb-target (comp-cond-cstr-target-block b bb-1)))
+         (setf (car blocks) (comp-block-name bb-target))
+         (when-let ((target-slot1 (comp-cond-cstr-target-slot
+                                   (comp-mvar-slot op1) (car insns-seq) b)))
+           (comp-emit-assume target-slot1 op2 bb-target test-fn))
+         (when-let ((target-slot2 (comp-cond-cstr-target-slot
+                                   (comp-mvar-slot op2) (car insns-seq) b)))
+           (comp-emit-assume target-slot2 op1 bb-target test-fn)))
+       (cl-return-from in-the-basic-block))))))
 
 (defun comp-cond-cstr (_)
   "Rewrite conditional branches adding appropriate 'assume' insns.
@@ -2002,45 +2046,38 @@ blocks."
 
 (defun comp-compute-edges ()
   "Compute the basic block edges for the current function."
-  (cl-flet ((edge-add (&rest args &aux (n (funcall
-                                           (comp-func-edge-cnt-gen comp-func))))
-                      (puthash
-                       n
-                       (apply #'make--comp-edge :number n args)
-                       (comp-func-edges-h comp-func))))
-
-    (cl-loop with blocks = (comp-func-blocks comp-func)
-             for bb being each hash-value of blocks
-             for last-insn = (car (last (comp-block-insns bb)))
-             for (op first second third forth) = last-insn
-             do (cl-case op
-                  (jump
-                   (edge-add :src bb :dst (gethash first blocks)))
-                  (cond-jump
-                   (edge-add :src bb :dst (gethash third blocks))
-                   (edge-add :src bb :dst (gethash forth blocks)))
-                  (cond-jump-narg-leq
-                   (edge-add :src bb :dst (gethash second blocks))
-                   (edge-add :src bb :dst (gethash third blocks)))
-                  (push-handler
-                   (edge-add :src bb :dst (gethash third blocks))
-                   (edge-add :src bb :dst (gethash forth blocks)))
-                  (return)
-                  (otherwise
-                   (signal 'native-ice
-                           (list "block does not end with a branch"
-                                 bb
-                                 (comp-func-name comp-func)))))
-             ;; Update edge refs into blocks.
-             finally
-             (cl-loop
-              for edge being the hash-value in (comp-func-edges-h comp-func)
-              do
-              (push edge
-                    (comp-block-out-edges (comp-edge-src edge)))
-              (push edge
-                    (comp-block-in-edges (comp-edge-dst edge))))
-             (comp-log-edges comp-func))))
+  (cl-loop with blocks = (comp-func-blocks comp-func)
+           for bb being each hash-value of blocks
+           for last-insn = (car (last (comp-block-insns bb)))
+           for (op first second third forth) = last-insn
+           do (cl-case op
+                (jump
+                 (make-comp-edge :src bb :dst (gethash first blocks)))
+                (cond-jump
+                 (make-comp-edge :src bb :dst (gethash third blocks))
+                 (make-comp-edge :src bb :dst (gethash forth blocks)))
+                (cond-jump-narg-leq
+                 (make-comp-edge :src bb :dst (gethash second blocks))
+                 (make-comp-edge :src bb :dst (gethash third blocks)))
+                (push-handler
+                 (make-comp-edge :src bb :dst (gethash third blocks))
+                 (make-comp-edge :src bb :dst (gethash forth blocks)))
+                (return)
+                (otherwise
+                 (signal 'native-ice
+                         (list "block does not end with a branch"
+                               bb
+                               (comp-func-name comp-func)))))
+           ;; Update edge refs into blocks.
+           finally
+           (cl-loop
+            for edge being the hash-value in (comp-func-edges-h comp-func)
+            do
+            (push edge
+                  (comp-block-out-edges (comp-edge-src edge)))
+            (push edge
+                  (comp-block-in-edges (comp-edge-dst edge))))
+           (comp-log-edges comp-func)))
 
 (defun comp-collect-rev-post-order (basic-block)
   "Walk BASIC-BLOCK children and return their name in reversed post-order."