Commit | Line | Data |
---|---|---|
dab0f9d5 AW |
1 | ;;; a simple inliner |
2 | ||
a75ea658 | 3 | ;; Copyright (C) 2009, 2010, 2011 Free Software Foundation, Inc. |
dab0f9d5 | 4 | |
c21c89b1 AW |
5 | ;;;; This library is free software; you can redistribute it and/or |
6 | ;;;; modify it under the terms of the GNU Lesser General Public | |
7 | ;;;; License as published by the Free Software Foundation; either | |
8 | ;;;; version 3 of the License, or (at your option) any later version. | |
9 | ;;;; | |
10 | ;;;; This library 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 GNU | |
13 | ;;;; Lesser General Public License for more details. | |
14 | ;;;; | |
15 | ;;;; You should have received a copy of the GNU Lesser General Public | |
16 | ;;;; License along with this library; if not, write to the Free Software | |
17 | ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
dab0f9d5 AW |
18 | |
19 | (define-module (language tree-il inline) | |
8a4ca0ea | 20 | #:use-module (system base pmatch) |
dab0f9d5 AW |
21 | #:use-module (system base syntax) |
22 | #:use-module (language tree-il) | |
23 | #:export (inline!)) | |
24 | ||
25 | ;; Possible optimizations: | |
26 | ;; * constant folding, propagation | |
27 | ;; * procedure inlining | |
28 | ;; * always when single call site | |
29 | ;; * always for "trivial" procs | |
30 | ;; * otherwise who knows | |
31 | ;; * dead code elimination | |
32 | ;; * degenerate case optimizations | |
33 | ;; * "fixing letrec" | |
34 | ||
0e4b7818 AW |
35 | (define (boolean-value x) |
36 | (let ((src (tree-il-src x))) | |
37 | (record-case x | |
38 | ((<void>) | |
39 | (make-const src #t)) | |
40 | ||
41 | ((<conditional> test consequent alternate) | |
42 | (record-case (boolean-value test) | |
43 | ((<const> exp) | |
44 | (case exp | |
45 | ((#t) (boolean-value consequent)) | |
46 | ((#f) (boolean-value alternate)) | |
47 | (else x))) | |
48 | (else x))) | |
49 | ||
a881a4ae AW |
50 | ((<primcall> src name args) |
51 | (pmatch (cons name args) | |
52 | ((,member ,k ,l) (guard (and (memq member '(memq memv)) | |
53 | (const? k) | |
54 | (list? (const-exp l)))) | |
55 | (cond | |
56 | ((null? (const-exp l)) | |
57 | (make-const #f #f)) | |
58 | ((const? k) | |
59 | (make-const #f (->bool ((case member | |
60 | ((memq) memq) | |
61 | ((memv) memv) | |
62 | (else (error "what" member))) | |
63 | (const-exp k) (const-exp l))))) | |
64 | (else | |
65 | (let lp ((elts (const-exp l))) | |
66 | (let ((test (make-primcall | |
67 | #f | |
68 | (case member | |
69 | ((memq) 'eq?) | |
70 | ((memv) 'eqv?) | |
71 | (else (error "what" member))) | |
72 | (list k (make-const #f (car elts)))))) | |
73 | (if (null? (cdr elts)) | |
74 | test | |
75 | (make-conditional | |
76 | src | |
77 | test | |
78 | (make-const #f #t) | |
79 | (lp (cdr elts))))))))) | |
a75ea658 AW |
80 | (else x))) |
81 | ||
0e4b7818 AW |
82 | ((<lambda> meta body) |
83 | (make-const src #t)) | |
84 | ||
85 | ((<const> exp) | |
86 | (make-const src (not (not exp)))) | |
87 | ||
88 | (else | |
89 | x)))) | |
90 | ||
dab0f9d5 AW |
91 | ;; This is a completely brain-dead optimization pass whose sole claim to |
92 | ;; fame is ((lambda () x)) => x. | |
93 | (define (inline! x) | |
8a4ca0ea AW |
94 | (define (inline1 x) |
95 | (record-case x | |
7081d4f9 | 96 | ((<call> src proc args) |
8a4ca0ea AW |
97 | (record-case proc |
98 | ;; ((lambda (y ...) x) z ...) => (let ((y z) ...) x) | |
99 | ((<lambda> body) | |
100 | (let lp ((lcase body)) | |
101 | (and lcase | |
102 | (record-case lcase | |
93f63467 AW |
103 | ((<lambda-case> req opt rest kw inits gensyms body alternate) |
104 | (if (and (= (length gensyms) (length req) (length args))) | |
105 | (let ((x (make-let src req gensyms args body))) | |
8a4ca0ea | 106 | (or (inline1 x) x)) |
3a88cb3b | 107 | (lp alternate))))))) |
bca488f1 | 108 | |
bca488f1 | 109 | (else #f))) |
80af1168 | 110 | |
a881a4ae AW |
111 | ((<primcall> src name args) |
112 | (pmatch (cons name args) | |
113 | ;; (call-with-values (lambda () foo) (lambda (a b . c) bar)) | |
114 | ;; => (let-values (((a b . c) foo)) bar) | |
115 | ;; | |
116 | ;; Note that this is a singly-binding form of let-values. Also | |
117 | ;; note that Scheme's let-values expands into call-with-values, | |
118 | ;; then here we reduce it to tree-il's let-values. | |
119 | ((@call-with-values ,producer ,consumer) | |
120 | (guard (lambda? consumer) | |
121 | (lambda-case? (lambda-body consumer)) | |
122 | (not (lambda-case-opt (lambda-body consumer))) | |
123 | (not (lambda-case-kw (lambda-body consumer))) | |
124 | (not (lambda-case-alternate (lambda-body consumer)))) | |
125 | (make-let-values | |
126 | src | |
127 | (let ((x (make-call src producer '()))) | |
128 | (or (inline1 x) x)) | |
129 | (lambda-body consumer))) | |
130 | (else #f))) | |
131 | ||
0e4b7818 AW |
132 | ((<conditional> test consequent alternate) |
133 | (let ((btest (boolean-value test))) | |
134 | (or (record-case btest | |
135 | ((<const> exp) | |
136 | (case exp | |
137 | ((#t) consequent) | |
138 | ((#f) alternate) | |
139 | (else #f))) | |
140 | (else #f)) | |
141 | (if (eq? test btest) | |
142 | x | |
143 | (make-conditional (conditional-src x) | |
144 | btest consequent alternate))))) | |
145 | ||
93f63467 AW |
146 | ((<let> gensyms body) |
147 | (if (null? gensyms) body x)) | |
80af1168 | 148 | |
93f63467 AW |
149 | ((<letrec> gensyms body) |
150 | (if (null? gensyms) body x)) | |
80af1168 | 151 | |
93f63467 AW |
152 | ((<fix> gensyms body) |
153 | (if (null? gensyms) body x)) | |
80af1168 | 154 | |
93f63467 AW |
155 | ((<lambda-case> req opt rest kw gensyms body alternate) |
156 | (define (args-compatible? args gensyms) | |
157 | (let lp ((args args) (gensyms gensyms)) | |
ac644098 | 158 | (cond |
93f63467 AW |
159 | ((null? args) (null? gensyms)) |
160 | ((null? gensyms) #f) | |
ac644098 | 161 | ((and (lexical-ref? (car args)) |
93f63467 AW |
162 | (eq? (lexical-ref-gensym (car args)) (car gensyms))) |
163 | (lp (cdr args) (cdr gensyms))) | |
ac644098 | 164 | (else #f)))) |
ea6b18e8 | 165 | |
98dcf051 | 166 | (and (not opt) (not kw) rest (not alternate) |
ac644098 | 167 | (record-case body |
a881a4ae | 168 | ((<primcall> name args) |
ac644098 | 169 | ;; (lambda args (apply (lambda ...) args)) => (lambda ...) |
a881a4ae | 170 | (and (eq? name '@apply) |
ac644098 AW |
171 | (pair? args) |
172 | (lambda? (car args)) | |
93f63467 | 173 | (args-compatible? (cdr args) gensyms) |
ac644098 AW |
174 | (lambda-body (car args)))) |
175 | (else #f)))) | |
176 | ||
177 | ;; Actually the opposite of inlining -- if the prompt cannot be proven to | |
7081d4f9 | 178 | ;; be escape-only, ensure that its body is the call of a thunk. |
ac644098 AW |
179 | ((<prompt> src tag body handler) |
180 | (define (escape-only? handler) | |
181 | (and (pair? (lambda-case-req handler)) | |
93f63467 | 182 | (let ((cont (car (lambda-case-gensyms handler)))) |
ac644098 AW |
183 | (tree-il-fold (lambda (leaf escape-only?) |
184 | (and escape-only? | |
185 | (not | |
186 | (and (lexical-ref? leaf) | |
187 | (eq? (lexical-ref-gensym leaf) cont))))) | |
188 | (lambda (down escape-only?) escape-only?) | |
189 | (lambda (up escape-only?) escape-only?) | |
190 | #t | |
191 | (lambda-case-body handler))))) | |
192 | (define (make-thunk body) | |
193 | (make-lambda #f '() (make-lambda-case #f '() #f #f #f '() '() body #f))) | |
194 | ||
7081d4f9 AW |
195 | (if (or (and (call? body) |
196 | (lambda? (call-proc body)) | |
197 | (null? (call-args body))) | |
ac644098 AW |
198 | (escape-only? handler)) |
199 | x | |
200 | (make-prompt src tag | |
7081d4f9 | 201 | (make-call #f (make-thunk body) '()) |
ac644098 | 202 | handler))) |
ea6b18e8 | 203 | |
8a4ca0ea AW |
204 | (else #f))) |
205 | (post-order! inline1 x)) |