type-fold.scm 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. ;;; Abstract constant folding on CPS
  2. ;;; Copyright (C) 2014, 2015 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. ;;; Commentary:
  18. ;;;
  19. ;;; This pass uses the abstract interpretation provided by type analysis
  20. ;;; to fold constant values and type predicates. It is most profitably
  21. ;;; run after CSE, to take advantage of scalar replacement.
  22. ;;;
  23. ;;; Code:
  24. (define-module (language cps type-fold)
  25. #:use-module (ice-9 match)
  26. #:use-module (language cps)
  27. #:use-module (language cps utils)
  28. #:use-module (language cps renumber)
  29. #:use-module (language cps types)
  30. #:use-module (language cps with-cps)
  31. #:use-module (language cps intmap)
  32. #:use-module (language cps intset)
  33. #:use-module (system base target)
  34. #:export (type-fold))
  35. ;; Branch folders.
  36. (define &scalar-types
  37. (logior &exact-integer &flonum &char &unspecified &false &true &nil &null))
  38. (define *branch-folders* (make-hash-table))
  39. (define-syntax-rule (define-branch-folder name f)
  40. (hashq-set! *branch-folders* 'name f))
  41. (define-syntax-rule (define-branch-folder-alias to from)
  42. (hashq-set! *branch-folders* 'to (hashq-ref *branch-folders* 'from)))
  43. (define-syntax-rule (define-unary-branch-folder (name arg min max) body ...)
  44. (define-branch-folder name (lambda (arg min max) body ...)))
  45. (define-syntax-rule (define-binary-branch-folder (name arg0 min0 max0
  46. arg1 min1 max1)
  47. body ...)
  48. (define-branch-folder name (lambda (arg0 min0 max0 arg1 min1 max1) body ...)))
  49. (define-syntax-rule (define-unary-type-predicate-folder name &type)
  50. (define-unary-branch-folder (name type min max)
  51. (let ((type* (logand type &type)))
  52. (cond
  53. ((zero? type*) (values #t #f))
  54. ((eqv? type type*) (values #t #t))
  55. (else (values #f #f))))))
  56. ;; All the cases that are in compile-bytecode.
  57. (define-unary-type-predicate-folder pair? &pair)
  58. (define-unary-type-predicate-folder null? &null)
  59. (define-unary-type-predicate-folder symbol? &symbol)
  60. (define-unary-type-predicate-folder variable? &box)
  61. (define-unary-type-predicate-folder vector? &vector)
  62. (define-unary-type-predicate-folder struct? &struct)
  63. (define-unary-type-predicate-folder string? &string)
  64. (define-unary-type-predicate-folder number? &number)
  65. (define-unary-type-predicate-folder char? &char)
  66. (define-binary-branch-folder (eq? type0 min0 max0 type1 min1 max1)
  67. (cond
  68. ((or (zero? (logand type0 type1)) (< max0 min1) (< max1 min0))
  69. (values #t #f))
  70. ((and (eqv? type0 type1)
  71. (eqv? min0 min1 max0 max1)
  72. (zero? (logand type0 (1- type0)))
  73. (not (zero? (logand type0 &scalar-types))))
  74. (values #t #t))
  75. (else
  76. (values #f #f))))
  77. (define-branch-folder-alias eqv? eq?)
  78. (define (compare-ranges type0 min0 max0 type1 min1 max1)
  79. ;; Since &real, &u64, and &f64 are disjoint, we can compare once
  80. ;; against their mask instead of doing three "or" comparisons.
  81. (and (zero? (logand (logior type0 type1) (lognot (logior &real &f64 &u64))))
  82. (cond ((< max0 min1) '<)
  83. ((> min0 max1) '>)
  84. ((= min0 max0 min1 max1) '=)
  85. ((<= max0 min1) '<=)
  86. ((>= min0 max1) '>=)
  87. (else #f))))
  88. (define-binary-branch-folder (< type0 min0 max0 type1 min1 max1)
  89. (case (compare-ranges type0 min0 max0 type1 min1 max1)
  90. ((<) (values #t #t))
  91. ((= >= >) (values #t #f))
  92. (else (values #f #f))))
  93. (define-branch-folder-alias u64-< <)
  94. (define-branch-folder-alias u64-<-scm <)
  95. (define-binary-branch-folder (<= type0 min0 max0 type1 min1 max1)
  96. (case (compare-ranges type0 min0 max0 type1 min1 max1)
  97. ((< <= =) (values #t #t))
  98. ((>) (values #t #f))
  99. (else (values #f #f))))
  100. (define-branch-folder-alias u64-<= <=)
  101. (define-branch-folder-alias u64-<=-scm <=)
  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. (define-branch-folder-alias u64-= =)
  108. (define-branch-folder-alias u64-=-scm =)
  109. (define-binary-branch-folder (>= type0 min0 max0 type1 min1 max1)
  110. (case (compare-ranges type0 min0 max0 type1 min1 max1)
  111. ((> >= =) (values #t #t))
  112. ((<) (values #t #f))
  113. (else (values #f #f))))
  114. (define-branch-folder-alias u64->= >=)
  115. (define-branch-folder-alias u64->=-scm >=)
  116. (define-binary-branch-folder (> type0 min0 max0 type1 min1 max1)
  117. (case (compare-ranges type0 min0 max0 type1 min1 max1)
  118. ((>) (values #t #t))
  119. ((= <= <) (values #t #f))
  120. (else (values #f #f))))
  121. (define-branch-folder-alias u64-> >)
  122. (define-branch-folder-alias u64->-scm >)
  123. (define-binary-branch-folder (logtest type0 min0 max0 type1 min1 max1)
  124. (define (logand-min a b)
  125. (if (< a b 0)
  126. (min a b)
  127. 0))
  128. (define (logand-max a b)
  129. (if (< a b 0)
  130. 0
  131. (max a b)))
  132. (if (and (= min0 max0) (= min1 max1) (eqv? type0 type1 &exact-integer))
  133. (values #t (logtest min0 min1))
  134. (values #f #f)))
  135. ;; Strength reduction.
  136. (define *primcall-reducers* (make-hash-table))
  137. (define-syntax-rule (define-primcall-reducer name f)
  138. (hashq-set! *primcall-reducers* 'name f))
  139. (define-syntax-rule (define-unary-primcall-reducer (name cps k src
  140. arg type min max)
  141. body ...)
  142. (define-primcall-reducer name
  143. (lambda (cps k src arg type min max)
  144. body ...)))
  145. (define-syntax-rule (define-binary-primcall-reducer (name cps k src
  146. arg0 type0 min0 max0
  147. arg1 type1 min1 max1)
  148. body ...)
  149. (define-primcall-reducer name
  150. (lambda (cps k src arg0 type0 min0 max0 arg1 type1 min1 max1)
  151. body ...)))
  152. (define-binary-primcall-reducer (mul cps k src
  153. arg0 type0 min0 max0
  154. arg1 type1 min1 max1)
  155. (define (fail) (with-cps cps #f))
  156. (define (negate arg)
  157. (with-cps cps
  158. ($ (with-cps-constants ((zero 0))
  159. (build-term
  160. ($continue k src ($primcall 'sub (zero arg))))))))
  161. (define (zero)
  162. (with-cps cps
  163. (build-term ($continue k src ($const 0)))))
  164. (define (identity arg)
  165. (with-cps cps
  166. (build-term ($continue k src ($values (arg))))))
  167. (define (double arg)
  168. (with-cps cps
  169. (build-term ($continue k src ($primcall 'add (arg arg))))))
  170. (define (power-of-two constant arg)
  171. (let ((n (let lp ((bits 0) (constant constant))
  172. (if (= constant 1) bits (lp (1+ bits) (ash constant -1))))))
  173. (with-cps cps
  174. ($ (with-cps-constants ((bits n))
  175. (build-term ($continue k src ($primcall 'ash (arg bits)))))))))
  176. (define (mul/constant constant constant-type arg arg-type)
  177. (cond
  178. ((not (or (= constant-type &exact-integer) (= constant-type arg-type)))
  179. (fail))
  180. ((eqv? constant -1)
  181. ;; (* arg -1) -> (- 0 arg)
  182. (negate arg))
  183. ((eqv? constant 0)
  184. ;; (* arg 0) -> 0 if arg is not a flonum or complex
  185. (and (= constant-type &exact-integer)
  186. (zero? (logand arg-type
  187. (lognot (logior &flonum &complex))))
  188. (zero)))
  189. ((eqv? constant 1)
  190. ;; (* arg 1) -> arg
  191. (identity arg))
  192. ((eqv? constant 2)
  193. ;; (* arg 2) -> (+ arg arg)
  194. (double arg))
  195. ((and (= constant-type arg-type &exact-integer)
  196. (positive? constant)
  197. (zero? (logand constant (1- constant))))
  198. ;; (* arg power-of-2) -> (ash arg (log2 power-of-2
  199. (power-of-two constant arg))
  200. (else
  201. (fail))))
  202. (cond
  203. ((logtest (logior type0 type1) (lognot &number)) (fail))
  204. ((= min0 max0) (mul/constant min0 type0 arg1 type1))
  205. ((= min1 max1) (mul/constant min1 type1 arg0 type0))
  206. (else (fail))))
  207. (define-binary-primcall-reducer (logbit? cps k src
  208. arg0 type0 min0 max0
  209. arg1 type1 min1 max1)
  210. (define (convert-to-logtest cps kbool)
  211. (define (compute-mask cps kmask src)
  212. (if (eq? min0 max0)
  213. (with-cps cps
  214. (build-term
  215. ($continue kmask src ($const (ash 1 min0)))))
  216. (with-cps cps
  217. ($ (with-cps-constants ((one 1))
  218. (build-term
  219. ($continue kmask src ($primcall 'ash (one arg0)))))))))
  220. (with-cps cps
  221. (letv mask)
  222. (letk kt ($kargs () ()
  223. ($continue kbool src ($const #t))))
  224. (letk kf ($kargs () ()
  225. ($continue kbool src ($const #f))))
  226. (letk kmask ($kargs (#f) (mask)
  227. ($continue kf src
  228. ($branch kt ($primcall 'logtest (mask arg1))))))
  229. ($ (compute-mask kmask src))))
  230. ;; Hairiness because we are converting from a primcall with unknown
  231. ;; arity to a branching primcall.
  232. (let ((positive-fixnum-bits (- (* (target-word-size) 8) 3)))
  233. (if (and (= type0 &exact-integer)
  234. (<= 0 min0 positive-fixnum-bits)
  235. (<= 0 max0 positive-fixnum-bits))
  236. (match (intmap-ref cps k)
  237. (($ $kreceive arity kargs)
  238. (match arity
  239. (($ $arity (_) () (not #f) () #f)
  240. (with-cps cps
  241. (letv bool)
  242. (let$ body (with-cps-constants ((nil '()))
  243. (build-term
  244. ($continue kargs src ($values (bool nil))))))
  245. (letk kbool ($kargs (#f) (bool) ,body))
  246. ($ (convert-to-logtest kbool))))
  247. (_
  248. (with-cps cps
  249. (letv bool)
  250. (letk kbool ($kargs (#f) (bool)
  251. ($continue k src ($primcall 'values (bool)))))
  252. ($ (convert-to-logtest kbool))))))
  253. (($ $ktail)
  254. (with-cps cps
  255. (letv bool)
  256. (letk kbool ($kargs (#f) (bool)
  257. ($continue k src ($values (bool)))))
  258. ($ (convert-to-logtest kbool)))))
  259. (with-cps cps #f))))
  260. ;;
  261. (define (local-type-fold start end cps)
  262. (define (scalar-value type val)
  263. (cond
  264. ((eqv? type &exact-integer) val)
  265. ((eqv? type &flonum) (exact->inexact val))
  266. ((eqv? type &char) (integer->char val))
  267. ((eqv? type &unspecified) *unspecified*)
  268. ((eqv? type &false) #f)
  269. ((eqv? type &true) #t)
  270. ((eqv? type &nil) #nil)
  271. ((eqv? type &null) '())
  272. (else (error "unhandled type" type val))))
  273. (let ((types (infer-types cps start)))
  274. (define (fold-primcall cps label names vars k src name args def)
  275. (call-with-values (lambda () (lookup-post-type types label def 0))
  276. (lambda (type min max)
  277. (and (not (zero? type))
  278. (zero? (logand type (1- type)))
  279. (zero? (logand type (lognot &scalar-types)))
  280. (eqv? min max)
  281. (let ((val (scalar-value type min)))
  282. ;; (pk 'folded src name args val)
  283. (with-cps cps
  284. (letv v*)
  285. (letk k* ($kargs (#f) (v*)
  286. ($continue k src ($const val))))
  287. ;; Rely on DCE to elide this expression, if
  288. ;; possible.
  289. (setk label
  290. ($kargs names vars
  291. ($continue k* src ($primcall name args))))))))))
  292. (define (reduce-primcall cps label names vars k src name args)
  293. (and=>
  294. (hashq-ref *primcall-reducers* name)
  295. (lambda (reducer)
  296. (match args
  297. ((arg0)
  298. (call-with-values (lambda () (lookup-pre-type types label arg0))
  299. (lambda (type0 min0 max0)
  300. (call-with-values (lambda ()
  301. (reducer cps k src arg0 type0 min0 max0))
  302. (lambda (cps term)
  303. (and term
  304. (with-cps cps
  305. (setk label ($kargs names vars ,term)))))))))
  306. ((arg0 arg1)
  307. (call-with-values (lambda () (lookup-pre-type types label arg0))
  308. (lambda (type0 min0 max0)
  309. (call-with-values (lambda () (lookup-pre-type types label arg1))
  310. (lambda (type1 min1 max1)
  311. (call-with-values (lambda ()
  312. (reducer cps k src arg0 type0 min0 max0
  313. arg1 type1 min1 max1))
  314. (lambda (cps term)
  315. (and term
  316. (with-cps cps
  317. (setk label ($kargs names vars ,term)))))))))))
  318. (_ #f)))))
  319. (define (fold-unary-branch cps label names vars kf kt src name arg)
  320. (and=>
  321. (hashq-ref *branch-folders* name)
  322. (lambda (folder)
  323. (call-with-values (lambda () (lookup-pre-type types label arg))
  324. (lambda (type min max)
  325. (call-with-values (lambda () (folder type min max))
  326. (lambda (f? v)
  327. ;; (when f? (pk 'folded-unary-branch label name arg v))
  328. (and f?
  329. (with-cps cps
  330. (setk label
  331. ($kargs names vars
  332. ($continue (if v kt kf) src
  333. ($values ())))))))))))))
  334. (define (fold-binary-branch cps label names vars kf kt src name arg0 arg1)
  335. (and=>
  336. (hashq-ref *branch-folders* name)
  337. (lambda (folder)
  338. (call-with-values (lambda () (lookup-pre-type types label arg0))
  339. (lambda (type0 min0 max0)
  340. (call-with-values (lambda () (lookup-pre-type types label arg1))
  341. (lambda (type1 min1 max1)
  342. (call-with-values (lambda ()
  343. (folder type0 min0 max0 type1 min1 max1))
  344. (lambda (f? v)
  345. ;; (when f? (pk 'folded-binary-branch label name arg0 arg1 v))
  346. (and f?
  347. (with-cps cps
  348. (setk label
  349. ($kargs names vars
  350. ($continue (if v kt kf) src
  351. ($values ())))))))))))))))
  352. (define (visit-expression cps label names vars k src exp)
  353. (match exp
  354. (($ $primcall name args)
  355. ;; We might be able to fold primcalls that define a value.
  356. (match (intmap-ref cps k)
  357. (($ $kargs (_) (def))
  358. (or (fold-primcall cps label names vars k src name args def)
  359. (reduce-primcall cps label names vars k src name args)
  360. cps))
  361. (_
  362. (or (reduce-primcall cps label names vars k src name args)
  363. cps))))
  364. (($ $branch kt ($ $primcall name args))
  365. ;; We might be able to fold primcalls that branch.
  366. (match args
  367. ((x)
  368. (or (fold-unary-branch cps label names vars k kt src name x)
  369. cps))
  370. ((x y)
  371. (or (fold-binary-branch cps label names vars k kt src name x y)
  372. cps))))
  373. (($ $branch kt ($ $values (arg)))
  374. ;; We might be able to fold branches on values.
  375. (call-with-values (lambda () (lookup-pre-type types label arg))
  376. (lambda (type min max)
  377. (cond
  378. ((zero? (logand type (logior &false &nil)))
  379. (with-cps cps
  380. (setk label
  381. ($kargs names vars ($continue kt src ($values ()))))))
  382. ((zero? (logand type (lognot (logior &false &nil))))
  383. (with-cps cps
  384. (setk label
  385. ($kargs names vars ($continue k src ($values ()))))))
  386. (else cps)))))
  387. (_ cps)))
  388. (let lp ((label start) (cps cps))
  389. (if (<= label end)
  390. (lp (1+ label)
  391. (match (intmap-ref cps label)
  392. (($ $kargs names vars ($ $continue k src exp))
  393. (visit-expression cps label names vars k src exp))
  394. (_ cps)))
  395. cps))))
  396. (define (fold-functions-in-renumbered-program f conts seed)
  397. (let* ((conts (persistent-intmap conts))
  398. (end (1+ (intmap-prev conts))))
  399. (let lp ((label 0) (seed seed))
  400. (if (eqv? label end)
  401. seed
  402. (match (intmap-ref conts label)
  403. (($ $kfun src meta self tail clause)
  404. (lp (1+ tail) (f label tail seed))))))))
  405. (define (type-fold conts)
  406. ;; Type analysis wants a program whose labels are sorted.
  407. (let ((conts (renumber conts)))
  408. (with-fresh-name-state conts
  409. (persistent-intmap
  410. (fold-functions-in-renumbered-program local-type-fold conts conts)))))