comp/postprocess.ss
#lang scheme/base

;; POSTPROCESSING and OPTIMIZATION

;;  * The first pass of the compiler is purely functional: it uses
;;    labels tags for chunks/chains of assembly code.  The code refers
;;    back to the labels.

;;  * In the postprocessor and assembler, a graph structure is
;;    constructued by linking the labels back to code.

(require
 "../target.ss"
 "../tools.ss"
 "../ns.ss"
 "../scat.ss"
 "../coma/macro.ss"
 "../control/2stack.ss"
 scheme/pretty
 scheme/match)

(provide
 words->cfg!
 macro->postprocess

 empty-ctrl->asm
 assert-empty-ctrl
 
 )

(define (empty-ctrl->asm state [name ""])
   (match state
          ((struct 2stack (ctor asm ctrl))
           (unless (null? ctrl)
             (error 'non-null-compilation-stack
                    "~a ~s" name ctrl))
           asm)))

(define (assert-empty-ctrl . a) (void (apply empty-ctrl->asm a)))


;; Turn a (set-of (list-of (list word code))) into a linked up
;; imperative data structure and perform postprocessing optimizations.

(define (words->cfg! compiled-words [postproc-asm
                                             (lambda (x) x)])
  (define roots (link! compiled-words))
    
  (define (all: . fns)
    (let ((words (apply append (map target-chain->list roots))))
      (for-each (lambda (fn) (for-each fn words)) fns)))

  ;; Individual optimizations
  (all: (target-post! postproc-asm))

  ;; No global optimizations yet
  
  roots)

;; Converts the (list-of (list-of (list word code))) to (list-of
;; word), with all graph structure linked in (code + chain)

(define (link! chains)
  (define link-chain!
    (match-lambda
     ((list w) w)
     ((list-rest w+ w ws)
      (set-target-word-next! w w+)
      (link-chain! (cons w ws)))))
  (define (link-code! word code)
    (set-target-word-code! word code)
    word)
  (map link-chain!
       (map (lambda (chain)
              (map* link-code! chain))
            chains)))


;; PER-WORD optimizations


;; Hook for target specific assembly postprocessing.  I.e. for PIC18
;; this translates the pseudo ops QW JW CW to real assembly code, and
;; performs SAVE elimination.

;; FIXME: the real question: why not postpone all optimisations till
;; later, and have the core language be simple?


(define ((target-post! postproc-asm) word)
  (set-target-word-code! word
                         (postproc-asm (target-word-code word))))


;; Lift a macro to a function that postprocesses a list of reversed
;; assembly code, by executing the macro after pushing the next
;; instruction to the assembly state. (Note that these macros are only
;; allowed to use the 2stack state.)
(define (macro->postprocess macro [name #f])
  (lambda (reverse-asm)
    (let next ((in-asm  (reverse reverse-asm))
               (out-asm '()))
        (if (null? in-asm)
            out-asm
            (let* ((in  (cons (car in-asm) out-asm))
                   (out (empty-ctrl->asm
                         (macro (make-state:2stack in '())))))
              (when name
                (printf "postproc: ~a\n" name)
                (for ((a (reverse in)))  (printf "\t~a\n" (instruction->string a))) (display "->\n")
                (for ((a (reverse out))) (printf "\t~a\n" (instruction->string a))) (display "\n\n"))
              (next (cdr in-asm) out))))))
                      


;; More optimizations:

;; * Serialize the code graph. Optimize jump sizes for words ending in
;;   'jw' (after dead code elimination), and eliminate jumps to the
;;   next word.

;; * jump chaining.