unify-returns.scm 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. ;;; Pass to make all return continuations have the same type
  2. ;;; Copyright (C) 2023 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. ;;; For WebAssembly, we CPS-convert our programs in such a way that we
  20. ;;; end up with an explicit stack; a "return" gets translated to popping
  21. ;;; a function off a stack, then tail-calling it. To put it more
  22. ;;; generally, return continuations are stack-allocated. Return
  23. ;;; continuations consist of data and code. The data can be s64, u64,
  24. ;;; f64, or scm. We use two stacks to represent the data: one for
  25. ;;; numeric values (s64, u64, f64) and one for values managed by the
  26. ;;; garbage collector (scm).
  27. ;;;
  28. ;;; What to do about code, though? What type of stack to use there?
  29. ;;; Well in general when you return from a function you don't know how
  30. ;;; many values the calling function is expecting. So the usual
  31. ;;; protocol is to have the return continuation take multiple values.
  32. ;;; For WebAssembly this will be our $kvarargs calling convention.
  33. ;;;
  34. ;;; However it is possible for some return continuations to be
  35. ;;; "well-known", in the sense that they know all their callers. If
  36. ;;; they can also prove that all callers pass a compatible number of
  37. ;;; arguments (return values), then the return continuation can elide
  38. ;;; the number-of-values check. This is the return-types optimization
  39. ;;; from (language cps return-types), which allows $call to continue to
  40. ;;; $kargs instead of $kreceive.
  41. ;;;
  42. ;;; Bringing it back to WebAssembly, this means that the type for return
  43. ;;; continuation code can be non-uniform in the presence of return-type
  44. ;;; optimization. We could use multiple stacks, but that gets tricky;
  45. ;;; really one starts to pine for the proper call stack which is
  46. ;;; appropriately polymorphic. But until then, this pass undoes a bit
  47. ;;; of return-type optimization by wrapping well-known continuations in
  48. ;;; a $kclause when they are placed on a return stack.
  49. ;;;
  50. ;;; Code:
  51. (define-module (language cps hoot unify-returns)
  52. #:use-module (ice-9 match)
  53. #:use-module (language cps)
  54. #:use-module (language cps intmap)
  55. #:use-module (language cps utils)
  56. #:use-module (language cps with-cps)
  57. #:export (unify-returns))
  58. (define (unify-returns cps)
  59. (define (strip-meta cps k)
  60. (define (strip meta)
  61. (match meta
  62. (() '())
  63. (((k' . v') . meta)
  64. (let ((meta (strip meta)))
  65. (if (eq? k' k)
  66. meta
  67. (acons k' v' meta))))))
  68. (intmap-map (lambda (label cont)
  69. (rewrite-cont cont
  70. (($ $kfun src meta self ktail kentry)
  71. ($kfun src (strip meta) self ktail kentry))
  72. (_ ,cont)))
  73. cps))
  74. (define (maybe-wrap-return-continuation out wrapped kfun failure success)
  75. (match (intmap-ref wrapped kfun (lambda (_) #f))
  76. (#f
  77. (match (intmap-ref cps kfun)
  78. (($ $kfun src meta self ktail kentry)
  79. (match (intmap-ref cps kentry)
  80. (($ $kargs names vars term)
  81. (let* ((self (and self (fresh-var)))
  82. (vars (map (lambda (_) (fresh-var)) vars))
  83. (meta (acons 'elide-arity-check? #t meta)))
  84. (with-cps out
  85. (letk ktail
  86. ($ktail))
  87. (letk kcall
  88. ($kargs names vars
  89. ($continue ktail src
  90. ($callk kfun self vars))))
  91. (letk kclause
  92. ($kclause (names '() #f '() #f) kcall #f))
  93. (letk kwrapped
  94. ($kfun src meta self ktail kclause))
  95. ($ (success (intmap-add wrapped kfun kwrapped) kwrapped)))))
  96. (_ (failure))))))
  97. (kwrapped
  98. (success out wrapped kwrapped))))
  99. (with-fresh-name-state cps
  100. (values
  101. (persistent-intmap
  102. (intmap-fold
  103. (lambda (label cont out wrapped)
  104. (match cont
  105. (($ $kargs names vars ($ $continue k src ($ $code kfun)))
  106. (maybe-wrap-return-continuation
  107. out wrapped kfun
  108. (lambda ()
  109. (values out wrapped))
  110. (lambda (out wrapped kwrapped)
  111. (with-cps out
  112. (setk label
  113. ($kargs names vars
  114. ($continue k src ($code kwrapped))))
  115. (intmap-add wrapped kfun kwrapped)))))
  116. (_ (values out wrapped))))
  117. cps
  118. (strip-meta cps 'elide-arity-check?)
  119. empty-intmap)))))