Fix bug with empty infinite loops
authorDavid Vázquez <davazp@gmail.com>
Sun, 19 May 2013 23:18:09 +0000 (00:18 +0100)
committerDavid Vázquez <davazp@gmail.com>
Sun, 19 May 2013 23:18:09 +0000 (00:18 +0100)
experimental/compiler.lisp

index 858865e..4fe65c5 100644 (file)
 
 ;;; Return T if B is an empty basic block and NIL otherwise.
 (defun empty-block-p (b)
-  (block-exit-p (node-next (block-entry b))))
+  (or (boundary-block-p b)
+      (block-exit-p (node-next (block-entry b)))))
 
 (defun boundary-block-p (block)
   (or (component-entry-p block)
     (let ((entry (make-component-entry :component *component*))
           (exit (make-component-exit :component *component*))
           (block (make-empty-block)))
+      (push entry (component-blocks *component*))
+      (push exit (component-blocks *component*))
       (setf (block-succ entry) (list block)
             (block-pred exit)  (list block)
             (block-succ block) (list exit)
 (defun map-postorder-blocks (function component)
   (let ((seen nil))
     (labels ((compute-from (block)
-               (unless (or (component-exit-p block) (find block seen))
+               (unless (find block seen)
                  (push block seen)
                  (dolist (successor (block-succ block))
                    (unless (component-exit-p block)
                      (compute-from successor)))
                  (funcall function block))))
-      (compute-from (unlist (block-succ (component-entry component))))
+      (compute-from (component-entry component))
       nil)))
 
 ;;; Change all the predecessors of BLOCK to precede NEW-BLOCK
 ;;; Try to coalesce BLOCK with the successor if it is unique and block
 ;;; is its unique predecessor.
 (defun maybe-coalesce-block (block)
-  (when (singlep (block-succ block))
+  (when (and (singlep (block-succ block)) (not (component-entry-p block)))
     (let ((succ (first (block-succ block))))
       (when (and (not (component-exit-p succ)) (singlep (block-pred succ)))
         (link-nodes (node-prev (block-exit block))
            (when (eq (block-data succ) 'reachable)
              (remove block (block-pred succ)))))
         ;; Delete empty blocks
-        ((empty-block-p block)
+        ((and (empty-block-p block)
+              (not (boundary-block-p block))
+              ;; We cannot delete a block if it is its own successor,
+              ;; even thought it is empty.
+              (not (member block (block-succ block))))
          (delete-block block))
         ;; The rest of blocks remain in the component.
         (t
 
 (defun compute-reverse-post-order (component)
   (let ((output nil)
-        (count 0))
+        (index (length (component-blocks component))))
     (flet ((add-block-to-list (block)
              (push block output)
-             (setf (block-order block) (incf count))))
+             (setf (block-order block) (decf index))))
       (map-postorder-blocks #'add-block-to-list component))
     (setf (component-reverse-post-order-p component) t)
     (setf (component-blocks component) output)))
 
-;;; Iterate across blocks in COMPONENT in reverse post order.
-(defmacro do-blocks-forward ((block component &optional result) &body body)
-  (with-gensyms (g!component)
-    `(let ((,g!component ,component))
-       (dolist (,block (if (component-reverse-post-order-p ,g!component)
-                           (component-blocks ,g!component)
-                           (error "reverse post order was not computed yet."))
-                 ,result)
-         ,@body))))
 
-;;; Iterate across blocks in COMPONENT in post order.
-(defmacro do-blocks-backward ((block component &optional result) &body body)
-  (with-gensyms (g!component)
-    `(let ((,g!component ,component))
-       (dolist (,block (if (component-reverse-post-order-p ,g!component)
-                           (reverse (component-blocks ,g!component))
-                           (error "reverse post order was not computed yet."))
+(defmacro do-blocks% ((block component &optional reverse ends result) &body body)
+  (with-gensyms (g!component g!blocks)
+    `(let* ((,g!component ,component)
+            (,g!blocks ,(if reverse
+                            `(reverse (component-blocks ,g!component))
+                            `(component-blocks ,g!component))))
+       ;; Do we have the information available?
+       (unless (component-reverse-post-order-p ,g!component)
+         (error "Reverse post order was not computed yet."))
+       (dolist (,block  ,(if (member ends '(:head :both))
+                             `,g!blocks
+                             `(cdr ,g!blocks))
                  ,result)
+         ,@(if (member ends '(:tail :both))
+               nil
+               `((if (component-exit-p ,block) (return))))
          ,@body))))
 
+;;; Iterate across blocks in COMPONENT in reverse post order.
+(defmacro do-blocks-forward ((block component &optional ends result) &body body)
+  `(do-blocks% (,block ,component nil ,ends ,result)
+     ,@body))
+
+;;; Iterate across blocks in COMPONENT in reverse post order.
+(defmacro do-blocks-backward ((block component &optional ends result) &body body)
+  `(do-blocks% (,block (reverse ,component) t ,ends ,result)
+     ,@body))
+
+
 (defun compute-dominators (component)
   ;; Initialize the dominators of the entry to the component to be
   ;; empty and the power set of the set of blocks for proper basic
     ;; set of (proper) basic blocks.
     (setf (block-dominators% (component-entry component))
           (make-array n :element-type 'bit :initial-element 0))
-    (dolist (block (component-blocks component))
+    (setf (aref (block-dominators% (component-entry component)) 0) 1)
+    (do-blocks-forward (block component :tail)
       (setf (block-dominators% block) (make-array n :element-type 'bit :initial-element 1))))
   ;; Iterate across the blocks in the component removing non domintors
   ;; until it reaches a fixed point.
-  (do ((i 0 0)
-       (iteration 0 (1+ iteration))
+  (do ((i 1 1)
        (changes t))
       ((not changes))
     (setf changes nil)
-    (do-blocks-forward (block component)
-      (let* ((predecessors (block-pred block)))
-        (bit-and (block-dominators% block) (block-dominators% (first predecessors)) t)
+    (do-blocks-forward (block component :tail)
+      ;; We compute the new set of dominators for this iteration in a
+      ;; fresh set NEW-DOMINATORS. So we do NOT modify the old
+      ;; dominators. It is important because the block could predeces
+      ;; itself. Indeed, it allows us to check if the set of
+      ;; dominators changed.
+      (let* ((predecessors (block-pred block))
+             (new-dominators (copy-seq (block-dominators% (first predecessors)))))
         (dolist (pred (rest predecessors))
-          (bit-and (block-dominators% block) (block-dominators% pred) t))
-        (setf (aref (block-dominators% block) i) 1)
-        (setf changes (or changes (not (equal (block-dominators% block) (block-dominators% block)))))
+          (bit-and new-dominators (block-dominators% pred) t))
+        (setf (aref new-dominators i) 1)
+        (unless changes
+          (setq changes (not (equal (block-dominators% block) new-dominators))))
+        (setf (block-dominators% block) new-dominators)
         (incf i)))))
 
 ;;; Return T if BLOCK1 dominates BLOCK2, else return NIL.
   (let ((order (block-order block1)))
     (= 1 (aref (block-dominators% block2) order))))
 
+(defun loop-header-p (block)
+  (some (lambda (pred) (dominate-p block pred))
+        (block-pred block)))
 
 ;;;; IR Debugging
 ;;;;
   (terpri))
 
 (defun print-block (block)
-  (write-line (format-block-name block))
+  (write-string (format-block-name block))
+  (if (loop-header-p block)
+      (write-line " [LOOP_HEADER]")
+      (terpri))
   (do-nodes (node block)
     (print-node node))
   (when (singlep (block-succ block))
       (ir-convert form (make-lvar :id "out"))
       (ir-normalize)
       (compute-reverse-post-order *component*)
+      (compute-dominators *component*)
       (/print *component*)
       *component*)))