symbolify.scm 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. ;;; Replace indices with symbolic identifiers
  2. ;;; Copyright (C) 2023 Igalia, S.L.
  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. ;;; Symbolifier. The idea is that when optimizing or transforming a
  18. ;;; WebAssembly module, often you will want to renumber locals,
  19. ;;; functions, or what-not; it's easiest to do that when all references
  20. ;;; are symbolic, and then you can just let (wasm resolve) assign
  21. ;;; indices.
  22. ;;;
  23. ;;; Code:
  24. (define-module (wasm symbolify)
  25. #:use-module (ice-9 match)
  26. #:use-module ((srfi srfi-1) #:select (append-map filter-map))
  27. #:use-module (srfi srfi-11)
  28. #:use-module (wasm types)
  29. #:export (symbolify-wasm))
  30. (define (make-gensym stem names)
  31. (define counter 0)
  32. (define (gensym)
  33. (let ((sym (string->symbol (format #f "~a~a" stem counter))))
  34. (set! counter (1+ counter))
  35. (if (hashq-ref names sym)
  36. (gensym)
  37. sym)))
  38. gensym)
  39. (define (symbolify-defs wasm)
  40. (match wasm
  41. (($ <wasm> id types imports funcs tables memories globals exports start
  42. elems datas tags strings custom)
  43. (define (make-namer stem)
  44. (define known-names (make-hash-table))
  45. (define gensym (make-gensym stem known-names))
  46. (values (lambda (name) (or name (gensym)))
  47. (lambda (name)
  48. (when name
  49. (when (hashq-ref known-names name)
  50. (error "duplicate name!" name))
  51. (hashq-set! known-names name #t)))))
  52. (define-values (type-name add-type-name!) (make-namer "$type"))
  53. (define-values (func-name add-func-name!) (make-namer "$func"))
  54. (define-values (table-name add-table-name!) (make-namer "$table"))
  55. (define-values (memory-name add-memory-name!) (make-namer "$memory"))
  56. (define-values (global-name add-global-name!) (make-namer "$global"))
  57. (define-values (elem-name add-elem-name!) (make-namer "$elem"))
  58. (define-values (data-name add-data-name!) (make-namer "$data"))
  59. (define-values (tag-name add-tag-name!) (make-namer "$tag"))
  60. (for-each (match-lambda
  61. (($ <rec-group> (($ <type> id) ...))
  62. (for-each add-type-name! id))
  63. (($ <type> id) (add-type-name! id)))
  64. types)
  65. (for-each (match-lambda
  66. (($ <import> mod name 'func id kind) (add-func-name! id))
  67. (($ <import> mod name 'table id kind) (add-table-name! id))
  68. (($ <import> mod name 'memory id kind) (add-memory-name! id))
  69. (($ <import> mod name 'global id kind) (add-global-name! id)))
  70. imports)
  71. (for-each (match-lambda (($ <func> id) (add-func-name! id))) funcs)
  72. (for-each (match-lambda (($ <table> id) (add-table-name! id))) tables)
  73. (for-each (match-lambda (($ <memory> id) (add-memory-name! id))) memories)
  74. (for-each (match-lambda (($ <global> id) (add-global-name! id))) globals)
  75. (for-each (match-lambda (($ <elem> id) (add-elem-name! id))) elems)
  76. (for-each (match-lambda (($ <data> id) (add-data-name! id))) datas)
  77. (for-each (match-lambda (($ <tag> id) (add-tag-name! id))) tags)
  78. (define (ensure-local-names type locals)
  79. (match type
  80. (($ <type-use> type-id
  81. ($ <func-sig> (($ <param> param-id param-type) ...)
  82. results))
  83. (match locals
  84. ((($ <local> local-id local-type) ...)
  85. (define-values (local-name add-local-name!)
  86. (make-namer "$_"))
  87. (for-each add-local-name! param-id)
  88. (for-each add-local-name! local-id)
  89. (let* ((params (map make-param
  90. (map local-name param-id)
  91. param-type))
  92. (type (make-type-use type-id
  93. (make-func-sig params results)))
  94. (locals (map make-local (map local-name local-id)
  95. local-type)))
  96. (values type locals)))))))
  97. (let ((types (map (match-lambda
  98. (($ <rec-group> (($ <type> id type) ...))
  99. (make-rec-group
  100. (map make-type (map type-name id) type)))
  101. (($ <type> id type)
  102. (make-type (type-name id) type)))
  103. types))
  104. (imports (map (match-lambda
  105. (($ <import> mod name kind id type)
  106. (let ((id (match kind
  107. ('func (func-name id))
  108. ('table (table-name id))
  109. ('memory (memory-name id))
  110. ('global (global-name id)))))
  111. (make-import mod name kind id type))))
  112. imports))
  113. (funcs (map (match-lambda
  114. (($ <func> id type locals body)
  115. (let-values (((type locals)
  116. (ensure-local-names type locals)))
  117. (make-func (func-name id) type locals body))))
  118. funcs))
  119. (tables (map (match-lambda
  120. (($ <table> id type init)
  121. (make-table (table-name id) type init)))
  122. tables))
  123. (memories (map (match-lambda
  124. (($ <memory> id type)
  125. (make-memory (memory-name id) type)))
  126. memories))
  127. (globals (map (match-lambda
  128. (($ <global> id type init)
  129. (make-global (global-name id) type init)))
  130. globals))
  131. (elems (map (match-lambda
  132. (($ <elem> id mode table type offset inits)
  133. (make-elem (elem-name id) mode table type offset
  134. inits)))
  135. elems))
  136. (datas (map (match-lambda
  137. (($ <data> id mode mem offset init)
  138. (make-data (data-name id) mode mem offset init)))
  139. datas))
  140. (tags (map (match-lambda
  141. (($ <tag> id type)
  142. (make-tag (tag-name id) type)))
  143. tags)))
  144. (make-wasm id types imports funcs tables memories globals exports start
  145. elems datas tags strings custom)))))
  146. (define (symbolify-uses wasm)
  147. (match wasm
  148. (($ <wasm> id types imports funcs tables memories globals exports start
  149. elems datas tags strings custom)
  150. (define (make-namer names)
  151. (define namev (list->vector names))
  152. (lambda (name)
  153. (if (symbol? name)
  154. name
  155. (vector-ref namev name))))
  156. (define (make-namer/imports kind names)
  157. (define imported
  158. (filter-map (match-lambda
  159. (($ <import> mod name kind* id type)
  160. (and (eq? kind kind*)
  161. (or id (error "unexpected id=#f")))))
  162. imports))
  163. (make-namer (append imported names)))
  164. (define type-name
  165. (make-namer
  166. (append-map (match-lambda
  167. (($ <rec-group> (($ <type> id) ...)) id)
  168. (($ <type> id) (list id)))
  169. types)))
  170. (define func-name
  171. (make-namer/imports 'func (match funcs ((($ <func> id) ...) id))))
  172. (define table-name
  173. (make-namer/imports 'table (match tables ((($ <table> id) ...) id))))
  174. (define memory-name
  175. (make-namer/imports 'memory (match memories ((($ <memory> id) ...) id))))
  176. (define global-name
  177. (make-namer/imports 'global (match globals ((($ <global> id) ...) id))))
  178. (define elem-name
  179. (make-namer (match elems ((($ <elem> id) ...) id))))
  180. (define data-name
  181. (make-namer (match datas ((($ <data> id) ...) id))))
  182. (define tag-name
  183. (make-namer (match tags ((($ <tag> id) ...) id))))
  184. (define (struct-field-name struct-type field)
  185. ;; FIXME: Unimplemented.
  186. field)
  187. (define (visit-heap-type type)
  188. (type-name type))
  189. (define (visit-val-type type)
  190. (match type
  191. (($ <ref-type> nullable? ht)
  192. (make-ref-type nullable? (visit-heap-type ht)))
  193. (_ type)))
  194. (define (visit-ref-type type)
  195. (visit-val-type type))
  196. (define (visit-param param)
  197. (match param
  198. (($ <param> id type)
  199. (make-param id (visit-val-type type)))))
  200. (define (visit-field field)
  201. (match field
  202. (($ <field> id mutable? type)
  203. (make-field id mutable? (visit-val-type type)))))
  204. (define (visit-func-sig type)
  205. (match type
  206. (($ <func-sig> params results)
  207. (make-func-sig (map visit-param params)
  208. (map visit-val-type results)))))
  209. (define (visit-base-type type)
  210. (match type
  211. (($ <struct-type> fields)
  212. (make-struct-type (map visit-field fields)))
  213. (($ <array-type> mutable? type)
  214. (make-array-type mutable? (visit-val-type type)))
  215. (_
  216. (visit-func-sig type))))
  217. (define (visit-sub-type type)
  218. (match type
  219. (($ <sub-type> final? supers type)
  220. (make-sub-type final? (map type-name supers)
  221. (visit-base-type type)))
  222. (_ (visit-base-type type))))
  223. (define (visit-type-use type)
  224. (match type
  225. (($ <type-use> id sig)
  226. (make-type-use (and=> id type-name)
  227. (visit-func-sig sig)))))
  228. (define (visit-table-type type)
  229. (match type
  230. (($ <table-type> limits elem-type)
  231. (make-table-type limits (visit-val-type elem-type)))))
  232. (define (visit-global-type type)
  233. (match type
  234. (($ <global-type> mutable? type)
  235. (make-global-type mutable? (visit-val-type type)))))
  236. (define (visit-block-type type)
  237. (match type
  238. (#f #f)
  239. (($ <type-use>) (visit-type-use type))
  240. ((or ($ <ref-type>) (? symbol?)) (visit-val-type type))))
  241. (define (visit-expr* expr local-name)
  242. (define (visit-expr expr labels)
  243. (define (label-name label)
  244. ;; FIXME: We don't yet apply symbolic names to labels.
  245. label)
  246. (define visit-inst
  247. (match-lambda
  248. (((and inst (or 'block 'loop)) label type body)
  249. (let* ((labels (cons label labels)))
  250. `(,inst ,label ,(visit-block-type type)
  251. ,(visit-expr body labels))))
  252. (('if label type consequent alternate)
  253. (let ((labels (cons label labels)))
  254. `(if ,label ,(visit-block-type type)
  255. ,(visit-expr consequent labels)
  256. ,(visit-expr alternate labels))))
  257. (('try label type body catches catch-all)
  258. (let ((labels (cons label labels)))
  259. `(try ,label ,(visit-block-type type)
  260. ,(visit-expr body labels)
  261. ,(map (lambda (body)
  262. (visit-expr body labels))
  263. catches)
  264. ,(and catch-all
  265. (visit-expr catch-all labels)))))
  266. (('try_delegate label type body handler)
  267. (let ((labels (cons label labels)))
  268. `(try_delegate ,label ,(visit-block-type type)
  269. ,(visit-expr body labels)
  270. ,(label-name handler))))
  271. (((and inst (or 'throw 'rethrow)) tag) `(,inst ,(tag-name tag)))
  272. (((and inst (or 'br 'br_if)) label)
  273. `(,inst ,(label-name label)))
  274. (('br_table targets default)
  275. `(br_table ,(map label-name targets) ,(label-name default)))
  276. (((and inst (or 'call 'return_call)) label)
  277. `(,inst ,(func-name label)))
  278. (('call_indirect table type)
  279. `(call_indirect ,(table-name table) ,(visit-type-use type)))
  280. (((and inst (or 'call_ref 'return_call_ref)) type)
  281. `(,inst ,(type-name type)))
  282. (('select types) `(select ,(map visit-val-type types)))
  283. (((and inst (or 'local.get 'local.set 'local.tee)) local)
  284. `(,inst ,(local-name local)))
  285. (((and inst (or 'global.get 'global.set)) global)
  286. `(,inst ,(global-name global)))
  287. (((and inst (or 'table.get 'table.set)) table)
  288. `(,inst ,(table-name table)))
  289. (((and inst (or 'memory.size 'memory.grow)) mem)
  290. `(,inst ,(memory-name mem)))
  291. (('ref.null ht) `(ref.null ,(visit-heap-type ht)))
  292. (('ref.func f) `(ref.func ,(func-name f)))
  293. ;; GC instructions.
  294. (('ref.null ht)
  295. `(ref.null ,(visit-heap-type ht)))
  296. (((and inst (or 'struct.new 'struct.new_default)) type)
  297. `(,inst ,(type-name type)))
  298. (((and inst (or 'struct.get 'struct.get_s 'struct.get_u 'struct.set))
  299. type field)
  300. `(,inst ,(type-name type) ,(struct-field-name type field)))
  301. (((and inst (or 'array.new 'array.new_default)) type)
  302. `(,inst ,(type-name type)))
  303. (('array.new_fixed type len)
  304. `(array.new_fixed ,(type-name type) ,len))
  305. (((and inst (or 'array.new_data 'array.init_data)) type data)
  306. `(,inst ,(type-name type) ,(data-name data)))
  307. (((and inst (or 'array.new_elem 'array.init_elem)) type elem)
  308. `(minst ,(type-name type) ,(elem-name elem)))
  309. (((and inst (or 'array.get 'array.get_s 'array.get_u 'array.set)) type)
  310. `(,inst ,(type-name type)))
  311. (('array.copy dst src)
  312. `(array.copy ,(type-name dst) ,(type-name src)))
  313. (((and inst (or 'ref.cast 'ref.test)) rt)
  314. `(,inst ,(visit-ref-type rt)))
  315. (((and inst (or 'br_on_cast 'br_on_cast_fail)) label rt1 rt2)
  316. `(,inst ,(label-name label)
  317. ,(visit-ref-type rt1) ,(visit-ref-type rt2)))
  318. ;; Stringref instructions.
  319. (('string.const str)
  320. `(string.const ,(if (string? str) str (list-ref strings str))))
  321. (((and inst (or 'string.new_utf8 'string.new_lossy_utf8 'string.new_wtf8
  322. 'string.new_wtf16
  323. 'string.encode_utf8 'string.encode_lossy_utf8
  324. 'string.encode_wtf8 'string.encode_wtf16
  325. 'stringview_wtf8.encode_utf8
  326. 'stringview_wtf8.encode_lossy_utf8
  327. 'stringview_wtf8.encode_wtf8
  328. 'stringview_wtf16.encode))
  329. mem)
  330. `(,inst ,(memory-name mem)))
  331. ;; Misc instructions.
  332. (('memory.init data mem)
  333. `(memory.init ,(data-name data) ,(memory-name mem)))
  334. (('data.drop data)
  335. `(data.drop ,(data-name data)))
  336. (('memory.copy dst src)
  337. `(memory.copy ,(memory-name dst) ,(memory-name src)))
  338. (('memory.fill mem)
  339. `(memory.fill ,(memory-name mem)))
  340. (('table.init elem table)
  341. `(table.init ,(elem-name elem) ,(table-name table)))
  342. (('elem.drop elem)
  343. `(elem.drop ,(elem-name elem)))
  344. (('table.copy dst src)
  345. `(table.copy ,(table-name dst) ,(table-name src)))
  346. (((and inst (or 'table.grow 'table.size 'table.fill)) table)
  347. `(,inst ,(table-name table)))
  348. ;; Not yet implemented: simd mem ops, atomic mem ops.
  349. (inst inst)))
  350. (map visit-inst expr))
  351. (visit-expr expr '()))
  352. (define (visit-init expr)
  353. (visit-expr* expr error))
  354. (define (visit-func func)
  355. (match func
  356. (($ <func> fid ftype locals body)
  357. (define local-name
  358. (match ftype
  359. (($ <type-use> _ ($ <func-sig> (($ <param> id _) ...) (_ ...)))
  360. (make-namer
  361. (append id
  362. (match locals ((($ <local> id _) ...) id)))))))
  363. (let ((type (visit-type-use ftype))
  364. (body (visit-expr* body local-name)))
  365. (make-func fid ftype locals body)))))
  366. (let ((types (map (match-lambda
  367. (($ <rec-group> (($ <type> id type) ...))
  368. (make-rec-group
  369. (map make-type id (map visit-sub-type type))))
  370. (($ <type> id type)
  371. (make-type (type-name id)
  372. (visit-sub-type type))))
  373. types))
  374. (imports (map (match-lambda
  375. (($ <import> mod name kind id type)
  376. (let ((type (match kind
  377. ('func (visit-type-use type))
  378. ('table (visit-table-type type))
  379. ('memory type)
  380. ('global (visit-global-type type)))))
  381. (make-import mod name kind id type))))
  382. imports))
  383. (funcs (map visit-func funcs))
  384. (tables (map (match-lambda
  385. (($ <table> id type init)
  386. (make-table id (visit-table-type type)
  387. (and init (visit-init init)))))
  388. tables))
  389. (globals (map (match-lambda
  390. (($ <global> id ($ <global-type> mutable? vt) init)
  391. (let* ((vt (visit-val-type vt))
  392. (type (make-global-type mutable? vt)))
  393. (make-global id type (visit-init init)))))
  394. globals))
  395. (exports (map (match-lambda
  396. (($ <export> name kind idx)
  397. (make-export name kind
  398. (match kind
  399. ('func (func-name idx))
  400. ('table (table-name idx))
  401. ('memory (memory-name idx))
  402. ('global (global-name idx))))))
  403. exports))
  404. (start (and=> start func-name))
  405. (elems (map (match-lambda
  406. (($ <elem> id mode table type offset inits)
  407. (make-elem id mode (and=> table table-name)
  408. (visit-val-type type)
  409. (and=> offset visit-init)
  410. (map visit-init inits))))
  411. elems))
  412. (datas (map (match-lambda
  413. (($ <data> id mode mem offset init)
  414. (make-data id mode (and=> mem memory-name)
  415. (and=> offset visit-init)
  416. init)))
  417. datas))
  418. (tags (map (match-lambda
  419. (($ <tag> id type)
  420. (make-tag (tag-name id) (visit-type-use type))))
  421. tags)))
  422. (make-wasm id types imports funcs tables memories globals exports start
  423. elems datas tags '() custom)))))
  424. (define (symbolify-wasm wasm)
  425. (symbolify-uses (symbolify-defs wasm)))