symbolify.scm 20 KB

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