home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MacFormat 1994 November
/
macformat-018.iso
/
Utility Spectacular
/
Developer
/
macgambit-20-compiler-src-p2
/
Interp⁄Comp (.scm)
/
pvm.scm
< prev
next >
Encoding:
Amiga
Atari
Commodore
DOS
FM Towns/JPY
Macintosh
Macintosh JP
NeXTSTEP
RISC OS/Acorn
Shift JIS
UTF-8
Wrap
Text File
|
1994-07-26
|
61.1 KB
|
1,750 lines
|
[
TEXT/gamI
]
;==============================================================================
; file: "pvm.scm"
;------------------------------------------------------------------------------
;
; Virtual machine abstraction package:
; -----------------------------------
; (See file 'doc/pvm' for details on the virtual machine)
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;
; Virtual machine operands:
; ------------------------
;
; Operands are represented with small integers. Operands can thus be tested
; for equality using 'eqv?'. 'eqv-opnd?' also tests for equal operands but
; it disregards the '?' flag. The encoding is as follows:
;
; OPERAND ENCODING
;
; reg(n) 0 + n
; stk(n) 10000 + n
; lbl(n) 20000 + n
; glo(name) 30000 + index in operand table
; clo(opnd,n) 40000 + index in operand table
; obj(x) 50000 + index in operand table
; ?loc 60000 + encoding(loc)
; Utilities:
; ---------
(define *opnd-table* '())
(define *opnd-table-alloc* '())
(define opnd-table-size 10000)
(define (enter-opnd arg1 arg2)
(let loop ((i 0))
(if (< i *opnd-table-alloc*)
(let ((x (vector-ref *opnd-table* i)))
(if (and (eqv? (car x) arg1) (eqv? (cdr x) arg2))
i
(loop (+ i 1))))
(if (< *opnd-table-alloc* opnd-table-size)
(begin
(set! *opnd-table-alloc* (+ *opnd-table-alloc* 1))
(vector-set! *opnd-table* i (cons arg1 arg2))
i)
(compiler-limitation-error
"program is too long [virtual machine operand table overflow]")))))
(define (eqv-opnd? opnd1 opnd2)
(eqv? (strip-pot-fut opnd1) (strip-pot-fut opnd2)))
(define (contains-opnd? opnd1 opnd2) ; does opnd2 contain opnd1?
(cond ((eqv-opnd? opnd1 opnd2)
#t)
((clo? opnd2)
(contains-opnd? opnd1 (clo-base opnd2)))
(else
#f)))
(define (any-contains-opnd? opnd opnds)
(if (null? opnds)
#f
(or (contains-opnd? opnd (car opnds))
(any-contains-opnd? opnd (cdr opnds)))))
; Locations:
; ---------
; -- location is a register (first is number 0)
(define (make-reg num) num)
(define (reg? x) (< (modulo x 60000) 10000))
(define (reg-num x) (modulo x 10000))
; -- location is in the stack (first slot in procedure's frame is number 1)
(define (make-stk num) (+ num 10000))
(define (stk? x) (= (quotient (modulo x 60000) 10000) 1))
(define (stk-num x) (modulo x 10000))
; -- location is a global variable
(define (make-glo name) (+ (enter-opnd name #t) 30000))
(define (glo? x) (= (quotient (modulo x 60000) 10000) 3))
(define (glo-name x) (car (vector-ref *opnd-table* (modulo x 10000))))
; -- location is a closed variable (base is ptr to closure env, index >= 1)
(define (make-clo base index) (+ (enter-opnd base index) 40000))
(define (clo? x) (= (quotient (modulo x 60000) 10000) 4))
(define (clo-base x) (car (vector-ref *opnd-table* (modulo x 10000))))
(define (clo-index x) (cdr (vector-ref *opnd-table* (modulo x 10000))))
; Values:
; ------
; -- value is the address of a local label
(define (make-lbl num) (+ num 20000))
(define (lbl? x) (= (quotient (modulo x 60000) 10000) 2))
(define (lbl-num x) (modulo x 10000))
(define label-limit 9999) ; largest label
; -- value is a scheme object
(define (make-obj val) (+ (enter-opnd val #f) 50000))
(define (obj? x) (= (quotient (modulo x 60000) 10000) 5))
(define (obj-val x) (car (vector-ref *opnd-table* (modulo x 10000))))
; Potentially future flag: (operands that should be touched to get their value)
; -----------------------
(define (put-pot-fut loc) (+ loc 60000))
(define (pot-fut? x) (>= x 60000))
(define (strip-pot-fut x) (modulo x 60000))
(define (set-pot-fut loc flag) (if flag (put-pot-fut loc) loc))
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;
; Processor context descriptions:
; ------------------------------
(define (make-pcontext fs map)
(vector fs map))
(define (pcontext-fs x) (vector-ref x 0))
(define (pcontext-map x) (vector-ref x 1))
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;
; Frame description:
; -----------------
(define (make-frame size slots regs closed live)
(vector size slots regs closed live))
(define (frame-size x) (vector-ref x 0))
(define (frame-slots x) (vector-ref x 1))
(define (frame-regs x) (vector-ref x 2))
(define (frame-closed x) (vector-ref x 3))
(define (frame-live x) (vector-ref x 4))
(define (frame-eq? x y)
(= (frame-size x) (frame-size y)))
(define (frame-truncate frame nb-slots)
(let ((fs (frame-size frame)))
(make-frame nb-slots
(nth-after (frame-slots frame) (- fs nb-slots))
(frame-regs frame)
(frame-closed frame)
(frame-live frame))))
(define (frame-live? var frame)
(let ((live (frame-live frame)))
(if (eq? var closure-env-var)
(let ((closed (frame-closed frame)))
(if (or (set-member? var live)
(not (set-empty? (set-intersection live (list->set closed)))))
closed
#f))
(if (set-member? var live)
var
#f))))
(define (frame-first-empty-slot frame)
(let loop ((i 1) (s (reverse (frame-slots frame))))
(if (pair? s)
(if (frame-live? (car s) frame)
(loop (+ i 1) (cdr s))
i)
i)))
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;
; Procedure objects:
; -----------------
(define (make-proc-obj
name
primitive?
code
call-pat
side-effects?
strict-pat
type)
(let ((proc-obj
(vector
proc-obj-tag
name
primitive?
code
call-pat
#f ; test
#f ; inlinable
#f ; specialize
side-effects?
strict-pat
type)))
(proc-obj-specialize-set! proc-obj (lambda (decls) proc-obj))
proc-obj))
(define proc-obj-tag (list 'PROC-OBJ))
(define (proc-obj? x)
(and (vector? x)
(> (vector-length x) 0)
(eq? (vector-ref x 0) proc-obj-tag)))
(define (proc-obj-name obj) (vector-ref obj 1))
(define (proc-obj-primitive? obj) (vector-ref obj 2))
(define (proc-obj-code obj) (vector-ref obj 3))
(define (proc-obj-call-pat obj) (vector-ref obj 4))
(define (proc-obj-test obj) (vector-ref obj 5))
(define (proc-obj-inlinable obj) (vector-ref obj 6))
(define (proc-obj-specialize obj) (vector-ref obj 7))
(define (proc-obj-side-effects? obj) (vector-ref obj 8))
(define (proc-obj-strict-pat obj) (vector-ref obj 9))
(define (proc-obj-type obj) (vector-ref obj 10))
(define (proc-obj-code-set! obj x) (vector-set! obj 3 x))
(define (proc-obj-test-set! obj x) (vector-set! obj 5 x))
(define (proc-obj-inlinable-set! obj x) (vector-set! obj 6 x))
(define (proc-obj-specialize-set! obj x) (vector-set! obj 7 x))
(define (make-pattern min-args nb-parms rest?)
(let loop ((x (if rest? (- nb-parms 1) (list nb-parms)))
(y (if rest? (- nb-parms 1) nb-parms)))
(let ((z (- y 1)))
(if (< z min-args) x (loop (cons z x) z)))))
(define (pattern-member? n pat) ; tests if 'n' is a member of pattern 'pat'
(cond ((pair? pat)
(if (= (car pat) n) #t (pattern-member? n (cdr pat))))
((null? pat)
#f)
(else
(<= pat n))))
(define (type-name type)
(if (pair? type) (car type) type))
(define (type-pot-fut? type)
(pair? type))
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;
; Basic block set manipulation:
; ----------------------------
; Virtual instructions have a linear structure. However, this is not how
; they are put together to form a piece of code. Rather, virtual instructions
; are grouped into 'basic blocks' which are 'linked' together. A basic block
; is a LABEL instruction followed by a sequence of non-branching instructions
; (i.e. APPLY, COPY or MAKE_CLOSURES) terminated by a single branch
; instruction (i.e. COND or JUMP). Links between basic
; blocks are denoted using label references. When a basic block ends with a
; COND instruction, the block is linked to the two basic blocks corresponding
; to the two possible control paths out of the COND instruction. When a basic
; block ends with a JUMP instruction, there is either zero or one link.
;
; Basic blocks naturally group together to form 'basic block sets'. A basic
; block set describes all the code of a procedure.
(define (make-bbs)
(define (limit-error)
(compiler-limitation-error "procedure is too long [too many labels]"))
(vector (make-counter label-limit limit-error) ; 0 - local label counter
(queue-empty) ; 1 - basic block queue
'())) ; 2 - entry label number
(define (bbs-lbl-counter bbs) (vector-ref bbs 0))
(define (bbs-bb-queue bbs) (vector-ref bbs 1))
(define (bbs-bb-queue-set! bbs bbq) (vector-set! bbs 1 bbq))
(define (bbs-entry-lbl-num bbs) (vector-ref bbs 2))
(define (bbs-entry-lbl-num-set! bbs lbl-num) (vector-set! bbs 2 lbl-num))
(define (bbs-new-lbl! bbs)
((bbs-lbl-counter bbs)))
(define (lbl-num->bb lbl-num bbs)
(let loop ((bb-list (queue->list (bbs-bb-queue bbs))))
(if (= (bb-lbl-num (car bb-list)) lbl-num)
(car bb-list)
(loop (cdr bb-list)))))
; Basic block manipulation procedures:
(define (make-bb label-instr bbs)
(let ((bb (vector
label-instr ; 0 - LABEL instr
(queue-empty) ; 1 - sequence of non-branching instrs
'() ; 2 - branch instruction
'() ; 3 - basic blocks referenced by this block
'()))) ; 4 - basic blocks which jump to this block
; (both filled in by 'bbs-purify!')
(queue-put! (vector-ref bbs 1) bb)
bb))
(define (bb-lbl-num bb) (LABEL-lbl-num (vector-ref bb 0)))
(define (bb-label-type bb) (LABEL-type (vector-ref bb 0)))
(define (bb-label-instr bb) (vector-ref bb 0))
(define (bb-label-instr-set! bb l) (vector-set! bb 0 l))
(define (bb-non-branch-instrs bb) (queue->list (vector-ref bb 1)))
(define (bb-non-branch-instrs-set! bb l) (vector-set! bb 1 (list->queue l)))
(define (bb-branch-instr bb) (vector-ref bb 2))
(define (bb-branch-instr-set! bb b) (vector-set! bb 2 b))
(define (bb-references bb) (vector-ref bb 3))
(define (bb-references-set! bb l) (vector-set! bb 3 l))
(define (bb-precedents bb) (vector-ref bb 4))
(define (bb-precedents-set! bb l) (vector-set! bb 4 l))
(define (bb-entry-frame-size bb)
(frame-size (pvm-instr-frame (bb-label-instr bb))))
(define (bb-exit-frame-size bb)
(frame-size (pvm-instr-frame (bb-branch-instr bb))))
(define (bb-slots-gained bb)
(- (bb-exit-frame-size bb) (bb-entry-frame-size bb)))
(define (bb-put-non-branch! bb pvm-instr)
(queue-put! (vector-ref bb 1) pvm-instr))
(define (bb-put-branch! bb pvm-instr)
(vector-set! bb 2 pvm-instr))
(define (bb-add-reference! bb ref)
(if (not (memq ref (vector-ref bb 3)))
(vector-set! bb 3 (cons ref (vector-ref bb 3)))))
(define (bb-add-precedent! bb prec)
(if (not (memq prec (vector-ref bb 4)))
(vector-set! bb 4 (cons prec (vector-ref bb 4)))))
; Virtual machine instruction representation:
(define (pvm-instr-type pvm-instr) (vector-ref pvm-instr 0))
(define (pvm-instr-frame pvm-instr) (vector-ref pvm-instr 1))
(define (pvm-instr-comment pvm-instr) (vector-ref pvm-instr 2))
(define (make-LABEL-SIMP lbl-num frame comment)
(vector 'LABEL frame comment lbl-num 'SIMP))
(define (make-LABEL-TASK lbl-num method frame comment)
(vector 'LABEL frame comment lbl-num 'TASK method))
(define (make-LABEL-PROC lbl-num nb-parms min rest? closed? frame comment)
(vector 'LABEL frame comment lbl-num 'PROC nb-parms min rest? closed?))
(define (make-LABEL-RETURN lbl-num task-method frame comment)
(vector 'LABEL frame comment lbl-num 'RETURN task-method))
(define (LABEL-lbl-num pvm-instr) (vector-ref pvm-instr 3))
(define (LABEL-type pvm-instr) (vector-ref pvm-instr 4))
(define (LABEL-TASK-method pvm-instr) (vector-ref pvm-instr 5))
(define (LABEL-PROC-nb-parms pvm-instr) (vector-ref pvm-instr 5))
(define (LABEL-PROC-min pvm-instr) (vector-ref pvm-instr 6))
(define (LABEL-PROC-rest? pvm-instr) (vector-ref pvm-instr 7))
(define (LABEL-PROC-closed? pvm-instr) (vector-ref pvm-instr 8))
(define (LABEL-RETURN-task-method pvm-instr) (vector-ref pvm-instr 5))
(define (make-APPLY prim opnds loc frame comment)
(vector 'APPLY frame comment prim opnds loc))
(define (APPLY-prim pvm-instr) (vector-ref pvm-instr 3))
(define (APPLY-opnds pvm-instr) (vector-ref pvm-instr 4))
(define (APPLY-loc pvm-instr) (vector-ref pvm-instr 5))
(define (make-COPY opnd loc frame comment)
(vector 'COPY frame comment opnd loc))
(define (COPY-opnd pvm-instr) (vector-ref pvm-instr 3))
(define (COPY-loc pvm-instr) (vector-ref pvm-instr 4))
(define (make-MAKE_CLOSURES parms frame comment)
(vector 'MAKE_CLOSURES frame comment parms))
(define (MAKE_CLOSURES-parms pvm-instr) (vector-ref pvm-instr 3))
(define (make-closure-parms loc lbl opnds)
(vector loc lbl opnds))
(define (closure-parms-loc x) (vector-ref x 0))
(define (closure-parms-lbl x) (vector-ref x 1))
(define (closure-parms-opnds x) (vector-ref x 2))
(define (make-COND test opnds true false intr-check? frame comment)
(vector 'COND frame comment test opnds true false intr-check?))
(define (COND-test pvm-instr) (vector-ref pvm-instr 3))
(define (COND-opnds pvm-instr) (vector-ref pvm-instr 4))
(define (COND-true pvm-instr) (vector-ref pvm-instr 5))
(define (COND-false pvm-instr) (vector-ref pvm-instr 6))
(define (COND-intr-check? pvm-instr) (vector-ref pvm-instr 7))
(define (make-JUMP opnd nb-args intr-check? frame comment)
(vector 'JUMP frame comment opnd nb-args intr-check?))
(define (JUMP-opnd pvm-instr) (vector-ref pvm-instr 3))
(define (JUMP-nb-args pvm-instr) (vector-ref pvm-instr 4))
(define (JUMP-intr-check? pvm-instr) (vector-ref pvm-instr 5))
(define (first-class-JUMP? pvm-instr) (JUMP-nb-args pvm-instr))
(define (make-comment)
(cons 'COMMENT '()))
(define (comment-put! comment name val)
(set-cdr! comment (cons (cons name val) (cdr comment))))
(define (comment-get comment name)
(and comment
(let ((x (assq name (cdr comment))))
(if x (cdr x) #f))))
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;
; 'Purification' of basic block sets:
; ----------------------------------
; This step removes unreachable basic blocks (i.e. dead code), duplicate
; basic blocks (i.e. common code) and jump cascades from a basic block set.
; It also orders the basic blocks so that the destination of a branch is put
; (if possible) right after the branch instruction. The 'references' and
; 'precedents' fields of each basic block are also filled in through the
; process. The first basic block of a 'purified' basic block set is always
; the entry point.
(define (bbs-purify! bbs)
(let loop () ; iterate until no more code to remove
(bbs-remove-jump-cascades! bbs)
(bbs-remove-dead-code! bbs)
(if pvm-opts?
(if (bbs-remove-common-code! bbs) (loop) (bbs-order! bbs))
(bbs-bb-queue-set! bbs
(list->queue
(sort-list (queue->list (bbs-bb-queue bbs))
(lambda (x y) (< (bb-lbl-num x) (bb-lbl-num y)))))))))
(define pvm-opts? #f)
(set! pvm-opts? #t)
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; Step 1, Jump cascade removal:
(define (bbs-remove-jump-cascades! bbs)
(define (empty-bb? bb)
(and (eq? (bb-label-type bb) 'SIMP) ; simple label and
(null? (bb-non-branch-instrs bb)))) ; no non-branching instrs
(define (jump-lbl? branch)
(let ((opnd (JUMP-opnd branch)))
(if (lbl? opnd) (lbl-num opnd) #f)))
(define (jump-to-non-entry-lbl? branch)
(and (eq? (pvm-instr-type branch) 'JUMP)
(not (first-class-JUMP? branch)) ; not a jump to an entry label
(jump-lbl? branch)))
(define (jump-cascade-to lbl-num fs intr-check? seen thunk)
(if (memq lbl-num seen) ; infinite loop?
(thunk lbl-num fs intr-check?)
(let ((bb (lbl-num->bb lbl-num bbs)))
(if (and (empty-bb? bb) (<= (bb-slots-gained bb) 0))
(let ((jump-lbl-num
(jump-to-non-entry-lbl? (bb-branch-instr bb))))
(if jump-lbl-num
(jump-cascade-to
jump-lbl-num
(+ fs (bb-slots-gained bb))
(or intr-check? (JUMP-intr-check? (bb-branch-instr bb)))
(cons lbl-num seen)
thunk)
(thunk lbl-num fs intr-check?)))
(thunk lbl-num fs intr-check?)))))
(define (equiv-lbl lbl-num seen)
(if (memq lbl-num seen) ; infinite loop?
lbl-num
(let ((bb (lbl-num->bb lbl-num bbs)))
(if (empty-bb? bb)
(let ((jump-lbl-num
(jump-to-non-entry-lbl? (bb-branch-instr bb))))
(if (and jump-lbl-num
(not (JUMP-intr-check? (bb-branch-instr bb)))
(= (bb-slots-gained bb) 0))
(equiv-lbl jump-lbl-num (cons lbl-num seen))
lbl-num))
lbl-num))))
(define (remove-cascade! bb)
(let ((branch (bb-branch-instr bb)))
(case (pvm-instr-type branch)
((COND)
(bb-put-branch! bb ; branch is a COND
(make-COND (COND-test branch)
(COND-opnds branch)
(equiv-lbl (COND-true branch) '())
(equiv-lbl (COND-false branch) '())
(COND-intr-check? branch)
(pvm-instr-frame branch)
(pvm-instr-comment branch))))
((JUMP) ; branch is a JUMP
(if (not (first-class-JUMP? branch)) ; but not to an entry label
(let ((dest-lbl-num (jump-lbl? branch)))
(if dest-lbl-num
(jump-cascade-to
dest-lbl-num
(frame-size (pvm-instr-frame branch))
(JUMP-intr-check? branch)
'()
(lambda (lbl-num fs intr-check?)
(let* ((dest-bb (lbl-num->bb lbl-num bbs))
(last-branch (bb-branch-instr dest-bb)))
(if (and (empty-bb? dest-bb)
(or (not intr-check?)
put-intr-check-on-COND?
(not (eq? (pvm-instr-type last-branch) 'COND))))
(let* ((new-fs (+ fs (bb-slots-gained dest-bb)))
(new-frame (frame-truncate
(pvm-instr-frame branch)
new-fs)))
(define (adjust-opnd opnd)
(cond ((stk? opnd)
(set-pot-fut
(make-stk
(+ (- fs (bb-entry-frame-size dest-bb))
(stk-num opnd)))
(pot-fut? opnd)))
((clo? opnd)
(set-pot-fut
(make-clo (adjust-opnd (clo-base opnd))
(clo-index opnd))
(pot-fut? opnd)))
(else
opnd)))
(case (pvm-instr-type last-branch)
((COND)
(bb-put-branch! bb
(make-COND (COND-test last-branch)
(map adjust-opnd (COND-opnds last-branch))
(equiv-lbl (COND-true last-branch) '())
(equiv-lbl (COND-false last-branch) '())
(or intr-check?
(COND-intr-check? last-branch))
new-frame
(pvm-instr-comment last-branch))))
((JUMP)
(bb-put-branch! bb
(make-JUMP (adjust-opnd (JUMP-opnd last-branch))
(JUMP-nb-args last-branch)
(or intr-check?
(JUMP-intr-check? last-branch))
new-frame
(pvm-instr-comment last-branch))))
(else
(compiler-internal-error
"bbs-remove-jump-cascades!, unknown branch type"))))
(bb-put-branch! bb
(make-JUMP (make-lbl lbl-num)
(JUMP-nb-args branch)
(or intr-check?
(JUMP-intr-check? branch))
(frame-truncate
(pvm-instr-frame branch)
fs)
(pvm-instr-comment branch)))))))))))
(else
(compiler-internal-error
"bbs-remove-jump-cascades!, unknown branch type")))))
(for-each remove-cascade!
(queue->list (bbs-bb-queue bbs))))
(define put-intr-check-on-COND? #f)
(set! put-intr-check-on-COND? #t)
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; Step 2, Dead code removal:
(define (bbs-remove-dead-code! bbs)
(let ((new-bb-queue (queue-empty))
(scan-queue (queue-empty)))
(define (reachable ref bb)
(if bb (bb-add-reference! bb ref))
(if (not (memq ref (queue->list new-bb-queue)))
(begin
(bb-references-set! ref '())
(bb-precedents-set! ref '())
(queue-put! new-bb-queue ref)
(queue-put! scan-queue ref))))
(define (direct-jump to-bb from-bb)
(reachable to-bb from-bb)
(bb-add-precedent! to-bb from-bb))
(define (scan-instr pvm-instr bb)
(define (scan-opnd pvm-opnd)
(cond ((lbl? pvm-opnd)
(reachable (lbl-num->bb (lbl-num pvm-opnd) bbs) bb))
((clo? pvm-opnd)
(scan-opnd (clo-base pvm-opnd)))))
(case (pvm-instr-type pvm-instr)
((LABEL)
'())
((APPLY)
(for-each scan-opnd (APPLY-opnds pvm-instr))
(if (APPLY-loc pvm-instr)
(scan-opnd (APPLY-loc pvm-instr))))
((COPY)
(scan-opnd (COPY-opnd pvm-instr))
(scan-opnd (COPY-loc pvm-instr)))
((MAKE_CLOSURES)
(for-each (lambda (parm)
(reachable (lbl-num->bb (closure-parms-lbl parm) bbs) bb)
(scan-opnd (closure-parms-loc parm))
(for-each scan-opnd (closure-parms-opnds parm)))
(MAKE_CLOSURES-parms pvm-instr)))
((COND)
(for-each scan-opnd (COND-opnds pvm-instr))
(direct-jump (lbl-num->bb (COND-true pvm-instr) bbs) bb)
(direct-jump (lbl-num->bb (COND-false pvm-instr) bbs) bb))
((JUMP)
(let ((opnd (JUMP-opnd pvm-instr)))
(if (lbl? opnd)
(direct-jump (lbl-num->bb (lbl-num opnd) bbs) bb)
(scan-opnd (JUMP-opnd pvm-instr)))))
(else
(compiler-internal-error
"bbs-remove-dead-code!, unknown PVM instruction type"))))
(reachable (lbl-num->bb (bbs-entry-lbl-num bbs) bbs) #f)
(let loop ()
(if (not (queue-empty? scan-queue))
(let ((bb (queue-get! scan-queue)))
(begin
(scan-instr (bb-label-instr bb) bb)
(for-each (lambda (pvm-instr) (scan-instr pvm-instr bb))
(bb-non-branch-instrs bb))
(scan-instr (bb-branch-instr bb) bb)
(loop)))))
(bbs-bb-queue-set! bbs new-bb-queue)))
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; Step 3, Common code removal:
(define (bbs-remove-common-code! bbs)
(let* ((bb-list (queue->list (bbs-bb-queue bbs)))
(n (length bb-list))
(hash-table-length
(cond ((< n 50) 43) ; select reasonable size for hash table
((< n 500) 403)
(else 4003)))
(hash-table (make-vector hash-table-length '()))
(prim-table '())
(block-map '())
(changed? #f))
(define (hash-prim prim)
(let ((n (length prim-table))
(i (pos-in-list prim prim-table)))
(if i
(- n i)
(begin
(set! prim-table (cons prim prim-table))
(+ n 1)))))
(define (hash-opnds l) ; this assumes that operands are encoded with nbs
(let loop ((l l) (n 0))
(if (pair? l)
(loop (cdr l)
(let ((x (car l)))
(if (lbl? x) n (modulo (+ (* n 10000) x) hash-table-length))))
n)))
(define (hash-bb bb) ; compute hash address for a basic block
(let ((branch (bb-branch-instr bb)))
(modulo
(case (pvm-instr-type branch)
((COND)
(+ (hash-opnds (COND-opnds branch))
(* 10 (hash-prim (COND-test branch)))
(* 100 (frame-size (pvm-instr-frame branch)))))
((JUMP)
(+ (hash-opnds (list (JUMP-opnd branch)))
(* 10 (or (JUMP-nb-args branch) -1))
(* 100 (frame-size (pvm-instr-frame branch)))))
(else
0))
hash-table-length)))
(define (replacement-lbl-num lbl)
(let ((x (assv lbl block-map)))
(if x (cdr x) lbl)))
(define (fix-map! bb1 bb2) ; bb1 should be replaced by bb2 in the block-map
(let loop ((l block-map))
(if (pair? l)
(let ((x (car l)))
(if (= bb1 (cdr x)) (set-cdr! x bb2))
(loop (cdr l))))))
(define (enter-bb! bb) ; enter a basic block in the hash table
(let ((h (hash-bb bb)))
(vector-set! hash-table h
(add-bb bb (vector-ref hash-table h)))))
(define (add-bb bb l) ; add basic block 'bb' to list of basic blocks
(if (pair? l)
(let ((bb* (car l))) ; pick next basic block in list
(set! block-map ; for now, assume that 'bb' = 'bb*'
(cons (cons (bb-lbl-num bb) (bb-lbl-num bb*))
block-map))
(if (eqv-bb? bb bb*) ; are they the same?
(begin
(fix-map! (bb-lbl-num bb) (bb-lbl-num bb*)) ; record the equivalence
(set! changed? #t)
l)
(begin
(set! block-map (cdr block-map)) ; they are not the same!
(if (eqv-pvm-instr? (bb-branch-instr bb) (bb-branch-instr bb*))
(extract-common-tail bb bb* ; check if tail is the same
(lambda (head head* tail)
(if (null? tail) ; common tail long enough?
(cons bb* (add-bb bb (cdr l))) ; no, so try rest of list
(let* ((lbl (bbs-new-lbl! bbs)) ; create bb for common tail
(branch (bb-branch-instr bb))
(fs** (need-pvm-instrs tail branch))
(frame (frame-truncate
(pvm-instr-frame
(if (null? head)
(bb-label-instr bb)
(car head)))
fs**))
(bb** (make-bb (make-LABEL-SIMP lbl frame #f) bbs)))
(bb-non-branch-instrs-set! bb** tail)
(bb-branch-instr-set! bb** branch)
(bb-non-branch-instrs-set! bb* (reverse head*))
(bb-branch-instr-set! bb*
(make-JUMP (make-lbl lbl) #f #f frame #f))
(bb-non-branch-instrs-set! bb (reverse head))
(bb-branch-instr-set! bb
(make-JUMP (make-lbl lbl) #f #f frame #f))
(set! changed? #t)
(cons bb (cons bb* (add-bb bb** (cdr l))))))))
(cons bb* (add-bb bb (cdr l)))))))
(list bb)))
(define (extract-common-tail bb1 bb2 cont)
(let loop ((l1 (reverse (bb-non-branch-instrs bb1)))
(l2 (reverse (bb-non-branch-instrs bb2)))
(tail '()))
(if (and (pair? l1) (pair? l2))
(let ((i1 (car l1))
(i2 (car l2)))
(if (eqv-pvm-instr? i1 i2)
(loop (cdr l1) (cdr l2) (cons i1 tail))
(cont l1 l2 tail)))
(cont l1 l2 tail))))
(define (eqv-bb? bb1 bb2)
(let ((bb1-non-branch (bb-non-branch-instrs bb1))
(bb2-non-branch (bb-non-branch-instrs bb2)))
(and (= (length bb1-non-branch) (length bb2-non-branch))
(eqv-pvm-instr? (bb-label-instr bb1) (bb-label-instr bb2))
(eqv-pvm-instr? (bb-branch-instr bb1) (bb-branch-instr bb2))
(eqv-list? eqv-pvm-instr? bb1-non-branch bb2-non-branch))))
(define (eqv-list? pred? l1 l2)
(if (pair? l1)
(and (pair? l2)
(pred? (car l1) (car l2))
(eqv-list? pred? (cdr l1) (cdr l2)))
(not (pair? l2))))
(define (eqv-lbl-num? lbl1 lbl2)
(= (replacement-lbl-num lbl1)
(replacement-lbl-num lbl2)))
(define (eqv-pvm-opnd? opnd1 opnd2)
(if (not opnd1)
(not opnd2)
(and opnd2
(eq? (pot-fut? opnd1) (pot-fut? opnd2))
(cond ((lbl? opnd1)
(and (lbl? opnd2)
(eqv-lbl-num? (lbl-num opnd1) (lbl-num opnd2))))
((clo? opnd1)
(and (clo? opnd2)
(= (clo-index opnd1) (clo-index opnd2))
(eqv-pvm-opnd? (clo-base opnd1)
(clo-base opnd2))))
(else
(eqv? opnd1 opnd2))))))
(define (eqv-pvm-instr? instr1 instr2)
(define (eqv-closure-parms? p1 p2)
(and (eqv-pvm-opnd? (closure-parms-loc p1)
(closure-parms-loc p2))
(eqv-lbl-num? (closure-parms-lbl p1)
(closure-parms-lbl p2))
(eqv-list? eqv-pvm-opnd?
(closure-parms-opnds p1)
(closure-parms-opnds p2))))
(let ((type1 (pvm-instr-type instr1))
(type2 (pvm-instr-type instr2)))
(and (eq? type1 type2)
(frame-eq? (pvm-instr-frame instr1) (pvm-instr-frame instr2))
(case type1
((LABEL)
(let ((ltype1 (LABEL-type instr1))
(ltype2 (LABEL-type instr2)))
(and (eq? ltype1 ltype2)
(case ltype1
((SIMP)
#t)
((TASK)
(eq? (LABEL-TASK-method instr1)
(LABEL-TASK-method instr2)))
((RETURN)
(eq? (LABEL-RETURN-task-method instr1)
(LABEL-RETURN-task-method instr2)))
((PROC)
(and (= (LABEL-PROC-min instr1)
(LABEL-PROC-min instr2))
(= (LABEL-PROC-nb-parms instr1)
(LABEL-PROC-nb-parms instr2))
(eq? (LABEL-PROC-rest? instr1)
(LABEL-PROC-rest? instr2))
(eq? (LABEL-PROC-closed? instr1)
(LABEL-PROC-closed? instr2))))
(else
(compiler-internal-error
"eqv-pvm-instr?, unknown label type"))))))
((APPLY)
(and (eq? (APPLY-prim instr1) (APPLY-prim instr2))
(eqv-list? eqv-pvm-opnd?
(APPLY-opnds instr1)
(APPLY-opnds instr2))
(eqv-pvm-opnd? (APPLY-loc instr1)
(APPLY-loc instr2))))
((COPY)
(and (eqv-pvm-opnd? (COPY-opnd instr1)
(COPY-opnd instr2))
(eqv-pvm-opnd? (COPY-loc instr1)
(COPY-loc instr2))))
((MAKE_CLOSURES)
(eqv-list? eqv-closure-parms?
(MAKE_CLOSURES-parms instr1)
(MAKE_CLOSURES-parms instr2)))
((COND)
(and (eq? (COND-test instr1)
(COND-test instr2))
(eqv-list? eqv-pvm-opnd?
(COND-opnds instr1)
(COND-opnds instr2))
(eqv-lbl-num? (COND-true instr1)
(COND-true instr2))
(eqv-lbl-num? (COND-false instr1)
(COND-false instr2))
(eq? (COND-intr-check? instr1)
(COND-intr-check? instr2))))
((JUMP)
(and (eqv-pvm-opnd? (JUMP-opnd instr1)
(JUMP-opnd instr2))
(eqv? (JUMP-nb-args instr1)
(JUMP-nb-args instr2))
(eq? (JUMP-intr-check? instr1)
(JUMP-intr-check? instr2))))
(else
(compiler-internal-error
"eqv-pvm-instr?, unknown 'pvm-instr':" instr1))))))
(define (update-pvm-opnd opnd)
(if opnd
(cond ((lbl? opnd)
(set-pot-fut
(make-lbl (replacement-lbl-num (lbl-num opnd)))
(pot-fut? opnd)))
((clo? opnd)
(set-pot-fut
(make-clo (update-pvm-opnd (clo-base opnd)) (clo-index opnd))
(pot-fut? opnd)))
(else
opnd))
opnd))
(define (update-pvm-instr instr)
(define (update-closure-parms p)
(make-closure-parms
(update-pvm-opnd (closure-parms-loc p))
(replacement-lbl-num (closure-parms-lbl p))
(map update-pvm-opnd (closure-parms-opnds p))))
(case (pvm-instr-type instr)
((LABEL)
(case (LABEL-type instr)
((SIMP)
(make-LABEL-SIMP (LABEL-lbl-num instr)
(pvm-instr-frame instr)
(pvm-instr-comment instr)))
((TASK)
(make-LABEL-TASK (LABEL-lbl-num instr)
(LABEL-TASK-method instr)
(pvm-instr-frame instr)
(pvm-instr-comment instr)))
((PROC)
(make-LABEL-PROC (LABEL-lbl-num instr)
(LABEL-PROC-nb-parms instr)
(LABEL-PROC-min instr)
(LABEL-PROC-rest? instr)
(LABEL-PROC-closed? instr)
(pvm-instr-frame instr)
(pvm-instr-comment instr)))
((RETURN)
(make-LABEL-RETURN (LABEL-lbl-num instr)
(LABEL-RETURN-task-method instr)
(pvm-instr-frame instr)
(pvm-instr-comment instr)))
(else
(compiler-internal-error
"update-pvm-instr, unknown label type"))))
((APPLY)
(make-APPLY (APPLY-prim instr)
(map update-pvm-opnd (APPLY-opnds instr))
(update-pvm-opnd (APPLY-loc instr))
(pvm-instr-frame instr)
(pvm-instr-comment instr)))
((COPY)
(make-COPY (update-pvm-opnd (COPY-opnd instr))
(update-pvm-opnd (COPY-loc instr))
(pvm-instr-frame instr)
(pvm-instr-comment instr)))
((MAKE_CLOSURES)
(make-MAKE_CLOSURES
(map update-closure-parms (MAKE_CLOSURES-parms instr))
(pvm-instr-frame instr)
(pvm-instr-comment instr)))
((COND)
(make-COND (COND-test instr)
(map update-pvm-opnd (COND-opnds instr))
(replacement-lbl-num (COND-true instr))
(replacement-lbl-num (COND-false instr))
(COND-intr-check? instr)
(pvm-instr-frame instr)
(pvm-instr-comment instr)))
((JUMP)
(make-JUMP (update-pvm-opnd (JUMP-opnd instr))
(JUMP-nb-args instr)
(JUMP-intr-check? instr)
(pvm-instr-frame instr)
(pvm-instr-comment instr)))
(else
(compiler-internal-error
"update-pvm-instr, unknown 'instr':" instr))))
(define (update-bb! bb)
(bb-label-instr-set! bb
(update-pvm-instr (bb-label-instr bb)))
(bb-non-branch-instrs-set! bb
(map update-pvm-instr (bb-non-branch-instrs bb)))
(bb-branch-instr-set! bb
(update-pvm-instr (bb-branch-instr bb))))
; Fill hash table, remove equivalent basic blocks and common tails
(for-each enter-bb! bb-list)
; Reconstruct bbs
(bbs-entry-lbl-num-set! bbs
(replacement-lbl-num (bbs-entry-lbl-num bbs)))
(let loop ((i 0) (result '()))
(if (< i hash-table-length)
(let ((bb-kept (vector-ref hash-table i)))
(for-each update-bb! bb-kept)
(loop (+ i 1) (append bb-kept result)))
(bbs-bb-queue-set! bbs (list->queue result))))
changed?))
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; Step 4, Basic block set ordering:
(define (bbs-order! bbs)
(let ((new-bb-queue (queue-empty))
(left-to-schedule (queue->list (bbs-bb-queue bbs))))
(define (remove x l)
(if (eq? (car l) x)
(cdr l)
(cons (car l) (remove x (cdr l)))))
; update list of basic blocks not yet scheduled
(define (remove-bb! bb)
(set! left-to-schedule (remove bb left-to-schedule))
bb)
; return a basic block which ends with a branch to 'bb' (and that is
; still in 'left-to-schedule') or #f if there aren't any
(define (prec-bb bb)
(let loop ((l (bb-precedents bb)) (best #f) (best-fs #f))
(if (null? l)
best
(let* ((x (car l))
(x-fs (bb-exit-frame-size x)))
(if (and (memq x left-to-schedule)
(or (not best) (< x-fs best-fs)))
(loop (cdr l) x x-fs)
(loop (cdr l) best best-fs))))))
; return the basic block which 'bb' jumps to (and that is still in
; 'left-to-schedule') or #f if there aren't any
(define (succ-bb bb)
(define (branches-to-lbl? bb)
(let ((branch (bb-branch-instr bb)))
(case (pvm-instr-type branch)
((COND) #t)
((JUMP) (lbl? (JUMP-opnd branch)))
(else
(compiler-internal-error
"bbs-order!, unknown branch type")))))
(define (best-succ bb1 bb2) ; heuristic that determines which
(if (branches-to-lbl? bb1) ; bb is most frequently executed
bb1
(if (branches-to-lbl? bb2)
bb2
(if (< (bb-exit-frame-size bb1)
(bb-exit-frame-size bb2))
bb2
bb1))))
(let ((branch (bb-branch-instr bb)))
(case (pvm-instr-type branch)
((COND)
(let* ((true-bb (lbl-num->bb (COND-true branch) bbs))
(true-bb* (and (memq true-bb left-to-schedule)
true-bb))
(false-bb (lbl-num->bb (COND-false branch) bbs))
(false-bb* (and (memq false-bb left-to-schedule)
false-bb)))
(if (and true-bb* false-bb*)
(best-succ true-bb* false-bb*)
(or true-bb* false-bb*))))
((JUMP)
(let ((opnd (JUMP-opnd branch)))
(and (lbl? opnd)
(let ((bb (lbl-num->bb (lbl-num opnd) bbs)))
(and (memq bb left-to-schedule) bb)))))
(else
(compiler-internal-error
"bbs-order!, unknown branch type")))))
; schedule a given basic block 'bb' with it's predecessors and
; successors.
(define (schedule-from bb)
(queue-put! new-bb-queue bb)
(let ((x (succ-bb bb)))
(if x
(begin
(schedule-around (remove-bb! x))
(let ((y (succ-bb bb)))
(if y
(schedule-around (remove-bb! y)))))))
(schedule-refs bb))
(define (schedule-around bb)
(let ((x (prec-bb bb)))
(if x
(let ((bb-list (schedule-back (remove-bb! x) '())))
(queue-put! new-bb-queue x)
(schedule-forw bb)
(for-each schedule-refs bb-list))
(schedule-from bb))))
(define (schedule-back bb bb-list)
(let ((bb-list* (cons bb bb-list))
(x (prec-bb bb)))
(if x
(let ((bb-list (schedule-back (remove-bb! x) bb-list*)))
(queue-put! new-bb-queue x)
bb-list)
bb-list*)))
(define (schedule-forw bb)
(queue-put! new-bb-queue bb)
(let ((x (succ-bb bb)))
(if x
(begin
(schedule-forw (remove-bb! x))
(let ((y (succ-bb bb)))
(if y
(schedule-around (remove-bb! y)))))))
(schedule-refs bb))
(define (schedule-refs bb)
(for-each
(lambda (x)
(if (memq x left-to-schedule) (schedule-around (remove-bb! x))))
(bb-references bb)))
(schedule-from (remove-bb! (lbl-num->bb (bbs-entry-lbl-num bbs) bbs)))
(bbs-bb-queue-set! bbs new-bb-queue)))
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;
; Sequentialization of a basic block set:
; --------------------------------------
; The procedure 'bbs->code-list' transforms a 'purified' basic block set
; into a sequence of virtual machine instructions. Each element of the
; resulting list is a 'code' object that contains a PVM instruction,
; a pointer to the basic block it came from and a `slots needed' index
; that specifies the minimum number of slots that have to be kept (relative
; to the start of the frame) after the instruction is executed.
; The procedure does a few optimizations: fall-through JUMP removal and
; deletion of unnecessary LABELs. The first element of the code list is the
; entry label for the piece of code.
(define (make-code bb pvm-instr sn) (vector bb pvm-instr sn))
(define (code-bb code) (vector-ref code 0))
(define (code-pvm-instr code) (vector-ref code 1))
(define (code-slots-needed code) (vector-ref code 2))
(define (code-slots-needed-set! code n) (vector-set! code 2 n))
(define (bbs->code-list bbs)
(let ((code-list (linearize bbs)))
(setup-slots-needed! code-list)
code-list))
(define (linearize bbs) ; turn bbs into list and remove LABELs & JUMPs
(let ((code-queue (queue-empty)))
(define (put-bb prec-bb pres-bb next-bb label-needed?)
(define (put-instr pvm-instr)
(queue-put! code-queue (make-code pres-bb pvm-instr #f)))
(if label-needed?
(put-instr (bb-label-instr pres-bb))) ; put label only if truly needed
(for-each put-instr (bb-non-branch-instrs pres-bb)) ; put non-branching instrs
(let ((branch (bb-branch-instr pres-bb)))
(case (pvm-instr-type branch)
((COND)
(put-instr branch)
#t)
((JUMP)
(let ((opnd (JUMP-opnd branch)))
(if (or (not next-bb) ; remove JUMP if it falls through?
(not (lbl? opnd))
(not (= (lbl-num opnd) (bb-lbl-num next-bb)))
(not (= (length (bb-precedents next-bb)) 1))
(not (eq? (bb-label-type next-bb) 'SIMP)) ; not a simple label
(not (= (frame-size (pvm-instr-frame branch))
(bb-entry-frame-size next-bb)))
(JUMP-intr-check? branch))
(begin (put-instr branch) #t)
#f)))
(else
(compiler-internal-error
"linearize, unknown branch type")))))
(let loop ((l (queue->list (bbs-bb-queue bbs)))
(prev-bb #f)
(label-needed? #t))
(if (not (null? l))
(let ((pres-bb (car l)))
(loop (cdr l)
pres-bb
(put-bb prev-bb
pres-bb
(if (null? (cdr l)) #f (cadr l))
label-needed?)))))
(queue->list code-queue)))
(define (setup-slots-needed! code-list) ; setup `slots-needed' field
(if (null? code-list)
#f
(let* ((code (car code-list))
(pvm-instr (code-pvm-instr code))
(sn-rest (setup-slots-needed! (cdr code-list))))
(case (pvm-instr-type pvm-instr)
((LABEL)
(if (> sn-rest (frame-size (pvm-instr-frame pvm-instr)))
(compiler-internal-error
"setup-slots-needed!, incoherent slots needed for LABEL"))
(code-slots-needed-set! code sn-rest)
#f)
((COND JUMP)
(let ((sn (frame-size (pvm-instr-frame pvm-instr))))
(code-slots-needed-set! code sn)
(need-pvm-instr pvm-instr sn)))
(else
(code-slots-needed-set! code sn-rest)
(need-pvm-instr pvm-instr sn-rest))))))
(define (need-pvm-instrs non-branch branch)
(if (pair? non-branch)
(need-pvm-instr (car non-branch)
(need-pvm-instrs (cdr non-branch) branch))
(need-pvm-instr branch (frame-size (pvm-instr-frame branch)))))
(define (need-pvm-instr pvm-instr sn-rest)
(case (pvm-instr-type pvm-instr)
((LABEL)
sn-rest)
((APPLY)
(let ((loc (APPLY-loc pvm-instr)))
(need-pvm-opnds (APPLY-opnds pvm-instr)
(need-pvm-loc-opnd loc
(need-pvm-loc loc sn-rest)))))
((COPY)
(let ((loc (COPY-loc pvm-instr)))
(need-pvm-opnd (COPY-opnd pvm-instr)
(need-pvm-loc-opnd loc
(need-pvm-loc loc sn-rest)))))
((MAKE_CLOSURES)
(let ((parms (MAKE_CLOSURES-parms pvm-instr)))
(define (need-parms-opnds p)
(if (null? p)
sn-rest
(need-pvm-opnds (closure-parms-opnds (car p))
(need-parms-opnds (cdr p)))))
(define (need-parms-loc p)
(if (null? p)
(need-parms-opnds parms)
(let ((loc (closure-parms-loc (car p))))
(need-pvm-loc-opnd loc
(need-pvm-loc loc (need-parms-loc (cdr p)))))))
(need-parms-loc parms)))
((COND)
(need-pvm-opnds (COND-opnds pvm-instr) sn-rest))
((JUMP)
(need-pvm-opnd (JUMP-opnd pvm-instr) sn-rest))
(else
(compiler-internal-error
"need-pvm-instr, unknown 'pvm-instr':" pvm-instr))))
(define (need-pvm-loc loc sn-rest)
(if (and loc (stk? loc) (>= (stk-num loc) sn-rest))
(- (stk-num loc) 1)
sn-rest))
(define (need-pvm-loc-opnd pvm-loc slots-needed)
(if (and pvm-loc (clo? pvm-loc))
(need-pvm-opnd (clo-base pvm-loc) slots-needed)
slots-needed))
(define (need-pvm-opnd pvm-opnd slots-needed)
(cond ((stk? pvm-opnd)
(max (stk-num pvm-opnd) slots-needed))
((clo? pvm-opnd)
(need-pvm-opnd (clo-base pvm-opnd) slots-needed))
(else
slots-needed)))
(define (need-pvm-opnds pvm-opnds slots-needed)
(if (null? pvm-opnds)
slots-needed
(need-pvm-opnd (car pvm-opnds)
(need-pvm-opnds (cdr pvm-opnds) slots-needed))))
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;
; Basic block writing:
; -------------------
(define (write-bb bb port)
(write-pvm-instr (bb-label-instr bb) port)
(display " [precedents=" port)
(write (map bb-lbl-num (bb-precedents bb)) port)
(display "]" port)
(newline port)
(for-each (lambda (x) (write-pvm-instr x port) (newline port))
(bb-non-branch-instrs bb))
(write-pvm-instr (bb-branch-instr bb) port))
(define (write-bbs bbs port)
(for-each (lambda (bb)
(if (= (bb-lbl-num bb) (bbs-entry-lbl-num bbs))
(begin (display "**** Entry block:" port) (newline port)))
(write-bb bb port)
(newline port))
(queue->list (bbs-bb-queue bbs))))
(define (virtual.dump proc port)
(let ((proc-seen (queue-empty))
(proc-left (queue-empty)))
(define (scan-opnd pvm-opnd)
(cond ((obj? pvm-opnd)
(let ((val (obj-val pvm-opnd)))
(if (and (proc-obj? val)
(proc-obj-code val)
(not (memq val (queue->list proc-seen))))
(begin
(queue-put! proc-seen val)
(queue-put! proc-left val)))))
((clo? pvm-opnd)
(scan-opnd (clo-base pvm-opnd)))))
(define (dump-proc p)
(define (scan-code code)
(let ((pvm-instr (code-pvm-instr code))
(slots-needed (code-slots-needed code)))
(if (> slots-needed 9) (display "[" port) (display "[ " port))
(display slots-needed port)
(display "] " port)
(write-pvm-instr pvm-instr port)
(newline port)
(case (pvm-instr-type pvm-instr)
((APPLY)
(for-each scan-opnd (APPLY-opnds pvm-instr))
(if (APPLY-loc pvm-instr)
(scan-opnd (APPLY-loc pvm-instr))))
((COPY)
(scan-opnd (COPY-opnd pvm-instr))
(scan-opnd (COPY-loc pvm-instr)))
((MAKE_CLOSURES)
(for-each (lambda (parms)
(scan-opnd (closure-parms-loc parms))
(for-each scan-opnd (closure-parms-opnds parms)))
(MAKE_CLOSURES-parms pvm-instr)))
((COND)
(for-each scan-opnd (COND-opnds pvm-instr)))
((JUMP)
(scan-opnd (JUMP-opnd pvm-instr)))
(else
'()))))
(if (proc-obj-primitive? p)
(display "**** #[primitive " port)
(display "**** #[procedure " port))
(display (proc-obj-name p) port)
(display "] =" port)
(newline port)
(for-each scan-code (bbs->code-list (proc-obj-code p)))
(newline port))
(scan-opnd (make-obj proc))
(let loop ()
(if (not (queue-empty? proc-left))
(begin
(dump-proc (queue-get! proc-left))
(loop))))))
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;
; Virtual instruction writing:
; ---------------------------
(define (write-pvm-instr pvm-instr port)
(define (write-closure-parms parms)
(let ((len (write-pvm-opnd (closure-parms-loc parms) port)))
(display ",L" port)
(let ((len (+ len (+ 2 (write-returning-len
(closure-parms-lbl parms)
port)))))
(let loop ((l (closure-parms-opnds parms)) (len len))
(if (pair? l)
(let ((opnd (car l)))
(display "," port)
(loop (cdr l) (+ len (+ (write-pvm-opnd opnd port) 1))))
len)))))
(define (write-upcase str)
(let ((len (string-length str)))
(let loop ((i 0))
(if (< i len)
(begin
(write-char (char-upcase (string-ref str i)) port)
(loop (+ i 1)))
len))))
(define (write-task-method method)
(if method
(begin
(display "," port)
(+ 1 (write-upcase (symbol->string method))))
0))
(define (write-instr pvm-instr)
(case (pvm-instr-type pvm-instr)
((LABEL)
(display "LABEL(L" port)
(let ((len (+ 7 (write-returning-len (LABEL-lbl-num pvm-instr) port))))
(case (LABEL-type pvm-instr)
((SIMP)
(display ",SIMP)" port)
(+ len 6))
((TASK)
(display ",TASK" port)
(let ((len (+ len
(+ 5
(write-task-method
(LABEL-TASK-method pvm-instr))))))
(display ")" port)
(+ len 1)))
((PROC)
(display ",PROC," port)
(let ((len (+ len
(+ 6
(if (not (= (LABEL-PROC-min pvm-instr)
(LABEL-PROC-nb-parms pvm-instr)))
(let ((len (+ len
(write-returning-len
(LABEL-PROC-min pvm-instr)
port))))
(display "-" port)
(+ len 1))
0)))))
(let ((len (+ len
(write-returning-len
(LABEL-PROC-nb-parms pvm-instr)
port))))
(let ((len (+ len
(if (LABEL-PROC-rest? pvm-instr)
(begin (display "..." port) 3)
0))))
(let ((len (+ len
(if (LABEL-PROC-closed? pvm-instr)
(begin (display ",CLOSED" port) 7)
0))))
(display ")" port)
(+ len 1))))))
((RETURN)
(display ",RETURN" port)
(let ((len (+ len
(+ 7
(write-task-method
(LABEL-RETURN-task-method pvm-instr))))))
(display ")" port)
(+ len 1)))
(else
(compiler-internal-error
"write-pvm-instr, unknown label type")))))
((APPLY)
(display " APPLY(" port)
(let ((len (+ 8 (display-returning-len
(proc-obj-name (APPLY-prim pvm-instr))
port))))
(let loop ((l (APPLY-opnds pvm-instr)) (len len))
(if (pair? l)
(let ((opnd (car l)))
(display "," port)
(loop (cdr l) (+ len (+ (write-pvm-opnd opnd port) 1))))
(begin
(display "," port)
(let ((len (+ len
(+ 1
(if (APPLY-loc pvm-instr)
(write-pvm-opnd (APPLY-loc pvm-instr) port)
0)))))
(display ")" port)
(+ len 1)))))))
((COPY)
(display " COPY(" port)
(let ((len (+ 7 (write-pvm-opnd (COPY-opnd pvm-instr) port))))
(display "," port)
(let ((len (+ len (+ 1 (write-pvm-opnd (COPY-loc pvm-instr) port)))))
(display ")" port)
(+ len 1))))
((MAKE_CLOSURES)
(display " MAKE_CLOSURES(" port)
(let ((len (+ 16 (write-closure-parms
(car (MAKE_CLOSURES-parms pvm-instr))))))
(let loop ((l (cdr (MAKE_CLOSURES-parms pvm-instr))) (len len))
(if (pair? l)
(let ((x (car l)))
(display "/" port)
(loop (cdr l) (+ len (+ (write-closure-parms x) 1))))
(begin
(display ")" port)
(+ len 1))))))
((COND)
(display " COND(" port)
(let ((len (+ 7 (display-returning-len
(proc-obj-name (COND-test pvm-instr))
port))))
(let loop ((l (COND-opnds pvm-instr)) (len len))
(if (pair? l)
(let ((opnd (car l)))
(display "," port)
(loop (cdr l) (+ len (+ (write-pvm-opnd opnd port) 1))))
(begin
(display ",L" port)
(let ((len (+ len (+ 2 (write-returning-len
(COND-true pvm-instr)
port)))))
(display ",L" port)
(let ((len (+ len (+ 2 (write-returning-len
(COND-false pvm-instr)
port)))))
(let ((len (+ len (if (COND-intr-check? pvm-instr)
(begin (display ",INTR-CHECK" port) 11)
0))))
(display ")" port)
(+ len 1)))))))))
((JUMP)
(display " JUMP(" port)
(let ((len (+ 7 (write-pvm-opnd (JUMP-opnd pvm-instr) port))))
(let ((len (+ len (if (JUMP-nb-args pvm-instr)
(begin
(display "," port)
(+ 1 (write-returning-len
(JUMP-nb-args pvm-instr)
port)))
0))))
(let ((len (+ len (if (JUMP-intr-check? pvm-instr)
(begin (display ",INTR-CHECK" port) 11)
0))))
(display ")" port)
(+ len 1)))))
(else
(compiler-internal-error
"write-pvm-instr, unknown 'pvm-instr':"
pvm-instr))))
(define (spaces n)
(if (> n 0)
(if (> n 7)
(begin (display " " port) (spaces (- n 8)))
(begin (display " " port) (spaces (- n 1))))))
(let ((len (write-instr pvm-instr)))
(spaces (- 80 len))
(display " " port)
(write-frame (pvm-instr-frame pvm-instr) port))
(let ((x (pvm-instr-comment pvm-instr)))
(if x
(let ((y (comment-get x 'TEXT)))
(if y
(begin
(display " ; " port)
(display y port)))))))
(define (write-frame frame port)
(define (write-var var opnd sep)
(display sep port)
(write-pvm-opnd opnd port)
(if var
(begin
(display "=" port)
(cond ((eq? var closure-env-var)
(write (map (lambda (var) (symbol->string (var-name var)))
(frame-closed frame))
port))
((eq? var ret-var)
(display "RET" port))
((temp-var? var)
(display "TMP" port))
(else
(write (symbol->string (var-name var)) port))))))
(define (live? var)
(let ((live (frame-live frame)))
(or (set-member? var live)
(and (eq? var closure-env-var)
(not (set-empty? (set-intersection
live
(list->set (frame-closed frame)))))))))
(display "{" port)
(let loop1 ((i 1) (l (reverse (frame-slots frame))) (sep ""))
(if (pair? l)
(let ((var (car l)))
(write-var (if (live? var) var #f) (make-stk i) sep)
(loop1 (+ i 1) (cdr l) " "))
(let loop2 ((i 0) (l (frame-regs frame)) (sep sep))
(if (pair? l)
(let ((var (car l)))
(if (live? var)
(begin
(write-var var (make-reg i) sep)
(loop2 (+ i 1) (cdr l) " "))
(loop2 (+ i 1) (cdr l) sep)))
(display "}" port))))))
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;
; Operand writing:
; ---------------
(define (write-pvm-opnd pvm-opnd port)
(define (write-opnd)
(cond ((reg? pvm-opnd)
(display "r" port)
(+ 1 (write-returning-len (reg-num pvm-opnd) port)))
((stk? pvm-opnd)
(display "s" port)
(+ 1 (write-returning-len (stk-num pvm-opnd) port)))
((glo? pvm-opnd)
(write-returning-len (symbol->string (glo-name pvm-opnd)) port))
((clo? pvm-opnd)
(let ((x (write-pvm-opnd (clo-base pvm-opnd) port)))
(display ":" port)
(+ (write-returning-len (clo-index pvm-opnd) port) (+ x 1))))
((lbl? pvm-opnd)
(display "L" port)
(+ (write-returning-len (lbl-num pvm-opnd) port) 1))
((obj? pvm-opnd)
(display "'" port)
(+ (write-pvm-opnd-value (obj-val pvm-opnd) port) 1))
(else
(compiler-internal-error
"write-pvm-opnd, unknown 'pvm-opnd':"
pvm-opnd))))
(if (pot-fut? pvm-opnd)
(begin
(display "?" port)
(+ (write-opnd) 1))
(write-opnd)))
(define (write-pvm-opnd-value val port)
(cond ((false-object? val)
(display "#f" port)
2)
((undef-object? val)
(display "#[undefined]" port)
12)
((proc-obj? val)
(if (proc-obj-primitive? val)
(display "#[primitive " port)
(display "#[procedure " port))
(let ((x (display-returning-len (proc-obj-name val) port)))
(display "]" port)
(+ x 13)))
(else
(write-returning-len val port))))
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(define (virtual.begin!) ; initialize package
(set! *opnd-table* (make-vector opnd-table-size))
(set! *opnd-table-alloc* 0)
'())
(define (virtual.end!) ; finalize package
(set! *opnd-table* '())
'())
;==============================================================================