Naive reduce-component
authorDavid Vázquez <davazp@gmail.com>
Thu, 23 May 2013 23:47:52 +0000 (00:47 +0100)
committerDavid Vázquez <davazp@gmail.com>
Thu, 23 May 2013 23:47:52 +0000 (00:47 +0100)
experimental/compiler.lisp

index c9a223d..448683e 100644 (file)
@@ -49,6 +49,8 @@
         (prog1 1
           (push (cons class 1) *counter-alist*)))))
 
+(defmacro while (condition &body body)
+  `(do nil ((not ,condition)) ,@body))
 
 ;;;; Intermediate representation structures
 ;;;;
 (defstruct (primitive-call (:include combination))
   function)
 
-
 ;;; A conditional branch. If the LVAR is not NIL, then we will jump to
 ;;; the basic block CONSEQUENT, jumping to ALTERNATIVE otherwise. By
 ;;; definition, a conditional must appear at the end of a basic block.
   alternative)
 
 
-
 ;;; Blocks are `basic block`. Basic blocks are organized as a control
 ;;; flow graph with some more information in omponents.
 (defstruct (basic-block
 ;;;; optimizations and code generation. Indeed, we provide some
 ;;;; abstractions to use this information.
 
-(defun compute-reverse-post-order (component)
+(defun compute-reverse-post-order (&optional (component *component*))
   (let ((output nil)
         (index (length (component-blocks component))))
     (flet ((add-block-to-list (block)
   `(do-blocks% (,block (reverse ,component) t ,ends ,result)
      ,@body))
 
-
-(defun compute-dominators (component)
+(defun compute-dominators (&optional (component *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
   ;; blocks in the component.
   (some (lambda (pred) (dominate-p block pred))
         (block-pred block)))
 
-;;; This function duplicates the block in component for each input
-;;; edge. A technique useful to make a general flowgraph reducible.
-(defun node-splitting (block)
-  (let ((predecessors (block-pred block)))
-    (when predecessors
-      (setf (block-pred block) (list (car predecessors)))
-      (dolist (pred (cdr predecessors))
-        (let ((newblock (copy-basic-block block)))
-          (setf (block-id newblock) (generate-id 'basic-block))
-          (push newblock (component-blocks (block-component block)))
-          (setf (block-pred newblock) (list pred))
-          (setf (block-succ pred) (substitute newblock block (block-succ pred))))))))
+;;; Save the edges of the flow graph of the current component. Then,
+;;; execute BODY as an implicit progn and restore the edges even if
+;;; BODY exists with an abnormal exit.
+(defmacro save-component-edges (&body body)
+  (with-gensyms (edges)
+    `(let (,edges)
+       ;; Save edges
+       (dolist (block (component-blocks *component*))
+         (push (list block (block-succ block) (block-pred block)) ,edges))
+       (unwind-protect (progn ,@body)
+         ;; Restore edges
+         (dolist (entry ,edges)
+           (destructuring-bind (block succ pred) entry
+             (setf (block-succ block) succ
+                   (block-pred block) pred)))))))
+
+(defun reduce-component (&optional (component *component*))
+  (let* ((list-blocks (component-blocks component))
+         ;; A vector of the blocks in the component. Blocks are added
+         ;; and deleted always at the fill pointer of the vector.
+         (vector-blocks
+          (make-array (length list-blocks)
+                      :initial-contents (component-blocks component)
+                      :adjustable t
+                      :fill-pointer t))
+         ;; A list of nodes which have been splitted during the
+         ;; reduction of the component. We apply 
+         (nodes-to-split '()))
+    (flet (;; Remove an edge from a block to itself
+           (T1 (block)
+             (when (member block (block-succ block))
+               (setf (block-succ block) (remove block (block-succ block)))
+               (setf (block-pred block) (remove block (block-pred block)))
+               t))
+           ;; Collapse a block back into its predecessor if it is unique
+           (T2 (block)
+             (when (singlep (block-pred block))
+               (let ((pred (unlist (block-pred block))))
+                 (setf (block-succ pred) (remove block (block-succ pred)))
+                 (dolist (succ (block-succ block))
+                   (pushnew succ (block-succ pred))
+                   (setf (block-pred succ) (substitute pred block (block-pred succ)))))
+               t))
+           ;; This function duplicates the block in component for each input
+           ;; edge. A technique useful to make a general flowgraph reducible.
+           (S (block)
+             (let ((predecessors (block-pred block)))
+               (when predecessors
+                 (setf (block-pred block) (list (car predecessors)))
+                 (let ((newblocks '()))
+                   (dolist (pred (cdr predecessors) newblocks)
+                     (let ((newblock (copy-basic-block block)))
+                       (setf (block-id newblock) (generate-id 'basic-block))
+                       (setf (block-pred newblock) (list pred))
+                       (setf (block-succ pred) (substitute newblock block (block-succ pred)))
+                       (push newblock newblocks))))))))
+      ;; Reduce component using the transformations T1 and T2 as much
+      ;; as possible. Then apply the node splitting transformation (S)
+      ;; to some blocks. By now, we apply it to every block with
+      ;; multiple predecessors, but most smart policy is possible,
+      ;; see: "Making Graphs Reducible with Controlled Node
+      ;; Splitting". These transformations do not affect to the
+      ;; original component flowgraph out of the SAVE-COMPONENT-EDGES
+      ;; extent. Eventually, we will reduce the component to a single
+      ;; node and the reduction finishes.
+      (save-component-edges
+        (while (< 1 (fill-pointer vector-blocks))
+          ;; Reduce component using T1 and T2 as much as possible
+          (do ((changes t))
+              ((not changes))
+            (setf changes nil)
+            (do ((i 0 (1+ i)))
+                ((>= i (length vector-blocks)))
+              (let ((block (aref vector-blocks i)))
+                (when (T1 block)
+                  (setf changes t))
+                (when (T2 block)
+                  ;; Move the block to the end of the vector and
+                  ;; remove decrementing the fill pointer.
+                  (rotatef (aref vector-blocks i) (aref vector-blocks (1- (length vector-blocks))))
+                  (vector-pop vector-blocks)
+                  (setf changes t)))))
+          ;; TODO: Implement a better selection of the nodes in the
+          ;; flowgraph to split. Paper to study: "Making Graphs
+          ;; Reducible with Controlled Node Splitting".
+          (dotimes (i (length vector-blocks))
+            (let ((block (aref vector-blocks i)))
+              (when (S block)
+                (push block nodes-to-split))))))
+      ;; Reapply the node splitting transformation to the same nodes
+      ;; on the original component.
+      (when nodes-to-split
+        (warn "Irreducible component. Applying node splitting")
+        (dolist (block nodes-to-split)
+          (assert (member block (component-blocks component)))
+          (dolist (newblock (S block))
+            (push newblock (component-blocks component))))))))
 
 
 
     (with-component-compilation ('toplevel)
       (ir-convert form (make-lvar :id "out"))
       (ir-normalize)
-      (compute-reverse-post-order *component*)
-      (compute-dominators *component*)
+      (reduce-component)
+      (compute-reverse-post-order)
+      (compute-dominators)
       (/print *component*)
       *component*)))