dump.scm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. ;;; Continuation-passing style (CPS) intermediate language (IL)
  2. ;; Copyright (C) 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. ;;; Helper facilities for working with CPS.
  19. ;;;
  20. ;;; Code:
  21. (define-module (language cps dump)
  22. #:use-module (ice-9 format)
  23. #:use-module (ice-9 match)
  24. #:use-module (srfi srfi-1)
  25. #:use-module (language cps)
  26. #:use-module (language cps intset)
  27. #:use-module (language cps intmap)
  28. #:use-module (language cps graphs)
  29. #:use-module (language cps utils)
  30. #:export (dump))
  31. ;; ideas: unused vars print as _
  32. ;; print all labels
  33. ;; call bb headers with values
  34. ;; annotate blocks with available bindings? live bindings?
  35. ;; how to print calls...
  36. ;; dot graph
  37. (define (cont-successors cont)
  38. (match cont
  39. (($ $kargs _ _ term)
  40. (match term
  41. (($ $continue k) (list k))
  42. (($ $branch kf kt) (list kf kt))
  43. (($ $switch kf kt*) (cons kf kt*))
  44. (($ $prompt k kh) (list k kh))
  45. (($ $throw) '())))
  46. (($ $kclause _ kbody kalternate)
  47. (if kalternate
  48. (list kbody kalternate)
  49. (list kbody)))
  50. (($ $kfun src meta self ktail kentry)
  51. (list ktail kentry))
  52. (($ $kreceive arity kargs) (list kargs))
  53. (($ $ktail) '())))
  54. (define (compute-block-entries cps kfun body all-labels?)
  55. (if all-labels?
  56. body
  57. (let ((preds (compute-predecessors cps kfun #:labels body)))
  58. ;; Conts whose predecessor count is not 1 start blocks.
  59. (define (add-entry label blocks)
  60. (match (intmap-ref preds label)
  61. ((_) blocks)
  62. (_ (intset-add! blocks label))))
  63. ;; Continuations of branches start blocks.
  64. (define (add-exits label blocks)
  65. (fold1 (lambda (succ blocks)
  66. (intset-add! blocks succ))
  67. (match (cont-successors (intmap-ref cps label))
  68. ((_) '())
  69. (succs succs))
  70. blocks))
  71. (persistent-intset
  72. (intset-fold
  73. (lambda (label blocks)
  74. (add-exits label (add-entry label blocks)))
  75. body
  76. empty-intset)))))
  77. (define (collect-blocks cps entries)
  78. (define (collect-block entry)
  79. (let ((cont (intmap-ref cps entry)))
  80. (acons entry cont
  81. (match (cont-successors (intmap-ref cps entry))
  82. ((succ)
  83. (if (intset-ref entries succ)
  84. '()
  85. (collect-block succ)))
  86. (_ '())))))
  87. (persistent-intmap
  88. (intset-fold
  89. (lambda (start blocks)
  90. (intmap-add! blocks start (collect-block start)))
  91. entries
  92. empty-intmap)))
  93. (define (compute-block-succs blocks)
  94. (intmap-map (lambda (entry conts)
  95. (match conts
  96. (((_ . _) ... (exit . cont))
  97. (fold1 (lambda (succ succs)
  98. (intset-add succs succ))
  99. (cont-successors cont)
  100. empty-intset))))
  101. blocks))
  102. (define (dump-block cps port labelled-conts)
  103. (define (format-label label) (format #f "L~a" label))
  104. (define (format-name name) (if name (symbol->string name) "_"))
  105. (define (format-var var) (format #f "v~a" var))
  106. (define (format-loc src)
  107. (match src
  108. (#f #f)
  109. (#(filename line column)
  110. (format #f "~a:~a:~a"
  111. (or filename "<unknown>")
  112. (1+ line)
  113. column))))
  114. (define (arg-list strs) (string-join strs ", "))
  115. (define (false-if-empty str) (if (string-null? str) #f str))
  116. (define (format-arity arity)
  117. (match arity
  118. (($ $arity req opt rest kw aok?)
  119. (arg-list
  120. `(,@(map format-name req)
  121. ,@(map (lambda (name)
  122. (format #f "[~a]" (format-name name)))
  123. opt)
  124. ,@(map (match-lambda
  125. ((kw name var)
  126. (format #f "~a" kw)))
  127. kw)
  128. ,@(if aok? '("[#:allow-other-keys]") '())
  129. ,@(if rest
  130. (list (string-append (format-name rest) "..."))
  131. '()))))))
  132. (define (format-primcall op param args)
  133. (format #f "~a~@[[~s]~](~a)" op param (arg-list (map format-var args))))
  134. (define (format-exp exp)
  135. (match exp
  136. (($ $const val)
  137. (format #f "const ~s" val))
  138. (($ $prim name)
  139. (format #f "prim ~s" name))
  140. (($ $fun body)
  141. (format #f "fun ~a" (format-label body)))
  142. (($ $rec names syms funs)
  143. (format #f "rec(~a)" (arg-list (map format-exp funs))))
  144. (($ $const-fun label)
  145. (format #f "const-fun ~a" (format-label label)))
  146. (($ $code label)
  147. (format #f "code ~a" (format-label label)))
  148. (($ $call proc args)
  149. (format #f "call ~a(~a)"
  150. (format-var proc) (arg-list (map format-var args))))
  151. (($ $callk k proc args)
  152. (format #f "callk ~a(~a)" (format-label k)
  153. (arg-list
  154. (cons (if proc (format-var proc) "_")
  155. (map format-var args)))))
  156. (($ $calli args callee)
  157. (format #f "calli ~a(~a)"
  158. (format-var callee) (arg-list (map format-var args))))
  159. (($ $primcall name param args)
  160. (format-primcall name param args))
  161. (($ $values args)
  162. (arg-list (map format-var args)))))
  163. (define (dump-annotation ann src)
  164. (when (or ann src)
  165. (format port "~45t ; ~@[~a ~]" ann)
  166. (when src
  167. (let* ((src (format-loc src))
  168. (col (- 80 4 (string-length src))))
  169. (format port "~vt at ~a" col src))))
  170. (newline port))
  171. (define (dump-definition src names vars fmt . args)
  172. (define (take formatter val)
  173. (cond
  174. ((not val) #f)
  175. ((string? val) (false-if-empty val))
  176. ((null? val) #f)
  177. (else (arg-list (map formatter val)))))
  178. (let ((names (take format-name names))
  179. (vars (take format-var vars)))
  180. (format port " ~@[~a := ~]~?" vars fmt args)
  181. (dump-annotation names src)))
  182. (define (dump-statement src ann fmt . args)
  183. (format port " ~?" fmt args)
  184. (dump-annotation (and ann (false-if-empty ann)) src))
  185. (define (dump-block-header label cont)
  186. (match cont
  187. (($ $kargs names vars)
  188. (format port "~a(~a):"
  189. (format-label label)
  190. (arg-list (map format-var vars)))
  191. (dump-annotation (false-if-empty (arg-list (map format-name names)))
  192. #f))
  193. (($ $ktail)
  194. (values))
  195. (($ $kfun src meta self ktail kentry)
  196. (let ((name (assq-ref meta 'name)))
  197. (format port "~a:" (format-label label))
  198. (dump-annotation name src)))
  199. ((or ($ $kreceive) ($ $kclause))
  200. (format port "~a:\n" (format-label label)))))
  201. (define (dump-block-body label cont)
  202. (match cont
  203. (($ $kargs _ _ ($ $continue k src exp))
  204. (match (intmap-ref cps k)
  205. (($ $kargs names vars)
  206. (dump-definition src names vars "~a" (format-exp exp)))
  207. (_
  208. (dump-definition src #f #f "~a" (format-exp exp)))))
  209. (($ $kreceive arity kargs)
  210. (match (intmap-ref cps kargs)
  211. (($ $kargs names vars)
  212. (dump-definition #f names vars
  213. "receive(~a)" (format-arity arity)))))
  214. (($ $ktail)
  215. (values))
  216. (($ $kclause arity kbody #f)
  217. (match (intmap-ref cps kbody)
  218. (($ $kargs names vars)
  219. (dump-definition #f names vars
  220. "receive(~a)" (format-arity arity)))))))
  221. (define (dump-block-exit label cont)
  222. (match cont
  223. (($ $kargs _ _ term)
  224. (match term
  225. (($ $continue k src exp)
  226. (match (intmap-ref cps k)
  227. (($ $ktail)
  228. (match exp
  229. (($ $values vals)
  230. (dump-statement src #f
  231. "return ~a" (arg-list (map format-var vals))))
  232. (_
  233. (dump-statement src #f
  234. "tail ~a" (format-exp exp)))))
  235. (_
  236. (dump-statement src #f
  237. "~a(~a)" (format-label k) (format-exp exp)))))
  238. (($ $branch kf kt src op param args)
  239. (dump-statement src #f
  240. "~a ? ~a() : ~a()"
  241. (format-primcall op param args)
  242. (format-label kt)
  243. (format-label kf)))
  244. (($ $switch kf kt* src arg)
  245. (dump-statement src #f
  246. "[~a]~a() or ~a()"
  247. (arg-list (map format-label kt*))
  248. (format-var arg)
  249. (format-label kf)))
  250. (($ $prompt k kh src escape? tag)
  251. (dump-statement src #f
  252. "~a(prompt(kh:~a,~a tag:~a)"
  253. (format-label k)
  254. (format-label kh)
  255. (if escape? ", escape-only" "")
  256. (format-var tag)))
  257. (($ $throw src op param args)
  258. (dump-statement src #f
  259. "throw ~a" (format-primcall op param args)))))
  260. (($ $kreceive arity kargs)
  261. (dump-statement #f #f
  262. "~a(receive(~a))"
  263. (format-label kargs)
  264. (format-arity arity)))
  265. (($ $kfun src meta self ktail kentry)
  266. (for-each (match-lambda
  267. ((k . v)
  268. (unless (eq? k 'name)
  269. (format port " meta: ~a: ~s\n" k v))))
  270. meta)
  271. ;; (format port " tail: ~a:\n" (format-label ktail))
  272. (when self
  273. (format port " ~a := self\n" (format-var self)))
  274. (format port " ~a(...)\n" (format-label kentry)))
  275. (($ $kclause arity kbody kalt)
  276. (dump-statement #f #f
  277. "~a(receive(~a))~@[or ~a()~]\n"
  278. (format-label kbody)
  279. (format-arity arity)
  280. (and=> kalt format-label)))
  281. (($ $ktail)
  282. (values))))
  283. (match labelled-conts
  284. (((label . cont) . _)
  285. (dump-block-header label cont)))
  286. (let lp ((labelled-conts labelled-conts))
  287. (match labelled-conts
  288. (((label . cont))
  289. (dump-block-exit label cont))
  290. (((label . cont) . labelled-conts)
  291. (dump-block-body label cont)
  292. (lp labelled-conts)))))
  293. (define (dump-function cps port kfun body all-labels?)
  294. (define entries (compute-block-entries cps kfun body all-labels?))
  295. (define blocks (collect-blocks cps entries))
  296. (define block-succs (compute-block-succs blocks))
  297. (define block-order (compute-reverse-post-order block-succs kfun))
  298. (for-each (lambda (entry)
  299. (dump-block cps port (intmap-ref blocks entry)))
  300. block-order)
  301. (values))
  302. (define* (dump cps #:key
  303. (port (current-output-port))
  304. (entry (intmap-next cps))
  305. (all-labels? #f))
  306. (let ((functions (compute-reachable-functions cps entry)))
  307. (intmap-fold (lambda (kfun body)
  308. (unless (eqv? kfun entry) (newline port))
  309. (dump-function cps port kfun body all-labels?))
  310. functions)))