dce.scm 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. ;;; Continuation-passing style (CPS) intermediate language (IL)
  2. ;; Copyright (C) 2013-2021, 2023 Free Software Foundation, Inc.
  3. ;;;; This library is free software; you can redistribute it and/or
  4. ;;;; modify it under the terms of the GNU Lesser General Public
  5. ;;;; License as published by the Free Software Foundation; either
  6. ;;;; version 3 of the License, or (at your option) any later version.
  7. ;;;;
  8. ;;;; This library is distributed in the hope that it will be useful,
  9. ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. ;;;; Lesser General Public License for more details.
  12. ;;;;
  13. ;;;; You should have received a copy of the GNU Lesser General Public
  14. ;;;; License along with this library; if not, write to the Free Software
  15. ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  16. ;;; Commentary:
  17. ;;;
  18. ;;; This pass kills dead expressions: code that has no side effects, and
  19. ;;; whose value is unused. It does so by marking all live values, and
  20. ;;; then discarding other values as dead. This happens recursively
  21. ;;; through procedures, so it should be possible to elide dead
  22. ;;; procedures as well.
  23. ;;;
  24. ;;; Code:
  25. (define-module (language cps dce)
  26. #:use-module (ice-9 match)
  27. #:use-module (srfi srfi-1)
  28. #:use-module (language cps)
  29. #:use-module (language cps effects-analysis)
  30. #:use-module (language cps renumber)
  31. #:use-module (language cps type-checks)
  32. #:use-module (language cps utils)
  33. #:use-module (language cps intmap)
  34. #:use-module (language cps intset)
  35. #:export (eliminate-dead-code))
  36. (define (fold-local-conts proc conts label seed)
  37. (match (intmap-ref conts label)
  38. (($ $kfun src meta self tail clause)
  39. (let lp ((label label) (seed seed))
  40. (if (<= label tail)
  41. (lp (1+ label) (proc label (intmap-ref conts label) seed))
  42. seed)))))
  43. (define (postorder-fold-local-conts2 proc conts label seed0 seed1)
  44. (match (intmap-ref conts label)
  45. (($ $kfun src meta self tail clause)
  46. (let ((start label))
  47. (let lp ((label tail) (seed0 seed0) (seed1 seed1))
  48. (if (<= start label)
  49. (let ((cont (intmap-ref conts label)))
  50. (call-with-values (lambda () (proc label cont seed0 seed1))
  51. (lambda (seed0 seed1)
  52. (lp (1- label) seed0 seed1))))
  53. (values seed0 seed1)))))))
  54. (define (compute-known-allocations conts effects)
  55. "Compute the variables bound in CONTS that have known allocation
  56. sites."
  57. ;; Compute the set of conts that are called with freshly allocated
  58. ;; values, and subtract from that set the conts that might be called
  59. ;; with values with unknown allocation sites. Then convert that set
  60. ;; of conts into a set of bound variables.
  61. (call-with-values
  62. (lambda ()
  63. (intmap-fold (lambda (label cont known unknown)
  64. ;; Note that we only need to add labels to the
  65. ;; known/unknown sets if the labels can bind
  66. ;; values. So there's no need to add tail,
  67. ;; clause, branch alternate, or prompt handler
  68. ;; labels, as they bind no values.
  69. (match cont
  70. (($ $kargs _ _ ($ $continue k))
  71. (let ((fx (intmap-ref effects label)))
  72. (if (and (not (causes-all-effects? fx))
  73. (causes-effect? fx &allocation))
  74. (values (intset-add! known k) unknown)
  75. (values known (intset-add! unknown k)))))
  76. (($ $kargs _ _ (or ($ $branch) ($ $switch)
  77. ($ $prompt) ($ $throw)))
  78. ;; Branches, switches, and prompts pass no
  79. ;; values to their continuations, and throw
  80. ;; terms don't continue at all.
  81. (values known unknown))
  82. (($ $kreceive arity kargs)
  83. (values known (intset-add! unknown kargs)))
  84. (($ $kfun src meta self tail entry)
  85. (values known
  86. (if entry
  87. (intset-add! unknown entry)
  88. unknown)))
  89. (($ $kclause arity body alt)
  90. (values known (intset-add! unknown body)))
  91. (($ $ktail)
  92. (values known unknown))))
  93. conts
  94. empty-intset
  95. empty-intset))
  96. (lambda (known unknown)
  97. (persistent-intset
  98. (intset-fold (lambda (label vars)
  99. (match (intmap-ref conts label)
  100. (($ $kargs (_) (var)) (intset-add! vars var))
  101. (_ vars)))
  102. (intset-subtract (persistent-intset known)
  103. (persistent-intset unknown))
  104. empty-intset)))))
  105. (define (compute-live-code conts)
  106. (let* ((effects (compute-effects/elide-type-checks conts))
  107. (known-allocations (compute-known-allocations conts effects)))
  108. (define (adjoin-var var set)
  109. (intset-add set var))
  110. (define (adjoin-vars vars set)
  111. (match vars
  112. (() set)
  113. ((var . vars) (adjoin-vars vars (adjoin-var var set)))))
  114. (define (var-live? var live-vars)
  115. (intset-ref live-vars var))
  116. (define (any-var-live? vars live-vars)
  117. (match vars
  118. (() #f)
  119. ((var . vars)
  120. (or (var-live? var live-vars)
  121. (any-var-live? vars live-vars)))))
  122. (define (cont-defs k)
  123. (match (intmap-ref conts k)
  124. (($ $kargs _ vars) vars)
  125. (_ #f)))
  126. (define (visit-live-exp label k exp live-labels live-vars)
  127. (match exp
  128. ((or ($ $const) ($ $prim))
  129. (values live-labels live-vars))
  130. (($ $fun body)
  131. (values (intset-add live-labels body) live-vars))
  132. (($ $const-fun body)
  133. (values (intset-add live-labels body) live-vars))
  134. (($ $code body)
  135. (values (intset-add live-labels body) live-vars))
  136. (($ $rec names vars (($ $fun kfuns) ...))
  137. (let lp ((vars vars) (kfuns kfuns)
  138. (live-labels live-labels) (live-vars live-vars))
  139. (match (vector vars kfuns)
  140. (#(() ()) (values live-labels live-vars))
  141. (#((var . vars) (kfun . kfuns))
  142. (lp vars kfuns
  143. (if (var-live? var live-vars)
  144. (intset-add live-labels kfun)
  145. live-labels)
  146. live-vars)))))
  147. (($ $call proc args)
  148. (values live-labels (adjoin-vars args (adjoin-var proc live-vars))))
  149. (($ $callk kfun proc args)
  150. (values (intset-add live-labels kfun)
  151. (adjoin-vars args (if proc
  152. (adjoin-var proc live-vars)
  153. live-vars))))
  154. (($ $calli args callee)
  155. (values live-labels (adjoin-var callee (adjoin-vars args live-vars))))
  156. (($ $primcall name param args)
  157. (values live-labels (adjoin-vars args live-vars)))
  158. (($ $values args)
  159. (values live-labels
  160. (match (cont-defs k)
  161. (#f (adjoin-vars args live-vars))
  162. (defs (fold (lambda (use def live-vars)
  163. (if (var-live? def live-vars)
  164. (adjoin-var use live-vars)
  165. live-vars))
  166. live-vars args defs)))))))
  167. (define (visit-exp label k exp live-labels live-vars)
  168. (cond
  169. ((intset-ref live-labels label)
  170. ;; Expression live already.
  171. (visit-live-exp label k exp live-labels live-vars))
  172. ((let ((defs (cont-defs k))
  173. (fx (intmap-ref effects label)))
  174. (or
  175. ;; No defs; perhaps continuation is $ktail.
  176. (not defs)
  177. ;; Do we have a live def?
  178. (any-var-live? defs live-vars)
  179. ;; Does this expression cause all effects? If so, it's
  180. ;; definitely live.
  181. (causes-all-effects? fx)
  182. ;; Does it cause a type check, but we weren't able to prove
  183. ;; that the types check?
  184. (causes-effect? fx &type-check)
  185. ;; We might have a setter. If the object being assigned to
  186. ;; is live or was not created by us, then this expression is
  187. ;; live. Otherwise the value is still dead.
  188. (and (causes-effect? fx &write)
  189. (match exp
  190. (($ $primcall
  191. (or 'scm-set! 'scm-set!/tag 'scm-set!/immediate
  192. 'word-set! 'word-set!/immediate
  193. 'vector-set! 'vector-set!/immediate
  194. 'set-car! 'set-cdr!
  195. 'box-set!
  196. 'struct-set!
  197. 'closure-set!)
  198. _ (obj . _))
  199. (or (var-live? obj live-vars)
  200. (not (intset-ref known-allocations obj))))
  201. (_ #t)))))
  202. ;; Mark expression as live and visit.
  203. (visit-live-exp label k exp (intset-add live-labels label) live-vars))
  204. (else
  205. ;; Still dead.
  206. (values live-labels live-vars))))
  207. ;; Note, this is for $branch or $switch.
  208. (define (visit-branch label kf kt* args live-labels live-vars)
  209. (define (next-live-term k)
  210. ;; FIXME: For a chain of dead branches, this is quadratic.
  211. (let lp ((seen empty-intset) (k k))
  212. (cond
  213. ((intset-ref live-labels k) k)
  214. ((intset-ref seen k) k)
  215. (else
  216. (match (intmap-ref conts k)
  217. (($ $kargs _ _ ($ $continue k*))
  218. (lp (intset-add seen k) k*))
  219. (_ k))))))
  220. (define (distinct-continuations?)
  221. (let ((kf' (next-live-term kf)))
  222. (let lp ((kt* kt*))
  223. (match kt*
  224. (() #f)
  225. ((kt . kt*)
  226. (cond
  227. ((or (eqv? kf kt)
  228. (eqv? kf' (next-live-term kt)))
  229. (lp kt*))
  230. (else #t)))))))
  231. (cond
  232. ((intset-ref live-labels label)
  233. ;; Branch live already.
  234. (values live-labels (adjoin-vars args live-vars)))
  235. ((or (causes-effect? (intmap-ref effects label) &type-check)
  236. (distinct-continuations?))
  237. ;; The branch is live if its continuations are not the same, or
  238. ;; if the branch itself causes type checks.
  239. (values (intset-add live-labels label)
  240. (adjoin-vars args live-vars)))
  241. (else
  242. ;; Still dead.
  243. (values live-labels live-vars))))
  244. (define (visit-fun label live-labels live-vars)
  245. ;; Visit uses before definitions.
  246. (postorder-fold-local-conts2
  247. (lambda (label cont live-labels live-vars)
  248. (match cont
  249. (($ $kargs _ _ ($ $continue k src exp))
  250. (visit-exp label k exp live-labels live-vars))
  251. (($ $kargs _ _ ($ $branch kf kt src op param args))
  252. (visit-branch label kf (list kt) args live-labels live-vars))
  253. (($ $kargs _ _ ($ $switch kf kt* src arg))
  254. (visit-branch label kf kt* (list arg) live-labels live-vars))
  255. (($ $kargs _ _ ($ $prompt k kh src escape? tag))
  256. ;; Prompts need special elision passes that would contify
  257. ;; aborts and remove corresponding "unwind" primcalls.
  258. (values (intset-add live-labels label)
  259. (adjoin-var tag live-vars)))
  260. (($ $kargs _ _ ($ $throw src op param args))
  261. ;; A reachable "throw" is always live.
  262. (values (intset-add live-labels label)
  263. (adjoin-vars args live-vars)))
  264. (($ $kreceive arity kargs)
  265. (values live-labels live-vars))
  266. (($ $kclause arity kargs kalt)
  267. (values live-labels (adjoin-vars (cont-defs kargs) live-vars)))
  268. (($ $kfun src meta self tail entry)
  269. (values live-labels
  270. (adjoin-vars
  271. (or (and entry (cont-defs entry)) '())
  272. (if self (adjoin-var self live-vars) live-vars))))
  273. (($ $ktail)
  274. (values live-labels live-vars))))
  275. conts label live-labels live-vars))
  276. (fixpoint (lambda (live-labels live-vars)
  277. (let lp ((label 0)
  278. (live-labels live-labels)
  279. (live-vars live-vars))
  280. (match (intset-next live-labels label)
  281. (#f (values live-labels live-vars))
  282. (label
  283. (call-with-values
  284. (lambda ()
  285. (match (intmap-ref conts label)
  286. (($ $kfun)
  287. (visit-fun label live-labels live-vars))
  288. (_ (values live-labels live-vars))))
  289. (lambda (live-labels live-vars)
  290. (lp (1+ label) live-labels live-vars)))))))
  291. (intset 0)
  292. empty-intset)))
  293. (define-syntax adjoin-conts
  294. (syntax-rules ()
  295. ((_ (exp ...) clause ...)
  296. (let ((cps (exp ...)))
  297. (adjoin-conts cps clause ...)))
  298. ((_ cps (label cont) clause ...)
  299. (adjoin-conts (intmap-add! cps label (build-cont cont))
  300. clause ...))
  301. ((_ cps)
  302. cps)))
  303. (define (process-eliminations conts live-labels live-vars)
  304. (define (label-live? label)
  305. (intset-ref live-labels label))
  306. (define (value-live? var)
  307. (intset-ref live-vars var))
  308. (define (make-adaptor k src defs)
  309. (let* ((names (map (lambda (_) 'tmp) defs))
  310. (vars (map (lambda (_) (fresh-var)) defs))
  311. (live (filter-map (lambda (def var)
  312. (and (value-live? def) var))
  313. defs vars)))
  314. (build-cont
  315. ($kargs names vars
  316. ($continue k src ($values live))))))
  317. (define (visit-term label term cps)
  318. (match term
  319. (($ $continue k src exp)
  320. (if (label-live? label)
  321. (match exp
  322. (($ $fun body)
  323. (values cps
  324. term))
  325. (($ $const-fun body)
  326. (values cps
  327. term))
  328. (($ $rec names vars funs)
  329. (match (filter-map (lambda (name var fun)
  330. (and (value-live? var)
  331. (list name var fun)))
  332. names vars funs)
  333. (()
  334. (values cps
  335. (build-term ($continue k src ($values ())))))
  336. (((names vars funs) ...)
  337. (values cps
  338. (build-term ($continue k src
  339. ($rec names vars funs)))))))
  340. (_
  341. (match (intmap-ref conts k)
  342. (($ $kargs ())
  343. (values cps term))
  344. (($ $kargs names ((? value-live?) ...))
  345. (values cps term))
  346. (($ $kargs names vars)
  347. (match exp
  348. (($ $values args)
  349. (let ((args (filter-map (lambda (use def)
  350. (and (value-live? def) use))
  351. args vars)))
  352. (values cps
  353. (build-term
  354. ($continue k src ($values args))))))
  355. (_
  356. (let-fresh (adapt) ()
  357. (values (adjoin-conts cps
  358. (adapt ,(make-adaptor k src vars)))
  359. (build-term
  360. ($continue adapt src ,exp)))))))
  361. (_
  362. (values cps term)))))
  363. (values cps
  364. (build-term
  365. ($continue k src ($values ()))))))
  366. (($ $branch kf kt src op param args)
  367. (if (label-live? label)
  368. (values cps term)
  369. ;; Dead branches continue to the same continuation
  370. ;; (eventually).
  371. (values cps (build-term ($continue kf src ($values ()))))))
  372. (($ $switch kf kt* src arg)
  373. ;; Same as in $branch case.
  374. (if (label-live? label)
  375. (values cps term)
  376. (values cps (build-term ($continue kf src ($values ()))))))
  377. (($ $prompt)
  378. (values cps term))
  379. (($ $throw)
  380. (values cps term))))
  381. (define (visit-cont label cont cps)
  382. (match cont
  383. (($ $kargs names vars term)
  384. (match (filter-map (lambda (name var)
  385. (and (value-live? var)
  386. (cons name var)))
  387. names vars)
  388. (((names . vars) ...)
  389. (call-with-values (lambda () (visit-term label term cps))
  390. (lambda (cps term)
  391. (adjoin-conts cps
  392. (label ($kargs names vars ,term))))))))
  393. (($ $kreceive ($ $arity req () rest () #f) kargs)
  394. (let ((defs (match (intmap-ref conts kargs)
  395. (($ $kargs names vars) vars))))
  396. (if (and-map value-live? defs)
  397. (adjoin-conts cps (label ,cont))
  398. (let-fresh (adapt) ()
  399. (adjoin-conts cps
  400. (adapt ,(make-adaptor kargs #f defs))
  401. (label ($kreceive req rest adapt)))))))
  402. (_
  403. (adjoin-conts cps (label ,cont)))))
  404. (with-fresh-name-state conts
  405. (persistent-intmap
  406. (intmap-fold (lambda (label cont cps)
  407. (match cont
  408. (($ $kfun)
  409. (if (label-live? label)
  410. (fold-local-conts visit-cont conts label cps)
  411. cps))
  412. (_ cps)))
  413. conts
  414. empty-intmap))))
  415. (define (eliminate-dead-code conts)
  416. ;; We work on a renumbered program so that we can easily visit uses
  417. ;; before definitions just by visiting higher-numbered labels before
  418. ;; lower-numbered labels. Renumbering is also a precondition for type
  419. ;; inference.
  420. (let ((conts (renumber conts)))
  421. (call-with-values (lambda () (compute-live-code conts))
  422. (lambda (live-labels live-vars)
  423. (process-eliminations conts live-labels live-vars)))))
  424. ;;; Local Variables:
  425. ;;; eval: (put 'adjoin-conts 'scheme-indent-function 1)
  426. ;;; End: