fix `nil?' type inference
[bpt/guile.git] / module / language / cps / type-fold.scm
CommitLineData
8bc65d2d 1;;; Abstract constant folding on CPS
34ff3af9 2;;; Copyright (C) 2014, 2015 Free Software Foundation, Inc.
8bc65d2d
AW
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)
9243902a 32 #:use-module (system base target)
8bc65d2d
AW
33 #:export (type-fold))
34
384d1ec3
AW
35
36\f
37
38;; Branch folders.
39
8bc65d2d 40(define &scalar-types
7f5887e7 41 (logior &exact-integer &flonum &char &unspecified &false &true &nil &null))
8bc65d2d
AW
42
43(define *branch-folders* (make-hash-table))
44
45(define-syntax-rule (define-branch-folder name f)
46 (hashq-set! *branch-folders* 'name f))
47
48(define-syntax-rule (define-branch-folder-alias to from)
49 (hashq-set! *branch-folders* 'to (hashq-ref *branch-folders* 'from)))
50
51(define-syntax-rule (define-unary-branch-folder (name arg min max) body ...)
52 (define-branch-folder name (lambda (arg min max) body ...)))
53
54(define-syntax-rule (define-binary-branch-folder (name arg0 min0 max0
55 arg1 min1 max1)
56 body ...)
57 (define-branch-folder name (lambda (arg0 min0 max0 arg1 min1 max1) body ...)))
58
59(define-syntax-rule (define-unary-type-predicate-folder name &type)
60 (define-unary-branch-folder (name type min max)
61 (let ((type* (logand type &type)))
62 (cond
63 ((zero? type*) (values #t #f))
64 ((eqv? type type*) (values #t #t))
65 (else (values #f #f))))))
66
67;; All the cases that are in compile-bytecode.
68(define-unary-type-predicate-folder pair? &pair)
69(define-unary-type-predicate-folder null? &null)
8bc65d2d
AW
70(define-unary-type-predicate-folder symbol? &symbol)
71(define-unary-type-predicate-folder variable? &box)
72(define-unary-type-predicate-folder vector? &vector)
73(define-unary-type-predicate-folder struct? &struct)
74(define-unary-type-predicate-folder string? &string)
75(define-unary-type-predicate-folder number? &number)
76(define-unary-type-predicate-folder char? &char)
77
78(define-binary-branch-folder (eq? type0 min0 max0 type1 min1 max1)
79 (cond
80 ((or (zero? (logand type0 type1)) (< max0 min1) (< max1 min0))
81 (values #t #f))
82 ((and (eqv? type0 type1)
83 (eqv? min0 min1 max0 max1)
84 (zero? (logand type0 (1- type0)))
85 (not (zero? (logand type0 &scalar-types))))
86 (values #t #t))
87 (else
88 (values #f #f))))
89(define-branch-folder-alias eqv? eq?)
90(define-branch-folder-alias equal? eq?)
91
92(define (compare-ranges type0 min0 max0 type1 min1 max1)
93 (and (zero? (logand (logior type0 type1) (lognot &real)))
94 (cond ((< max0 min1) '<)
95 ((> min0 max1) '>)
96 ((= min0 max0 min1 max1) '=)
97 ((<= max0 min1) '<=)
98 ((>= min0 max1) '>=)
99 (else #f))))
100
101(define-binary-branch-folder (< type0 min0 max0 type1 min1 max1)
102 (case (compare-ranges type0 min0 max0 type1 min1 max1)
103 ((<) (values #t #t))
104 ((= >= >) (values #t #f))
105 (else (values #f #f))))
106
107(define-binary-branch-folder (<= type0 min0 max0 type1 min1 max1)
108 (case (compare-ranges type0 min0 max0 type1 min1 max1)
109 ((< <= =) (values #t #t))
110 ((>) (values #t #f))
111 (else (values #f #f))))
112
113(define-binary-branch-folder (= type0 min0 max0 type1 min1 max1)
114 (case (compare-ranges type0 min0 max0 type1 min1 max1)
115 ((=) (values #t #t))
116 ((< >) (values #t #f))
117 (else (values #f #f))))
118
119(define-binary-branch-folder (>= type0 min0 max0 type1 min1 max1)
120 (case (compare-ranges type0 min0 max0 type1 min1 max1)
121 ((> >= =) (values #t #t))
122 ((<) (values #t #f))
123 (else (values #f #f))))
124
125(define-binary-branch-folder (> type0 min0 max0 type1 min1 max1)
126 (case (compare-ranges type0 min0 max0 type1 min1 max1)
127 ((>) (values #t #t))
128 ((= <= <) (values #t #f))
129 (else (values #f #f))))
130
d613ccaa
AW
131(define-binary-branch-folder (logtest type0 min0 max0 type1 min1 max1)
132 (define (logand-min a b)
133 (if (< a b 0)
134 (min a b)
135 0))
136 (define (logand-max a b)
137 (if (< a b 0)
138 0
139 (max a b)))
140 (if (and (= min0 max0) (= min1 max1) (eqv? type0 type1 &exact-integer))
141 (values #t (logtest min0 min1))
142 (values #f #f)))
143
384d1ec3
AW
144
145\f
146
147;; Strength reduction.
148
149(define *primcall-reducers* (make-hash-table))
150
151(define-syntax-rule (define-primcall-reducer name f)
152 (hashq-set! *primcall-reducers* 'name f))
153
9243902a 154(define-syntax-rule (define-unary-primcall-reducer (name dfg k src
384d1ec3
AW
155 arg type min max)
156 body ...)
157 (define-primcall-reducer name
9243902a 158 (lambda (dfg k src arg type min max) body ...)))
384d1ec3 159
9243902a 160(define-syntax-rule (define-binary-primcall-reducer (name dfg k src
384d1ec3
AW
161 arg0 type0 min0 max0
162 arg1 type1 min1 max1)
163 body ...)
164 (define-primcall-reducer name
9243902a 165 (lambda (dfg k src arg0 type0 min0 max0 arg1 type1 min1 max1) body ...)))
384d1ec3 166
9243902a 167(define-binary-primcall-reducer (mul dfg k src
384d1ec3
AW
168 arg0 type0 min0 max0
169 arg1 type1 min1 max1)
170 (define (negate arg)
171 (let-fresh (kzero) (zero)
172 (build-cps-term
173 ($letk ((kzero ($kargs (#f) (zero)
174 ($continue k src ($primcall 'sub (zero arg))))))
175 ($continue kzero src ($const 0))))))
176 (define (zero)
177 (build-cps-term ($continue k src ($const 0))))
178 (define (identity arg)
179 (build-cps-term ($continue k src ($values (arg)))))
180 (define (double arg)
181 (build-cps-term ($continue k src ($primcall 'add (arg arg)))))
182 (define (power-of-two constant arg)
183 (let ((n (let lp ((bits 0) (constant constant))
184 (if (= constant 1) bits (lp (1+ bits) (ash constant -1))))))
185 (let-fresh (kbits) (bits)
186 (build-cps-term
187 ($letk ((kbits ($kargs (#f) (bits)
188 ($continue k src ($primcall 'ash (arg bits))))))
189 ($continue kbits src ($const n)))))))
190 (define (mul/constant constant constant-type arg arg-type)
191 (and (or (= constant-type &exact-integer) (= constant-type arg-type))
192 (case constant
193 ;; (* arg -1) -> (- 0 arg)
194 ((-1) (negate arg))
195 ;; (* arg 0) -> 0 if arg is not a flonum or complex
196 ((0) (and (= constant-type &exact-integer)
197 (zero? (logand arg-type
198 (lognot (logior &flonum &complex))))
199 (zero)))
200 ;; (* arg 1) -> arg
201 ((1) (identity arg))
202 ;; (* arg 2) -> (+ arg arg)
203 ((2) (double arg))
204 (else (and (= constant-type arg-type &exact-integer)
205 (positive? constant)
206 (zero? (logand constant (1- constant)))
207 (power-of-two constant arg))))))
208 (cond
9243902a 209 ((logtest (logior type0 type1) (lognot &number)) #f)
384d1ec3
AW
210 ((= min0 max0) (mul/constant min0 type0 arg1 type1))
211 ((= min1 max1) (mul/constant min1 type1 arg0 type0))
212 (else #f)))
213
9243902a
AW
214(define-binary-primcall-reducer (logbit? dfg k src
215 arg0 type0 min0 max0
216 arg1 type1 min1 max1)
217 (define (convert-to-logtest bool-term)
218 (let-fresh (kt kf kmask kbool) (mask bool)
219 (build-cps-term
220 ($letk ((kt ($kargs () ()
221 ($continue kbool src ($const #t))))
222 (kf ($kargs () ()
223 ($continue kbool src ($const #f))))
224 (kbool ($kargs (#f) (bool)
225 ,(bool-term bool)))
226 (kmask ($kargs (#f) (mask)
227 ($continue kf src
228 ($branch kt ($primcall 'logtest (mask arg1)))))))
229 ,(if (eq? min0 max0)
230 ($continue kmask src ($const (ash 1 min0)))
231 (let-fresh (kone) (one)
232 (build-cps-term
233 ($letk ((kone ($kargs (#f) (one)
234 ($continue kmask src
235 ($primcall 'ash (one arg0))))))
236 ($continue kone src ($const 1))))))))))
237 ;; Hairiness because we are converting from a primcall with unknown
238 ;; arity to a branching primcall.
239 (let ((positive-fixnum-bits (- (* (target-word-size) 8) 3)))
240 (and (= type0 &exact-integer)
241 (<= 0 min0 positive-fixnum-bits)
242 (<= 0 max0 positive-fixnum-bits)
243 (match (lookup-cont k dfg)
244 (($ $kreceive arity kargs)
245 (match arity
246 (($ $arity (_) () (not #f) () #f)
247 (convert-to-logtest
248 (lambda (bool)
249 (let-fresh (knil) (nil)
250 (build-cps-term
251 ($letk ((knil ($kargs (#f) (nil)
252 ($continue kargs src
253 ($values (bool nil))))))
254 ($continue knil src ($const '()))))))))
255 (_
256 (convert-to-logtest
257 (lambda (bool)
258 (build-cps-term
259 ($continue k src ($primcall 'values (bool)))))))))
260 (($ $ktail)
261 (convert-to-logtest
262 (lambda (bool)
263 (build-cps-term
264 ($continue k src ($primcall 'return (bool)))))))))))
265
384d1ec3
AW
266
267\f
268
269;;
270
271(define (fold-and-reduce fun dfg min-label min-var)
8bc65d2d
AW
272 (define (scalar-value type val)
273 (cond
274 ((eqv? type &exact-integer) val)
275 ((eqv? type &flonum) (exact->inexact val))
276 ((eqv? type &char) (integer->char val))
277 ((eqv? type &unspecified) *unspecified*)
7f5887e7
AW
278 ((eqv? type &false) #f)
279 ((eqv? type &true) #t)
8bc65d2d
AW
280 ((eqv? type &nil) #nil)
281 ((eqv? type &null) '())
282 (else (error "unhandled type" type val))))
ec412d75
AW
283 (let* ((typev (infer-types fun dfg))
284 (label-count ((make-local-cont-folder label-count)
285 (lambda (k cont label-count) (1+ label-count))
286 fun 0))
287 (folded? (make-bitvector label-count #f))
384d1ec3
AW
288 (folded-values (make-vector label-count #f))
289 (reduced-terms (make-vector label-count #f)))
8bc65d2d
AW
290 (define (label->idx label) (- label min-label))
291 (define (var->idx var) (- var min-var))
384d1ec3
AW
292 (define (maybe-reduce-primcall! label k src name args)
293 (let* ((reducer (hashq-ref *primcall-reducers* name)))
9243902a 294 (when reducer
384d1ec3
AW
295 (vector-set!
296 reduced-terms
297 (label->idx label)
298 (match args
299 ((arg0)
300 (call-with-values (lambda () (lookup-pre-type typev label arg0))
301 (lambda (type0 min0 max0)
9243902a 302 (reducer dfg k src arg0 type0 min0 max0))))
384d1ec3
AW
303 ((arg0 arg1)
304 (call-with-values (lambda () (lookup-pre-type typev label arg0))
305 (lambda (type0 min0 max0)
306 (call-with-values (lambda () (lookup-pre-type typev label arg1))
307 (lambda (type1 min1 max1)
9243902a 308 (reducer dfg k src arg0 type0 min0 max0
384d1ec3
AW
309 arg1 type1 min1 max1))))))
310 (_ #f))))))
ec412d75
AW
311 (define (maybe-fold-value! label name def)
312 (call-with-values (lambda () (lookup-post-type typev label def 0))
8bc65d2d 313 (lambda (type min max)
384d1ec3
AW
314 (cond
315 ((and (not (zero? type))
316 (zero? (logand type (1- type)))
317 (zero? (logand type (lognot &scalar-types)))
318 (eqv? min max))
ec412d75
AW
319 (bitvector-set! folded? (label->idx label) #t)
320 (vector-set! folded-values (label->idx label)
9243902a
AW
321 (scalar-value type min))
322 #t)
323 (else #f)))))
8bc65d2d
AW
324 (define (maybe-fold-unary-branch! label name arg)
325 (let* ((folder (hashq-ref *branch-folders* name)))
326 (when folder
327 (call-with-values (lambda () (lookup-pre-type typev label arg))
328 (lambda (type min max)
329 (call-with-values (lambda () (folder type min max))
330 (lambda (f? v)
ec412d75
AW
331 (bitvector-set! folded? (label->idx label) f?)
332 (vector-set! folded-values (label->idx label) v))))))))
8bc65d2d
AW
333 (define (maybe-fold-binary-branch! label name arg0 arg1)
334 (let* ((folder (hashq-ref *branch-folders* name)))
335 (when folder
336 (call-with-values (lambda () (lookup-pre-type typev label arg0))
337 (lambda (type0 min0 max0)
338 (call-with-values (lambda () (lookup-pre-type typev label arg1))
339 (lambda (type1 min1 max1)
340 (call-with-values (lambda ()
341 (folder type0 min0 max0 type1 min1 max1))
342 (lambda (f? v)
ec412d75
AW
343 (bitvector-set! folded? (label->idx label) f?)
344 (vector-set! folded-values (label->idx label) v))))))))))
8bc65d2d
AW
345 (define (visit-cont cont)
346 (match cont
347 (($ $cont label ($ $kargs _ _ body))
348 (visit-term body label))
349 (($ $cont label ($ $kclause arity body alternate))
350 (visit-cont body)
351 (visit-cont alternate))
352 (_ #f)))
353 (define (visit-term term label)
354 (match term
355 (($ $letk conts body)
356 (for-each visit-cont conts)
357 (visit-term body label))
8bc65d2d 358 (($ $continue k src ($ $primcall name args))
59258f7c 359 ;; We might be able to fold primcalls that define a value.
8bc65d2d
AW
360 (match (lookup-cont k dfg)
361 (($ $kargs (_) (def))
ec412d75 362 ;(pk 'maybe-fold-value src name args)
9243902a
AW
363 (unless (maybe-fold-value! label name def)
364 (maybe-reduce-primcall! label k src name args)))
365 (_
366 (maybe-reduce-primcall! label k src name args))))
92805e21
AW
367 (($ $continue kf src ($ $branch kt ($ $primcall name args)))
368 ;; We might be able to fold primcalls that branch.
ec412d75 369 ;(pk 'maybe-fold-branch label src name args)
92805e21
AW
370 (match args
371 ((arg)
ec412d75 372 (maybe-fold-unary-branch! label name arg))
92805e21 373 ((arg0 arg1)
ec412d75 374 (maybe-fold-binary-branch! label name arg0 arg1))))
8bc65d2d 375 (_ #f)))
a7ee377d
AW
376 (when typev
377 (match fun
378 (($ $cont kfun ($ $kfun src meta self tail clause))
379 (visit-cont clause))))
384d1ec3 380 (values folded? folded-values reduced-terms)))
8bc65d2d
AW
381
382(define (fold-constants* fun dfg)
383 (match fun
384 (($ $cont min-label ($ $kfun _ _ min-var))
384d1ec3
AW
385 (call-with-values (lambda () (fold-and-reduce fun dfg min-label min-var))
386 (lambda (folded? folded-values reduced-terms)
8bc65d2d
AW
387 (define (label->idx label) (- label min-label))
388 (define (var->idx var) (- var min-var))
389 (define (visit-cont cont)
390 (rewrite-cps-cont cont
391 (($ $cont label ($ $kargs names syms body))
392 (label ($kargs names syms ,(visit-term body label))))
393 (($ $cont label ($ $kclause arity body alternate))
394 (label ($kclause ,arity ,(visit-cont body)
395 ,(and alternate (visit-cont alternate)))))
396 (_ ,cont)))
397 (define (visit-term term label)
398 (rewrite-cps-term term
399 (($ $letk conts body)
400 ($letk ,(map visit-cont conts)
401 ,(visit-term body label)))
8bc65d2d
AW
402 (($ $continue k src (and fun ($ $fun)))
403 ($continue k src ,(visit-fun fun)))
34ff3af9
AW
404 (($ $continue k src ($ $rec names vars funs))
405 ($continue k src ($rec names vars (map visit-fun funs))))
384d1ec3
AW
406 (($ $continue k src (and primcall ($ $primcall name args)))
407 ,(cond
408 ((bitvector-ref folded? (label->idx label))
409 (let ((val (vector-ref folded-values (label->idx label))))
410 ;; Uncomment for debugging.
411 ;; (pk 'folded src primcall val)
412 (let-fresh (k*) (v*)
413 ;; Rely on DCE to elide this expression, if
414 ;; possible.
415 (build-cps-term
416 ($letk ((k* ($kargs (#f) (v*)
417 ($continue k src ($const val)))))
418 ($continue k* src ,primcall))))))
419 (else
420 (or (vector-ref reduced-terms (label->idx label))
421 term))))
92805e21 422 (($ $continue kf src ($ $branch kt ($ $primcall)))
c7b71b1f 423 ,(if (bitvector-ref folded? (label->idx label))
92805e21
AW
424 ;; Folded branch.
425 (let ((val (vector-ref folded-values (label->idx label))))
426 (build-cps-term
427 ($continue (if val kt kf) src ($values ()))))
428 term))
8bc65d2d
AW
429 (_ ,term)))
430 (define (visit-fun fun)
431 (rewrite-cps-exp fun
50fcdfec
AW
432 (($ $fun body)
433 ($fun ,(fold-constants* body dfg)))))
8bc65d2d
AW
434 (rewrite-cps-cont fun
435 (($ $cont kfun ($ $kfun src meta self tail clause))
436 (kfun ($kfun src meta self ,tail ,(visit-cont clause))))))))))
437
438(define (type-fold fun)
439 (let* ((fun (renumber fun))
440 (dfg (compute-dfg fun)))
441 (with-fresh-name-state-from-dfg dfg
442 (fold-constants* fun dfg))))