js-assembler/fracture.rkt
#lang typed/racket/base

(require "assemble-structs.rkt"
         "collect-jump-targets.rkt"
         "../compiler/il-structs.rkt"
         "../compiler/expression-structs.rkt"
         racket/list)


;; Breaks up a sequence of statements into a list of basic blocks.
;;
;; The first basic block is special, and represents the start of execution.
;;
;; A basic block consists of a sequence of straight line statements, followed by one of
;; the following:
;;
;;     * A conditional jump.
;;     * An unconditional jump.
;;     * Termination.

(provide fracture)



;; fracture: (listof stmt) -> (listof basic-block)
(: fracture ((Listof Statement) -> (values (Listof BasicBlock)
                                           (Listof Symbol))))
(define (fracture stmts)
  (let*: ([first-block-label : Symbol (if (and (not (empty? stmts))
                                               (symbol? (first stmts)))
                                          (first stmts)
                                          (make-label 'start))]
          [stmts : (Listof Statement) (if (and (not (empty? stmts))
                                               (symbol? (first stmts)))
                                          (rest stmts)
                                          stmts)]
          [jump-targets : (Listof Symbol)
                        (cons first-block-label (collect-general-jump-targets stmts))]
          [entry-points : (Listof Symbol)
                        (cons first-block-label (collect-entry-points stmts))])
    (let: loop : (values (Listof BasicBlock) (Listof Symbol))
      ([name : Symbol first-block-label]
       [acc : (Listof UnlabeledStatement) '()]
       [basic-blocks  : (Listof BasicBlock) '()]
       [stmts : (Listof Statement) stmts]
       [last-stmt-goto? : Boolean #f])
      (cond
        [(null? stmts)
         (values (reverse (cons (make-BasicBlock name (reverse acc))
                                basic-blocks))
                 entry-points)]
        [else
         (let: ([first-stmt : Statement (car stmts)])
           (: do-on-label (Symbol -> (values (Listof BasicBlock) (Listof Symbol))))
           (define (do-on-label label-name)
             (cond
               [(member label-name jump-targets)
                (loop label-name
                      '()
                      (cons (make-BasicBlock 
                             name  
                             (if last-stmt-goto? 
                                 (reverse acc)
                                 (reverse (append `(,(make-GotoStatement (make-Label label-name)))
                                                  acc))))
                            basic-blocks)
                      (cdr stmts)
                      last-stmt-goto?)]
               [else
                (loop name
                      acc
                      basic-blocks
                      (cdr stmts)
                      last-stmt-goto?)]))
           (cond
             [(symbol? first-stmt)
              (do-on-label first-stmt)]
             [(LinkedLabel? first-stmt)
              (do-on-label (LinkedLabel-label first-stmt))]
             [else
              (loop name
                    (cons first-stmt acc)
                    basic-blocks
                    (cdr stmts)
                    (GotoStatement? (car stmts)))]))]))))