1 ;;; TREE-IL -> GLIL compiler
3 ;; Copyright (C) 2001,2008,2009 Free Software Foundation, Inc.
5 ;; This program is free software; you can redistribute it and/or modify
6 ;; it under the terms of the GNU General Public License as published by
7 ;; the Free Software Foundation; either version 2, or (at your option)
10 ;; This program is distributed in the hope that it will be useful,
11 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ;; GNU General Public License for more details.
15 ;; You should have received a copy of the GNU General Public License
16 ;; along with this program; see the file COPYING. If not, write to
17 ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 ;; Boston, MA 02111-1307, USA.
22 (define-module (language tree-il analyze)
23 #:use-module (system base syntax)
24 #:use-module (language tree-il)
25 #:export (analyze-lexicals))
27 ;; allocation: the process of assigning a type and index to each var
28 ;; a var is external if it is heaps; assigning index is easy
29 ;; args are assigned in order
30 ;; locals are indexed as their linear position in the binding path
37 ;; This algorithm has the problem that variables are only allocated
38 ;; indices at the end of the binding path. If variables bound early in
39 ;; the path are not used in later portions of the path, their indices
40 ;; will not be recycled. This problem is particularly egregious in the
44 ;; -> (let ((a x)) (if a a (let ((b y)) (if b b z))))
46 ;; As you can see, the `a' binding is only used in the ephemeral `then'
47 ;; clause of the first `if', but its index would be reserved for the
48 ;; whole of the `or' expansion. So we have a hack for this specific
49 ;; case. A proper solution would be some sort of liveness analysis, and
50 ;; not our linear allocation algorithm.
53 ;; sym -> (local . index) | (heap level . index)
54 ;; lambda -> (nlocs . nexts)
56 (define (analyze-lexicals x)
57 ;; parents: lambda -> parent
58 ;; useful when we see a closed-over var, so we can calculate its
59 ;; coordinates (depth and index).
60 ;; bindings: lambda -> (sym ...)
61 ;; useful for two reasons: one, so we know how much space to allocate
62 ;; when we go into a lambda; and two, so that we know when to stop,
63 ;; when looking for closed-over vars.
64 ;; heaps: sym -> lambda
65 ;; allows us to heapify vars in an O(1) fashion
66 ;; refcounts: sym -> count
67 ;; allows us to detect the or-expansion an O(1) time
69 (define (find-heap sym parent)
70 ;; fixme: check displaced lexicals here?
71 (if (memq sym (hashq-ref bindings parent))
73 (find-heap sym (hashq-ref parents parent))))
75 (define (analyze! x parent level)
76 (define (step y) (analyze! y parent level))
77 (define (recur x parent) (analyze! x parent (1+ level)))
79 ((<application> proc args)
80 (step proc) (for-each step args))
82 ((<conditional> test then else)
83 (step test) (step then) (step else))
85 ((<lexical-ref> name gensym)
86 (hashq-set! refcounts gensym (1+ (hashq-ref refcounts gensym 0)))
87 (if (and (not (memq gensym (hashq-ref bindings parent)))
88 (not (hashq-ref heaps gensym)))
89 (hashq-set! heaps gensym (find-heap gensym parent))))
91 ((<lexical-set> name gensym exp)
93 (if (not (hashq-ref heaps gensym))
94 (hashq-set! heaps gensym (find-heap gensym parent))))
96 ((<module-set> mod name public? exp)
99 ((<toplevel-set> name exp)
102 ((<toplevel-define> name exp)
106 (for-each step exps))
108 ((<lambda> vars meta body)
109 (hashq-set! parents x parent)
110 (hashq-set! bindings x
111 (let rev* ((vars vars) (out '()))
112 (cond ((null? vars) out)
113 ((pair? vars) (rev* (cdr vars)
114 (cons (car vars) out)))
115 (else (cons vars out)))))
117 (hashq-set! bindings x (reverse! (hashq-ref bindings x))))
119 ((<let> vars vals exp)
121 (hashq-set! bindings parent
122 (append (reverse vars) (hashq-ref bindings parent)))
125 ((<letrec> vars vals exp)
126 (hashq-set! bindings parent
127 (append (reverse vars) (hashq-ref bindings parent)))
133 (define (allocate-heap! binder)
134 (hashq-set! heap-indexes binder
135 (1+ (hashq-ref heap-indexes binder -1))))
137 (define (allocate! x level n)
138 (define (recur y) (allocate! y level n))
140 ((<application> proc args)
141 (apply max (recur proc) (map recur args)))
143 ((<conditional> test then else)
144 (max (recur test) (recur then) (recur else)))
146 ((<lexical-set> name gensym exp)
149 ((<module-set> mod name public? exp)
152 ((<toplevel-set> name exp)
155 ((<toplevel-define> name exp)
159 (apply max (map recur exps)))
161 ((<lambda> vars meta body)
162 (let lp ((vars vars) (n 0))
164 (hashq-set! allocation x
165 (let ((nlocs (- (allocate! body (1+ level) n) n)))
166 (cons nlocs (1+ (hashq-ref heap-indexes x -1)))))
167 (let ((v (if (pair? vars) (car vars) vars)))
168 (let ((binder (hashq-ref heaps v)))
172 (cons* 'heap (1+ level) (allocate-heap! binder))
174 (lp (if (pair? vars) (cdr vars) '()) (1+ n)))))
177 ((<let> vars vals exp)
178 (let ((nmax (apply max (map recur vals))))
181 ((and (conditional? exp)
183 (let ((v (car vars)))
184 (and (not (hashq-ref heaps v))
185 (= (hashq-ref refcounts v 0) 2)
186 (lexical-ref? (conditional-test exp))
187 (eq? (lexical-ref-gensym (conditional-test exp)) v)
188 (lexical-ref? (conditional-then exp))
189 (eq? (lexical-ref-gensym (conditional-then exp)) v))))
190 (hashq-set! allocation (car vars) (cons 'stack n))
191 ;; the 1+ for this var
192 (max nmax (1+ n) (allocate! (conditional-else exp) level n)))
194 (let lp ((vars vars) (n n))
196 (max nmax (allocate! exp level n))
197 (let ((v (car vars)))
198 (let ((binder (hashq-ref heaps v)))
202 (cons* 'heap level (allocate-heap! binder))
204 (lp (cdr vars) (if binder n (1+ n)))))))))))
206 ((<letrec> vars vals exp)
207 (let lp ((vars vars) (n n))
209 (let ((nmax (apply max
211 (allocate! x level n))
213 (max nmax (allocate! exp level n)))
214 (let ((v (car vars)))
215 (let ((binder (hashq-ref heaps v)))
219 (cons* 'heap level (allocate-heap! binder))
221 (lp (cdr vars) (if binder n (1+ n))))))))
225 (define parents (make-hash-table))
226 (define bindings (make-hash-table))
227 (define heaps (make-hash-table))
228 (define refcounts (make-hash-table))
229 (define allocation (make-hash-table))
230 (define heap-indexes (make-hash-table))