scsh-0.6/ps-compiler/node/node.scm

545 lines
16 KiB
Scheme

; Copyright (c) 1993-1999 by Richard Kelsey. See file COPYING.
; This file contains the definitions of the node tree data structure.
;---------------------------------------------------------------------------
; Records to represent variables.
(define-record-type variable
((name) ; Source code name for variable (used for debugging only)
(id) ; Unique numeric identifier (used for debugging only)
(type) ; Type for variable's value
)
(binder ; LAMBDA node which binds this variable
(refs '()) ; List of leaf nodes n for which (REFERENCE-VARIABLE n) = var.
(flag #f) ; Useful slot, used by shapes, COPY-NODE, NODE->VECTOR, etc.
; all users must leave this is #F
(flags '()) ; For various annotations, e.g. IGNORABLE
(generate #f) ; For whatever code generation wants
))
(define-record-discloser type/variable
(lambda (var)
(node-hash var)
(list 'variable (variable-name var) (variable-id var))))
(define (make-variable name type)
(variable-maker name (new-id) type))
(define (make-global-variable name type)
(let ((var (make-variable name type)))
(set-variable-binder! var #f)
var))
(define (global-variable? var)
(not (variable-binder var)))
; Every variable has a unique numeric identifier that is used for printing.
(define *variable-id* 0)
(define (new-id)
(let ((id *variable-id*))
(set! *variable-id* (+ 1 *variable-id*))
id))
(define (erase-variable var)
(cond ((eq? (variable-id var) '<erased>)
(bug "variable ~S already erased" var))
(else
(set-variable-id! var '<erased>))))
(define *node-hash-table* #f)
(define (reset-node-id)
(set! *variable-id* 0)
(set! *node-hash-table* (make-table)))
(define (node-hash var-or-lambda)
(let ((id (if (variable? var-or-lambda)
(variable-id var-or-lambda)
(lambda-id var-or-lambda))))
(table-set! *node-hash-table* id var-or-lambda)))
(define (node-unhash n)
(table-ref *node-hash-table* n))
; The index of VAR in the variables bound by its binder.
(define (variable-index var)
(let ((binder (variable-binder var)))
(if (not binder)
(bug "VARIABLE-INDEX called on global variable ~S" var)
(do ((i 0 (+ i 1))
(vs (lambda-variables binder) (cdr vs)))
((eq? (car vs) var)
i)))))
; Copy an old variable.
(define (copy-variable old)
(let ((var (make-variable (variable-name old) (variable-type old))))
(set-variable-flags! var (variable-flags old))
var))
; An unused variable is either #F or a variable with no references.
(define (used? var)
(and var
(not (null? (variable-refs var)))))
(define (unused? var)
(not (used? var)))
; known values for top-level variables
(define (flag-accessor flag)
(lambda (var)
(let ((p (flag-assq flag (variable-flags var))))
(if p (cdr p) #f))))
(define (flag-setter flag)
(lambda (var value)
(set-variable-flags! var
(cons (cons flag value)
(variable-flags var)))))
(define (flag-remover flag)
(lambda (var)
(set-variable-flags! var (filter (lambda (x)
(or (not (pair? x))
(not (eq? (car x) flag))))
(variable-flags var)))))
(define variable-known-value (flag-accessor 'known-value))
(define add-variable-known-value! (flag-setter 'known-value))
(define remove-variable-known-value! (flag-remover 'known-value))
(define variable-simplifier (flag-accessor 'simplifier))
(define add-variable-simplifier! (flag-setter 'simplifier))
(define remove-variable-simplifier! (flag-remover 'simplifier))
(define variable-known-lambda (flag-accessor 'known-lambda))
(define note-known-global-lambda! (flag-setter 'known-lambda))
;----------------------------------------------------------------------------
; The main record for the node tree
(define-record-type node
((variant) ; One of LAMBDA, CALL, REFERENCE, LITERAL
)
((parent empty) ; Parent node
(index '<free>) ; Index of this node in parent
(simplified? #f) ; True if it has already been simplified.
(flag #f) ; Useful flag, all users must leave this is #F
stuff-0 ; Variant components - each type of node has a different
stuff-1 ; use for these fields
stuff-2
stuff-3
))
(define-record-discloser type/node
(lambda (node)
`(node ,(node-variant node)
. ,(case (node-variant node)
((lambda)
(node-hash node)
(list (lambda-name node) (lambda-id node)))
((call)
(list (primop-id (call-primop node))))
((reference)
(let ((var (reference-variable node)))
(list (variable-name var) (variable-id var))))
((literal)
(list (literal-value node)))
(else
'())))))
(define make-node node-maker)
;--------------------------------------------------------------------------
; EMPTY is used to mark empty parent and child slots in nodes.
(define empty
(list 'empty))
(define (empty? obj) (eq? obj empty))
(define (proclaim-empty probe)
(cond ((not (empty? probe))
(bug "not empty - ~S" probe))))
;----------------------------------------------------------------------------
; This walks the tree rooted at NODE and removes all pointers that point into
; this tree from outside.
(define (erase node)
(let label ((node node))
(cond ((empty? node)
#f)
(else
(case (node-variant node)
((lambda)
(label (lambda-body node)))
((call)
(walk-vector label (call-args node))))
(really-erase node)))))
; This does the following:
; Checks that this node has not already been removed from the tree.
;
; Reference nodes are removed from the refs list of the variable they reference.
;
; For lambda nodes, the variables are erased, non-CONT lambdas are removed from
; the *LAMBDAS* list (CONT lambdas are never on the list).
;
; Literal nodes whose values have reference lists are removed from those
; reference lists.
(define (really-erase node)
(cond ((empty? node)
#f)
(else
(cond ((eq? (node-index node) '<erased>)
(bug "node erased twice ~S" node))
((reference-node? node)
(let ((var (reference-variable node)))
(set-variable-refs! var
(delq! node (variable-refs var)))))
((lambda-node? node)
(for-each (lambda (v)
(if v (erase-variable v)))
(lambda-variables node))
(if (neq? (lambda-type node) 'cont)
(delete-lambda node))
(set-lambda-variables! node '())) ; safety
((literal-node? node)
(let ((refs (literal-refs node)))
(if refs
(set-literal-reference-list!
refs
(delq! node (literal-reference-list refs)))))))
; (erase-type (node-type node))
(set-node-index! node '<erased>))))
;---------------------------------------------------------------------------
; CONNECTING AND DISCONNECTING NODES
;
; There are two versions of each of these routines, one for value nodes
; (LAMBDA, REFERENCE, or LITERAL), and one for call nodes.
; Detach a node from the tree.
(define (detach node)
(vector-set! (call-args (node-parent node))
(node-index node)
empty)
(set-node-index! node #f)
(set-node-parent! node empty)
node)
(define (detach-body node)
(set-lambda-body! (node-parent node) empty)
(set-node-index! node #f)
(set-node-parent! node empty)
node)
; Attach a node to the tree.
(define (attach parent index child)
(proclaim-empty (node-parent child))
(proclaim-empty (vector-ref (call-args parent) index))
(vector-set! (call-args parent) index child)
(set-node-parent! child parent)
(set-node-index! child index)
(values))
(define (attach-body parent call)
(proclaim-empty (node-parent call))
(proclaim-empty (lambda-body parent))
(set-lambda-body! parent call)
(set-node-parent! call parent)
(set-node-index! call '-1)
(values))
; Replace node in tree with value of applying proc to node.
; Note the fact that a change has been made at this point in the tree.
(define (move node proc)
(let ((parent (node-parent node))
(index (node-index node)))
(detach node)
(let ((new (proc node)))
(attach parent index new)
(mark-changed new))))
(define (move-body node proc)
(let ((parent (node-parent node)))
(detach-body node)
(let ((new (proc node)))
(attach-body parent new)
(mark-changed new))))
; Put CALL into the tree as the body of lambda-node PARENT, making the current
; body of PARENT the body of lambda-node CONT.
(define (insert-body call cont parent)
(move-body (lambda-body parent)
(lambda (old-call)
(attach-body cont old-call)
call)))
; Replace old-node with new-node, noting that a change has been made at this
; point in the tree.
(define (replace old-node new-node)
(let ((index (node-index old-node))
(parent (node-parent old-node)))
(mark-changed old-node)
(erase (detach old-node))
(attach parent index new-node)
(set-node-simplified?! new-node #f)
(values)))
(define (replace-body old-node new-node)
(let ((parent (node-parent old-node)))
(mark-changed old-node)
(erase (detach-body old-node))
(attach-body parent new-node)
(set-node-simplified?! new-node #f)
(values)))
; Starting with the parent of NODE, set the SIMPLIFIED? flags of the
; ancestors of NODE to be #F.
(define (mark-changed node)
(do ((p (node-parent node) (node-parent p)))
((or (empty? p)
(not (node-simplified? p))))
(set-node-simplified?! p #f)))
;-------------------------------------------------------------------------
; Syntax for defining the different types of nodes.
(define-syntax define-node-type
(lambda (form rename compare)
(let ((id (cadr form))
(slots (cddr form)))
(let ((pred (concatenate-symbol id '- 'node?)))
`(begin (define (,pred x)
(eq? ',id (node-variant x)))
. ,(do ((i 0 (+ i 1))
(s slots (cdr s))
(r '() (let ((n (concatenate-symbol id '- (car s)))
(f (concatenate-symbol 'node-stuff- i)))
`((define-node-field ,n ,pred ,f)
. ,r))))
((null? s) (reverse r))))))))
; These are used to rename the NODE-STUFF fields of particular node variants.
(define-syntax define-node-field
(lambda (form rename compare)
(let ((id (cadr form))
(predicate (caddr form))
(field (cadddr form)))
`(begin
(define (,id node)
(,field (enforce ,predicate node)))
(define (,(concatenate-symbol 'set- id '!) node val)
(,(concatenate-symbol 'set- field '!)
(enforce ,predicate node)
val))))))
;-------------------------------------------------------------------------
; literals
(define-node-type literal
value ; the value
type ; the type of the value
refs ; either #F or a literal-reference record; only a few types of literal
) ; literal values require reference lists
(define-record-type literal-reference
()
((list '()) ; list of literal nodes that refer to a particular value
))
(define make-literal-reference-list literal-reference-maker)
(define (make-literal-node value type)
(let ((node (make-node 'literal)))
(set-literal-value! node value)
(set-literal-type! node type)
(set-literal-refs! node #f)
node))
(define (copy-literal-node node)
(let ((new (make-node 'literal))
(refs (literal-refs node)))
(set-literal-value! new (literal-value node))
(set-literal-type! new (literal-type node))
(set-literal-refs! new refs)
(if refs (set-literal-reference-list!
refs
(cons new (literal-reference-list refs))))
new))
(define (make-marked-literal value refs)
(let ((node (make-node 'literal)))
(set-literal-value! node value)
(set-literal-refs! node refs)
(set-literal-reference-list! refs
(cons node (literal-reference-list refs)))
node))
;-------------------------------------------------------------------------
; These just contain an identifier.
(define-node-type reference
variable
)
(define (make-reference-node variable)
(let ((node (make-node 'reference)))
(set-reference-variable! node variable)
(set-variable-refs! variable (cons node (variable-refs variable)))
node))
; Literal and reference nodes are leaf nodes as they do not contain any other
; nodes.
(define (leaf-node? n)
(or (literal-node? n)
(reference-node? n)))
;--------------------------------------------------------------------------
; Call nodes
(define-node-type call
primop ; the primitive being called
args ; vector of child nodes
exits ; the number of arguments that are continuations
source ; source info
)
; Create a call node with primop P, N children and EXITS exits.
(define (make-call-node primop n exits)
(let ((node (make-node 'call)))
(set-call-primop! node primop)
(set-call-args! node (make-vector n empty))
(set-call-exits! node exits)
(set-call-source! node #f)
node))
(define (call-arg call index)
(vector-ref (call-args call) index))
(define (call-arg-count call)
(vector-length (call-args call)))
;----------------------------------------------------------------------------
; LAMBDA NODES
(define-node-type lambda
body ; the call-node that is the body of the lambda
variables ; a list of variable records with #Fs for ignored positions
source ; source code for the lambda (if any)
data ; a LAMBDA-DATA record (lambdas have more associated data than
) ; the other node types.)
(define-subrecord lambda lambda-data lambda-data
((name) ; symbol (for debugging only)
id ; unique integer (for debugging only)
(type)) ; PROC, KNOWN-PROC, CONT, or JUMP (maybe ESCAPE at some point)
((block #f) ; either a basic-block (for flow analysis) or a code-block
; (for code generation).
(env #f) ; a record containing lexical environment data
(protocol #f) ; calling protocol from the source language
(prev #f) ; previous node on *LAMBDAS* list
(next #f) ; next node on *LAMBDAS* list
))
; Doubly linked list of all non-CONT lambdas
(define *lambdas* #f)
(define (initialize-lambdas)
(set! *lambdas* (make-lambda-node '*lambdas* 'cont '()))
(link-lambdas *lambdas* *lambdas*))
(define (link-lambdas node1 node2)
(set-lambda-prev! node2 node1)
(set-lambda-next! node1 node2))
(define (add-lambda node)
(let ((next (lambda-next *lambdas*)))
(link-lambdas *lambdas* node)
(link-lambdas node next)))
(define (delete-lambda node)
(link-lambdas (lambda-prev node) (lambda-next node))
(set-lambda-prev! node #f)
(set-lambda-next! node #f))
(define (walk-lambdas proc)
(do ((n (lambda-next *lambdas*) (lambda-next n)))
((eq? n *lambdas*))
(proc n))
(values))
(define (make-lambda-list)
(do ((n (lambda-next *lambdas*) (lambda-next n))
(l '() (cons n l)))
((eq? n *lambdas*)
l)))
(define (add-lambdas nodes)
(for-each add-lambda nodes))
; Create a lambda node. NAME is used as the name of the lambda node's
; self variable. VARS is a list of variables. The VARIABLE-BINDER slot
; of each variable is set to be the new lambda node.
(define (make-lambda-node name type vars)
(let ((node (make-node 'lambda))
(data (lambda-data-maker name (new-id) type)))
(set-lambda-body! node empty)
(set-lambda-variables! node vars)
(set-lambda-data! node data)
(set-lambda-source! node #f)
(for-each (lambda (var)
(if var (set-variable-binder! var node)))
vars)
(if (neq? type 'cont)
(add-lambda node))
node))
; Change the type of lambda-node NODE to be TYPE. This may require adding or
; deleting NODE from the list *LAMBDAS*.
(define (change-lambda-type node type)
(let ((has (lambda-type node)))
(cond ((neq? type (lambda-type node))
(set-lambda-type! node type)
(cond ((eq? type 'cont)
(delete-lambda node))
((eq? has 'cont)
(add-lambda node)))))
(values)))
(define (lambda-variable-count node)
(length (lambda-variables node)))
(define (calls-known? node)
(neq? (lambda-type node) 'proc))
(define (set-calls-known?! node)
(set-lambda-type! node 'known-proc))
(define (proc-lambda? node)
(or (eq? 'proc (lambda-type node))
(eq? 'known-proc (lambda-type node))))