Commit | Line | Data |
---|---|---|
8bc65d2d AW |
1 | ;;; Abstract constant folding on CPS |
2 | ;;; Copyright (C) 2014 Free Software Foundation, Inc. | |
3 | ;;; | |
4 | ;;; This library is free software: you can redistribute it and/or modify | |
5 | ;;; it under the terms of the GNU Lesser General Public License as | |
6 | ;;; published by the Free Software Foundation, either version 3 of the | |
7 | ;;; License, or (at your option) any later version. | |
8 | ;;; | |
9 | ;;; This library is distributed in the hope that it will be useful, but | |
10 | ;;; WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
12 | ;;; Lesser General Public License for more details. | |
13 | ;;; | |
14 | ;;; You should have received a copy of the GNU Lesser General Public | |
15 | ;;; License along with this program. If not, see | |
16 | ;;; <http://www.gnu.org/licenses/>. | |
17 | ||
18 | ;;; Commentary: | |
19 | ;;; | |
20 | ;;; This pass uses the abstract interpretation provided by type analysis | |
21 | ;;; to fold constant values and type predicates. It is most profitably | |
22 | ;;; run after CSE, to take advantage of scalar replacement. | |
23 | ;;; | |
24 | ;;; Code: | |
25 | ||
26 | (define-module (language cps type-fold) | |
27 | #:use-module (ice-9 match) | |
28 | #:use-module (language cps) | |
29 | #:use-module (language cps dfg) | |
30 | #:use-module (language cps renumber) | |
31 | #:use-module (language cps types) | |
32 | #:export (type-fold)) | |
33 | ||
34 | (define &scalar-types | |
35 | (logior &exact-integer &flonum &char &unspecified &boolean &nil &null)) | |
36 | ||
37 | (define *branch-folders* (make-hash-table)) | |
38 | ||
39 | (define-syntax-rule (define-branch-folder name f) | |
40 | (hashq-set! *branch-folders* 'name f)) | |
41 | ||
42 | (define-syntax-rule (define-branch-folder-alias to from) | |
43 | (hashq-set! *branch-folders* 'to (hashq-ref *branch-folders* 'from))) | |
44 | ||
45 | (define-syntax-rule (define-unary-branch-folder (name arg min max) body ...) | |
46 | (define-branch-folder name (lambda (arg min max) body ...))) | |
47 | ||
48 | (define-syntax-rule (define-binary-branch-folder (name arg0 min0 max0 | |
49 | arg1 min1 max1) | |
50 | body ...) | |
51 | (define-branch-folder name (lambda (arg0 min0 max0 arg1 min1 max1) body ...))) | |
52 | ||
53 | (define-syntax-rule (define-unary-type-predicate-folder name &type) | |
54 | (define-unary-branch-folder (name type min max) | |
55 | (let ((type* (logand type &type))) | |
56 | (cond | |
57 | ((zero? type*) (values #t #f)) | |
58 | ((eqv? type type*) (values #t #t)) | |
59 | (else (values #f #f)))))) | |
60 | ||
61 | ;; All the cases that are in compile-bytecode. | |
62 | (define-unary-type-predicate-folder pair? &pair) | |
63 | (define-unary-type-predicate-folder null? &null) | |
64 | (define-unary-type-predicate-folder nil? &nil) | |
65 | (define-unary-type-predicate-folder symbol? &symbol) | |
66 | (define-unary-type-predicate-folder variable? &box) | |
67 | (define-unary-type-predicate-folder vector? &vector) | |
68 | (define-unary-type-predicate-folder struct? &struct) | |
69 | (define-unary-type-predicate-folder string? &string) | |
70 | (define-unary-type-predicate-folder number? &number) | |
71 | (define-unary-type-predicate-folder char? &char) | |
72 | ||
73 | (define-binary-branch-folder (eq? type0 min0 max0 type1 min1 max1) | |
74 | (cond | |
75 | ((or (zero? (logand type0 type1)) (< max0 min1) (< max1 min0)) | |
76 | (values #t #f)) | |
77 | ((and (eqv? type0 type1) | |
78 | (eqv? min0 min1 max0 max1) | |
79 | (zero? (logand type0 (1- type0))) | |
80 | (not (zero? (logand type0 &scalar-types)))) | |
81 | (values #t #t)) | |
82 | (else | |
83 | (values #f #f)))) | |
84 | (define-branch-folder-alias eqv? eq?) | |
85 | (define-branch-folder-alias equal? eq?) | |
86 | ||
87 | (define (compare-ranges type0 min0 max0 type1 min1 max1) | |
88 | (and (zero? (logand (logior type0 type1) (lognot &real))) | |
89 | (cond ((< max0 min1) '<) | |
90 | ((> min0 max1) '>) | |
91 | ((= min0 max0 min1 max1) '=) | |
92 | ((<= max0 min1) '<=) | |
93 | ((>= min0 max1) '>=) | |
94 | (else #f)))) | |
95 | ||
96 | (define-binary-branch-folder (< type0 min0 max0 type1 min1 max1) | |
97 | (case (compare-ranges type0 min0 max0 type1 min1 max1) | |
98 | ((<) (values #t #t)) | |
99 | ((= >= >) (values #t #f)) | |
100 | (else (values #f #f)))) | |
101 | ||
102 | (define-binary-branch-folder (<= type0 min0 max0 type1 min1 max1) | |
103 | (case (compare-ranges type0 min0 max0 type1 min1 max1) | |
104 | ((< <= =) (values #t #t)) | |
105 | ((>) (values #t #f)) | |
106 | (else (values #f #f)))) | |
107 | ||
108 | (define-binary-branch-folder (= type0 min0 max0 type1 min1 max1) | |
109 | (case (compare-ranges type0 min0 max0 type1 min1 max1) | |
110 | ((=) (values #t #t)) | |
111 | ((< >) (values #t #f)) | |
112 | (else (values #f #f)))) | |
113 | ||
114 | (define-binary-branch-folder (>= type0 min0 max0 type1 min1 max1) | |
115 | (case (compare-ranges type0 min0 max0 type1 min1 max1) | |
116 | ((> >= =) (values #t #t)) | |
117 | ((<) (values #t #f)) | |
118 | (else (values #f #f)))) | |
119 | ||
120 | (define-binary-branch-folder (> type0 min0 max0 type1 min1 max1) | |
121 | (case (compare-ranges type0 min0 max0 type1 min1 max1) | |
122 | ((>) (values #t #t)) | |
123 | ((= <= <) (values #t #f)) | |
124 | (else (values #f #f)))) | |
125 | ||
126 | (define (compute-folded fun dfg min-label min-var) | |
127 | (define (scalar-value type val) | |
128 | (cond | |
129 | ((eqv? type &exact-integer) val) | |
130 | ((eqv? type &flonum) (exact->inexact val)) | |
131 | ((eqv? type &char) (integer->char val)) | |
132 | ((eqv? type &unspecified) *unspecified*) | |
133 | ((eqv? type &boolean) (not (zero? val))) | |
134 | ((eqv? type &nil) #nil) | |
135 | ((eqv? type &null) '()) | |
136 | (else (error "unhandled type" type val)))) | |
a7ee377d AW |
137 | (let* ((typev (infer-types fun dfg #:max-label-count 3000)) |
138 | (folded? (and typev | |
139 | (make-bitvector (/ (vector-length typev) 2) #f))) | |
140 | (folded-values (and typev | |
141 | (make-vector (bitvector-length folded?) #f)))) | |
8bc65d2d AW |
142 | (define (label->idx label) (- label min-label)) |
143 | (define (var->idx var) (- var min-var)) | |
144 | (define (maybe-fold-value! label name k def) | |
145 | (call-with-values (lambda () (lookup-post-type typev label def)) | |
146 | (lambda (type min max) | |
147 | (when (and (not (zero? type)) | |
148 | (zero? (logand type (1- type))) | |
149 | (zero? (logand type (lognot &scalar-types))) | |
150 | (eqv? min max)) | |
151 | (bitvector-set! folded? label #t) | |
152 | (vector-set! folded-values label (scalar-value type min)))))) | |
153 | (define (maybe-fold-unary-branch! label name arg) | |
154 | (let* ((folder (hashq-ref *branch-folders* name))) | |
155 | (when folder | |
156 | (call-with-values (lambda () (lookup-pre-type typev label arg)) | |
157 | (lambda (type min max) | |
158 | (call-with-values (lambda () (folder type min max)) | |
159 | (lambda (f? v) | |
160 | (bitvector-set! folded? label f?) | |
161 | (vector-set! folded-values label v)))))))) | |
162 | (define (maybe-fold-binary-branch! label name arg0 arg1) | |
163 | (let* ((folder (hashq-ref *branch-folders* name))) | |
164 | (when folder | |
165 | (call-with-values (lambda () (lookup-pre-type typev label arg0)) | |
166 | (lambda (type0 min0 max0) | |
167 | (call-with-values (lambda () (lookup-pre-type typev label arg1)) | |
168 | (lambda (type1 min1 max1) | |
169 | (call-with-values (lambda () | |
170 | (folder type0 min0 max0 type1 min1 max1)) | |
171 | (lambda (f? v) | |
172 | (bitvector-set! folded? label f?) | |
173 | (vector-set! folded-values label v)))))))))) | |
174 | (define (visit-cont cont) | |
175 | (match cont | |
176 | (($ $cont label ($ $kargs _ _ body)) | |
177 | (visit-term body label)) | |
178 | (($ $cont label ($ $kclause arity body alternate)) | |
179 | (visit-cont body) | |
180 | (visit-cont alternate)) | |
181 | (_ #f))) | |
182 | (define (visit-term term label) | |
183 | (match term | |
184 | (($ $letk conts body) | |
185 | (for-each visit-cont conts) | |
186 | (visit-term body label)) | |
187 | (($ $letrec _ _ _ body) | |
188 | (visit-term body label)) | |
189 | (($ $continue k src ($ $primcall name args)) | |
59258f7c | 190 | ;; We might be able to fold primcalls that define a value. |
8bc65d2d AW |
191 | (match (lookup-cont k dfg) |
192 | (($ $kargs (_) (def)) | |
193 | (maybe-fold-value! (label->idx label) name (label->idx k) | |
194 | (var->idx def))) | |
8bc65d2d | 195 | (_ #f))) |
92805e21 AW |
196 | (($ $continue kf src ($ $branch kt ($ $primcall name args))) |
197 | ;; We might be able to fold primcalls that branch. | |
198 | (match args | |
199 | ((arg) | |
200 | (maybe-fold-unary-branch! (label->idx label) name | |
201 | (var->idx arg))) | |
202 | ((arg0 arg1) | |
203 | (maybe-fold-binary-branch! (label->idx label) name | |
204 | (var->idx arg0) (var->idx arg1))))) | |
8bc65d2d | 205 | (_ #f))) |
a7ee377d AW |
206 | (when typev |
207 | (match fun | |
208 | (($ $cont kfun ($ $kfun src meta self tail clause)) | |
209 | (visit-cont clause)))) | |
8bc65d2d AW |
210 | (values folded? folded-values))) |
211 | ||
212 | (define (fold-constants* fun dfg) | |
213 | (match fun | |
214 | (($ $cont min-label ($ $kfun _ _ min-var)) | |
215 | (call-with-values (lambda () (compute-folded fun dfg min-label min-var)) | |
216 | (lambda (folded? folded-values) | |
217 | (define (label->idx label) (- label min-label)) | |
218 | (define (var->idx var) (- var min-var)) | |
219 | (define (visit-cont cont) | |
220 | (rewrite-cps-cont cont | |
221 | (($ $cont label ($ $kargs names syms body)) | |
222 | (label ($kargs names syms ,(visit-term body label)))) | |
223 | (($ $cont label ($ $kclause arity body alternate)) | |
224 | (label ($kclause ,arity ,(visit-cont body) | |
225 | ,(and alternate (visit-cont alternate))))) | |
226 | (_ ,cont))) | |
227 | (define (visit-term term label) | |
228 | (rewrite-cps-term term | |
229 | (($ $letk conts body) | |
230 | ($letk ,(map visit-cont conts) | |
231 | ,(visit-term body label))) | |
232 | (($ $letrec names vars funs body) | |
233 | ($letrec names vars (map visit-fun funs) | |
234 | ,(visit-term body label))) | |
235 | (($ $continue k src (and fun ($ $fun))) | |
236 | ($continue k src ,(visit-fun fun))) | |
237 | (($ $continue k src (and primcall ($ $primcall))) | |
a7ee377d AW |
238 | ,(if (and folded? |
239 | (bitvector-ref folded? (label->idx label))) | |
8bc65d2d AW |
240 | (let ((val (vector-ref folded-values (label->idx label)))) |
241 | ;; Uncomment for debugging. | |
242 | ;; (pk 'folded src primcall val) | |
59258f7c AW |
243 | (let-fresh (k*) (v*) |
244 | ;; Rely on DCE to elide this expression, if | |
245 | ;; possible. | |
246 | (build-cps-term | |
247 | ($letk ((k* ($kargs (#f) (v*) | |
248 | ($continue k src ($const val))))) | |
249 | ($continue k* src ,primcall))))) | |
8bc65d2d | 250 | term)) |
92805e21 AW |
251 | (($ $continue kf src ($ $branch kt ($ $primcall))) |
252 | ,(if (and folded? | |
253 | (bitvector-ref folded? (label->idx label))) | |
254 | ;; Folded branch. | |
255 | (let ((val (vector-ref folded-values (label->idx label)))) | |
256 | (build-cps-term | |
257 | ($continue (if val kt kf) src ($values ())))) | |
258 | term)) | |
8bc65d2d AW |
259 | (_ ,term))) |
260 | (define (visit-fun fun) | |
261 | (rewrite-cps-exp fun | |
262 | (($ $fun free body) | |
263 | ($fun free ,(fold-constants* body dfg))))) | |
264 | (rewrite-cps-cont fun | |
265 | (($ $cont kfun ($ $kfun src meta self tail clause)) | |
266 | (kfun ($kfun src meta self ,tail ,(visit-cont clause)))))))))) | |
267 | ||
268 | (define (type-fold fun) | |
269 | (let* ((fun (renumber fun)) | |
270 | (dfg (compute-dfg fun))) | |
271 | (with-fresh-name-state-from-dfg dfg | |
272 | (fold-constants* fun dfg)))) |