canonical-types.scm 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. ;;; WebAssembly VM
  2. ;;; Copyright (C) 2023 David Thompson <dave@spritely.institute>
  3. ;;;
  4. ;;; Licensed under the Apache License, Version 2.0 (the "License");
  5. ;;; you may not use this file except in compliance with the License.
  6. ;;; You may obtain a copy of the License at
  7. ;;;
  8. ;;; http://www.apache.org/licenses/LICENSE-2.0
  9. ;;;
  10. ;;; Unless required by applicable law or agreed to in writing, software
  11. ;;; distributed under the License is distributed on an "AS IS" BASIS,
  12. ;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. ;;; See the License for the specific language governing permissions and
  14. ;;; limitations under the License.
  15. ;;; Commentary:
  16. ;;;
  17. ;;; Reference type canonicalization.
  18. ;;;
  19. ;;; Code:
  20. (define-module (wasm canonical-types)
  21. #:use-module (ice-9 match)
  22. #:use-module (srfi srfi-1)
  23. #:use-module (wasm types)
  24. #:export (canonicalize-types!
  25. canonicalize-type!))
  26. ;; A bit of global state for ref type canonicalization across modules.
  27. (define *canonical-groups* (make-hash-table))
  28. (define (canonicalize-types! types)
  29. ;; Create a vector big enough to hold all of the resulting types.
  30. (let ((canonical-vec (make-vector
  31. (fold (lambda (type sum)
  32. (match type
  33. (($ <rec-group> types)
  34. (+ sum (length types)))
  35. (_ (+ sum 1))))
  36. 0 types))))
  37. (define (visit-group types group-start)
  38. ;; Rolling up a type replaces indices outside of the type group
  39. ;; with a canonical type descriptor and indices inside of the
  40. ;; type group with relative indices. This creates a new type
  41. ;; that can be equal? tested against a type from another module.
  42. (define (roll-up type group-start)
  43. (match type
  44. ((? symbol?) type)
  45. ((? exact-integer? idx)
  46. (if (< type group-start)
  47. `(outer ,(vector-ref canonical-vec idx))
  48. (- idx group-start)))
  49. (($ <ref-type> nullable? heap-type)
  50. (make-ref-type nullable? (roll-up heap-type group-start)))
  51. (($ <func-sig> params results)
  52. (make-func-sig (map (match-lambda
  53. (($ <param> _ type)
  54. (make-param #f (roll-up type group-start))))
  55. params)
  56. (map (lambda (type)
  57. (roll-up type group-start))
  58. results)))
  59. (($ <struct-type> fields)
  60. (make-struct-type
  61. (map (match-lambda
  62. (($ <field> _ mutable? type)
  63. (make-field #f mutable? (roll-up type group-start))))
  64. fields)))
  65. (($ <array-type> mutable? type)
  66. (make-array-type mutable? (roll-up type group-start)))
  67. (($ <sub-type> final? supers type)
  68. (make-sub-type final?
  69. (map (lambda (super)
  70. (roll-up super group-start))
  71. supers)
  72. (roll-up type group-start)))))
  73. ;; If a type group with identical structure has already been
  74. ;; canonicalized, return the cached type descriptors. Otherwise,
  75. ;; generate new ones, cache them, and return them.
  76. (let ((types* (map (match-lambda
  77. (($ <type> id type)
  78. (roll-up type group-start)))
  79. types)))
  80. (match (hash-ref *canonical-groups* types*)
  81. ;; Cache hit: Just copy 'em over.
  82. ((? vector? cached-group)
  83. (do ((i 0 (+ i 1)))
  84. ((= i (vector-length cached-group)))
  85. (vector-set! canonical-vec (+ group-start i)
  86. (vector-ref cached-group i)))
  87. (+ group-start (vector-length cached-group)))
  88. ;; Cache miss: Generate and cache new descriptors.
  89. (#f
  90. (let ((group-vec (make-vector (length types))))
  91. (let loop ((types types*)
  92. (i 0))
  93. ;; Unrolling a type replaces relative recursive type
  94. ;; indices with canonical type references.
  95. (define (unroll type)
  96. (match type
  97. ((? symbol?) type)
  98. ;; Types may have recursive references to other
  99. ;; types within the same group, so we're lazy about
  100. ;; it.
  101. ((? exact-integer? idx)
  102. (delay (vector-ref group-vec idx)))
  103. ;; Types from outside the group are already
  104. ;; unrolled so recursion stops.
  105. (('outer type) type)
  106. (($ <ref-type> nullable? heap-type)
  107. (make-ref-type nullable? (unroll heap-type)))
  108. (($ <func-sig> params results)
  109. (make-func-sig (map (match-lambda
  110. (($ <param> _ type)
  111. (make-param #f (unroll type))))
  112. params)
  113. (map unroll results)))
  114. (($ <struct-type> fields)
  115. (make-struct-type
  116. (map (match-lambda
  117. (($ <field> _ mutable? type)
  118. (make-field #f mutable? (unroll type))))
  119. fields)))
  120. (($ <array-type> mutable? type)
  121. (make-array-type mutable? (unroll type)))
  122. (($ <sub-type> final? supers type)
  123. (make-sub-type final?
  124. (map unroll supers)
  125. (unroll type)))))
  126. (match types
  127. (()
  128. (hash-set! *canonical-groups* types* group-vec)
  129. (+ group-start i))
  130. ((type . rest)
  131. (let ((type* (unroll type)))
  132. (vector-set! group-vec i type*)
  133. (vector-set! canonical-vec (+ group-start i) type*)
  134. (loop rest (+ i 1)))))))))))
  135. ;; Visit all the type groups and canonicalize them. A type that
  136. ;; is not in a recursive type group is treated as being in a group
  137. ;; of one.
  138. (let loop ((groups types)
  139. (i 0))
  140. (match groups
  141. (() #t)
  142. ((($ <rec-group> (types ...)) . rest)
  143. (loop rest (visit-group types i)))
  144. (((? type? type) . rest)
  145. (loop rest (visit-group (list type) i)))))
  146. ;; Generate a new type list using the canonical types.
  147. (let loop ((types (append-map (match-lambda
  148. (($ <rec-group> types) types)
  149. ((? type? type) (list type)))
  150. types))
  151. (i 0))
  152. (match types
  153. (() '())
  154. ((($ <type> id type) . rest)
  155. (cons (make-type id (vector-ref canonical-vec i))
  156. (loop rest (+ i 1))))))))
  157. ;; Convenience procedure for canonicalizing individual types outside
  158. ;; of a module context. Useful in the stack effect and reflection
  159. ;; modules, for example.
  160. (define (canonicalize-type! type)
  161. (match (canonicalize-types! (list (make-type #f type)))
  162. ((($ <type> _ val)) val)))