link.scm 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  1. ;;; WebAssembly linker
  2. ;;; Copyright (C) 2023, 2024, 2025 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. ;;; Linker for WebAssembly, to augment a wasm module by pulling in
  18. ;;; missing definitions from a standard library.
  19. ;;;
  20. ;;; Code:
  21. (define-module (wasm link)
  22. #:use-module (ice-9 match)
  23. #:use-module (wasm types)
  24. #:use-module (wasm types)
  25. #:export (add-stdlib))
  26. (define (fold1 f l s0)
  27. (let lp ((l l) (s0 s0))
  28. (match l
  29. (() s0)
  30. ((elt . l) (lp l (f elt s0))))))
  31. (define-syntax match-inst
  32. (syntax-rules (_)
  33. ((match-inst inst ((head . tail) . body) ... (_ . default))
  34. (match inst
  35. ((op . args)
  36. (match op
  37. (head (match args (tail . body)))
  38. ...
  39. (_ . default)))))))
  40. (define (sort-types types)
  41. (define visited (make-hash-table))
  42. (define (visited? type) (hashq-ref visited type))
  43. (define (mark-visited! type) (hashq-set! visited type #t))
  44. (define all-types (make-hash-table))
  45. (define (add-type! name type) (hashq-set! all-types name type))
  46. (for-each (lambda (type)
  47. (match type
  48. (($ <type> id _)
  49. (add-type! id type))
  50. (($ <rec-group> (($ <type> id) ...))
  51. (for-each (lambda (id) (add-type! id type)) id))))
  52. types)
  53. (define (lookup-type name) (hashq-ref all-types name))
  54. (define (visit-heap-type type order)
  55. (match (lookup-type type)
  56. (#f order)
  57. (type (visit-type type order))))
  58. (define (visit-val-type type order)
  59. (match type
  60. (($ <ref-type> nullable? ht)
  61. (visit-heap-type ht order))
  62. (_ order)))
  63. (define (visit-storage-type type order)
  64. (visit-val-type type order))
  65. (define (visit-successors type order)
  66. (define (visit-base type order)
  67. (match type
  68. (($ <array-type> mutable? type)
  69. (visit-storage-type type order))
  70. (($ <struct-type> fields)
  71. (fold1 (lambda (field order)
  72. (match field
  73. (($ <field> id mutable? type)
  74. (visit-storage-type type order))))
  75. fields order))
  76. (($ <func-sig> params results)
  77. (fold1 (lambda (param order)
  78. (match param
  79. (($ <param> id type)
  80. (visit-val-type type order))))
  81. params (fold1 visit-val-type results order)))))
  82. (define (visit-sub type order)
  83. (match type
  84. (($ <sub-type> final? supers type)
  85. (visit-base type (fold1 visit-heap-type supers order)))
  86. (_ (visit-base type order))))
  87. (match type
  88. (($ <rec-group> (($ <type> id type) ...))
  89. (fold1 visit-sub type order))
  90. (($ <type> id type)
  91. (visit-sub type order))))
  92. (define (visit-type type order)
  93. (cond
  94. ((visited? type) order)
  95. (else
  96. ;; After visiting successors, add label to the reverse post-order.
  97. (mark-visited! type)
  98. (cons type (visit-successors type order)))))
  99. (reverse (fold1 visit-type types '())))
  100. (define* (link wasm #:key
  101. (link-type (lambda (id) #f))
  102. (link-import (lambda (id kind) #f))
  103. (link-func (lambda (id) #f))
  104. (link-table (lambda (id) #f))
  105. (link-memory (lambda (id) #f))
  106. (link-global (lambda (id) #f))
  107. (link-data (lambda (id) #f))
  108. (link-elem (lambda (id) #f))
  109. (link-tag (lambda (id) #f)))
  110. (define (for-each-instruction f body)
  111. (define (visit* body)
  112. (for-each visit1 body))
  113. (define (visit1 inst)
  114. (f inst)
  115. (match-inst inst
  116. (((or 'block 'loop) label type insts)
  117. (visit* insts))
  118. (('if label type consequent alternate)
  119. (visit* consequent)
  120. (visit* alternate))
  121. (('try label type body ((tag . catch) ...) catch-all)
  122. (visit* body)
  123. (for-each visit* catch)
  124. (when catch-all (visit* catch-all)))
  125. (('try_delegate label type body handler)
  126. (visit* body))
  127. (_ (values))))
  128. (visit* body))
  129. (match wasm
  130. (($ <wasm>
  131. %id %types %imports %funcs %tables %memories %globals
  132. %exports %start %elems %datas %tags %strings %custom)
  133. (define (visit-heap-type! type)
  134. (match type
  135. ((or 'func 'extern 'any 'eq 'i31 'noextern 'nofunc 'struct 'array 'none
  136. 'string 'stringview_wtf8 'stringview_wtf16 'stringview_iter)
  137. (values))
  138. (_
  139. (link-type! type))))
  140. (define (visit-val-type! type)
  141. (match type
  142. ((or 'i32 'i64 'f32 'f64 'v128
  143. 'funcref 'externref 'anyref 'eqref 'i31ref
  144. 'nullexternref 'nullfuncref
  145. 'structref 'arrayref
  146. 'nullref
  147. 'stringref
  148. 'stringview_wtf8ref 'stringview_wtf16ref 'stringview_iterref)
  149. (values))
  150. (($ <ref-type> nullable? ht)
  151. (visit-heap-type! ht))))
  152. (define (visit-storage-type! type)
  153. (match type
  154. ((or 'i8 'i16) (values))
  155. (_ (visit-val-type! type))))
  156. (define (visit-func-sig! params results)
  157. (for-each (match-lambda
  158. (($ <param> id type)
  159. (visit-val-type! type)))
  160. params)
  161. (for-each visit-val-type! results))
  162. (define (visit-ref-type! type)
  163. (match type
  164. (($ <ref-type> nullable? ht)
  165. (visit-heap-type! ht))
  166. (_ (values))))
  167. (define (visit-func-type! type)
  168. (visit-heap-type! type))
  169. (define (visit-type-use! type)
  170. (match type
  171. (($ <type-use> idx ($ <func-sig> params results))
  172. (visit-func-sig! params results)
  173. (when (symbol? idx)
  174. (visit-func-type! idx)))))
  175. (define (visit-type! type)
  176. (define (visit-base! type)
  177. (match type
  178. (($ <array-type> mutable? type)
  179. (visit-storage-type! type))
  180. (($ <struct-type> fields)
  181. (for-each (lambda (field)
  182. (match field
  183. (($ <field> id mutable? type)
  184. (visit-storage-type! type))))
  185. fields))
  186. (($ <func-sig> params results)
  187. (visit-func-sig! params results))))
  188. (define (visit-sub! type)
  189. (match type
  190. (($ <sub-type> final? supers type)
  191. (visit-base! type)
  192. (for-each visit-heap-type! supers))
  193. (_ (visit-base! type))))
  194. (match type
  195. (($ <rec-group> (($ <type> id type) ...))
  196. (for-each visit-sub! type))
  197. (($ <type> id type)
  198. (visit-sub! type))))
  199. (define (visit-import! import)
  200. (match import
  201. (($ <import> mod name 'func id type)
  202. (visit-type-use! type))
  203. (($ <import> mod name 'table id ($ <table-type> limits type))
  204. (visit-val-type! type))
  205. (($ <import> mod name 'memory id type)
  206. (values))
  207. (($ <import> mod name 'global id ($ <global-type> mutable? type))
  208. (visit-val-type! type))
  209. (($ <import> mod name 'tag id ($ <tag-type> attribute type))
  210. (visit-type-use! type))))
  211. (define (visit-body! body)
  212. (for-each-instruction
  213. (lambda (inst)
  214. (match-inst inst
  215. (((or 'block 'loop 'if 'try_delegate) label type . _)
  216. (when type
  217. (visit-type-use! type)))
  218. (('try label type body ((tags . _) ...) _)
  219. (when type
  220. (visit-type-use! type))
  221. (for-each link-tag! tags))
  222. (('throw tag)
  223. (link-tag! tag))
  224. (((or 'call 'return_call 'ref.func) label)
  225. (link-func! label))
  226. (((or 'call_indirect 'return_call_indirect) table type)
  227. (link-table! table)
  228. (visit-type-use! type))
  229. (((or 'call_ref 'return_call_ref) type)
  230. (visit-heap-type! type))
  231. (((or 'global.get 'global.set) label)
  232. (link-global! label))
  233. (((or 'table.get 'table.set
  234. 'table.grow 'table.size 'table.fill) label)
  235. (link-table! label))
  236. (('table.init elem table)
  237. (link-elem! elem)
  238. (link-table! table))
  239. (('table.copy dst src)
  240. (link-table! dst)
  241. (link-table! src))
  242. (((or 'i32.load 'i64.load 'f32.load 'f64.load
  243. 'i32.load8_s 'i32.load8_u 'i32.load16_s 'i32.load16_u
  244. 'i64.load8_s 'i64.load8_u 'i64.load16_s 'i64.load16_u
  245. 'i64.load32_s 'i64.load32_u
  246. 'i32.store 'i64.store 'f32.store 'f64.store
  247. 'i32.store8 'i32.store16 'i64.store8 'i64.store16
  248. 'i64.store32)
  249. ($ <mem-arg> id offset align))
  250. (link-memory! id))
  251. (((or 'memory.size 'memory.grow 'memory.init 'memory.fill) id)
  252. (link-memory! id))
  253. (('memory.copy dst src)
  254. (link-memory! dst)
  255. (link-memory! src))
  256. (('select type ...)
  257. (for-each visit-val-type! type))
  258. (('ref.null type)
  259. (visit-heap-type! type))
  260. (((or 'ref.test 'ref.cast) ($ <ref-type> nullable? type))
  261. (visit-heap-type! type))
  262. (((or 'br_on_cast 'br_on_cast_fail) idx rt1 rt2)
  263. (visit-ref-type! rt1)
  264. (visit-ref-type! rt2))
  265. (((or 'struct.get 'struct.get_s 'struct.get_u
  266. 'struct.set) type field)
  267. (visit-heap-type! type))
  268. (((or 'struct.new 'struct.new_default
  269. 'array.new 'array.new_default
  270. 'array.get 'array.get_s 'array.get_u
  271. 'array.set) type)
  272. (visit-heap-type! type))
  273. (('array.copy dst src)
  274. (visit-heap-type! dst)
  275. (visit-heap-type! src))
  276. (('array.new_fixed type count)
  277. (visit-heap-type! type))
  278. (((or 'array.new_data 'array.init_data) type data)
  279. (visit-heap-type! type)
  280. (link-data! data))
  281. (((or 'array.new_elem 'array.init_elem) type elem)
  282. (visit-heap-type! type)
  283. (link-elem! elem))
  284. (_ (values))))
  285. body))
  286. (define (visit-func! func)
  287. (match func
  288. (($ <func> id type (($ <local> lid ltype) ...) body)
  289. (visit-type-use! type)
  290. (for-each visit-val-type! ltype)
  291. (visit-body! body))))
  292. (define (visit-table! table)
  293. (match table
  294. (($ <table> id ($ <table-type> limits type) init)
  295. (visit-val-type! type)
  296. (when init
  297. (visit-body! init)))))
  298. (define (visit-memory! memory)
  299. ;; Nothing to do.
  300. (values))
  301. (define (visit-global! global)
  302. (match global
  303. (($ <global> id ($ <global-type> mutable? type) init)
  304. (visit-val-type! type)
  305. (visit-body! init))))
  306. (define (visit-export! export)
  307. (match export
  308. (($ <export> name kind id)
  309. (match kind
  310. ('func (link-func! id))
  311. ('table (link-table! id))
  312. ('global (link-global! id))
  313. ('memory (link-memory! id))
  314. ('tag (link-tag! id))))))
  315. (define (visit-start! start)
  316. (link-func! start))
  317. (define (visit-elem! elem)
  318. (match elem
  319. (($ <elem> id mode table type offset inits)
  320. (visit-body! inits)
  321. (visit-val-type! type)
  322. (when offset
  323. (visit-body! offset)))))
  324. (define (visit-data! data)
  325. (match data
  326. (($ <data> id mode mem offset init)
  327. (when (eq? mode 'active)
  328. (link-memory! mem)))))
  329. (define (visit-tag! tag)
  330. (match tag
  331. (($ <tag> id ($ <tag-type> attribute type))
  332. (visit-type-use! type))))
  333. (define-syntax-rule (define-linker (link! record!)
  334. %elts elt-id link-elt import-kind visit-elt!)
  335. (begin
  336. (define table (make-hash-table))
  337. (define (record! id)
  338. (hashq-set! table id #t))
  339. (define (record-elt! elt)
  340. (record! (elt-id elt)))
  341. (define (link! id)
  342. (unless (hashq-ref table id)
  343. (match (link-elt id)
  344. (#f
  345. (if import-kind
  346. (link-import! id import-kind)
  347. (error "dangling reference" id)))
  348. (elt
  349. (unless (eq? id (elt-id elt)) (error "what"))
  350. (when (hashq-ref table id) (error "unexpected!"))
  351. (record! id)
  352. (set! %elts (cons elt %elts))
  353. (visit-elt! elt)))))
  354. (for-each record-elt! %elts)))
  355. (define %types-by-id (make-hash-table))
  356. (define (link-type! type)
  357. (unless (hashq-ref %types-by-id type)
  358. (let ((type (or (link-type type)
  359. (error "unknown heap type" type))))
  360. (record-type! type)
  361. (set! %types (cons type %types))
  362. (visit-type! type))))
  363. (define (record-type! type)
  364. (define (record! id)
  365. (hashq-set! %types-by-id id type))
  366. (match type
  367. (($ <rec-group> (($ <type> id _) ...)) (for-each record! id))
  368. (($ <type> id _) (record! id))))
  369. (define (link-import! id kind)
  370. (let ((import (link-import id kind)))
  371. (unless import
  372. (error "dangling reference" id kind))
  373. (set! %imports (cons import %imports))
  374. (record-import! import)
  375. (visit-import! import)))
  376. (define (record-import! import)
  377. (match import
  378. (($ <import> mod name 'func id) (record-func! id))
  379. (($ <import> mod name 'table id) (record-table! id))
  380. (($ <import> mod name 'global id) (record-global! id))
  381. (($ <import> mod name 'memory id) (record-memory! id))
  382. (($ <import> mod name 'tag id) (record-tag! id))))
  383. (define-linker (link-func! record-func!)
  384. %funcs func-id link-func 'func visit-func!)
  385. (define-linker (link-table! record-table!)
  386. %tables table-id link-table 'table visit-table!)
  387. (define-linker (link-memory! record-memory!)
  388. %memories memory-id link-memory 'memory visit-memory!)
  389. (define-linker (link-global! record-global!)
  390. %globals global-id link-global 'global visit-global!)
  391. (define-linker (link-data! record-data!)
  392. %datas data-id link-data #f visit-data!)
  393. (define-linker (link-elem! record-elem!)
  394. %elems elem-id link-elem #f visit-elem!)
  395. (define-linker (link-tag! record-tag!)
  396. %tags tag-id link-tag 'tag visit-tag!)
  397. (for-each record-type! %types)
  398. (for-each record-import! %imports)
  399. (for-each visit-type! %types)
  400. (for-each visit-import! %imports)
  401. (for-each visit-func! %funcs)
  402. (for-each visit-table! %tables)
  403. (for-each visit-memory! %memories)
  404. (for-each visit-global! %globals)
  405. (for-each visit-export! %exports)
  406. (when %start (visit-start! %start))
  407. (for-each visit-elem! %elems)
  408. (for-each visit-data! %datas)
  409. (for-each visit-tag! %tags)
  410. (make-wasm %id (sort-types %types) %imports %funcs %tables %memories
  411. %globals %exports %start %elems %datas %tags %strings
  412. %custom))))
  413. (define* (add-stdlib wasm stdlib #:key
  414. (synthesize-type (lambda (id) #f))
  415. (synthesize-import (lambda (id kind) #f)))
  416. (match stdlib
  417. (($ <wasm> std-id std-types std-imports std-funcs std-tables std-memories
  418. std-globals std-exports std-start std-elems std-datas std-tags
  419. std-strings std-custom)
  420. (define types (make-hash-table))
  421. (define imports (make-hash-table))
  422. (define funcs (make-hash-table))
  423. (define tables (make-hash-table))
  424. (define memories (make-hash-table))
  425. (define globals (make-hash-table))
  426. (define elems (make-hash-table))
  427. (define datas (make-hash-table))
  428. (define tags (make-hash-table))
  429. (for-each (match-lambda
  430. ((and t ($ <type> id _)) (hashq-set! types id t))
  431. ((and t ($ <rec-group> (($ <type> id) ...)))
  432. (for-each (lambda (id) (hashq-set! types id t)) id)))
  433. std-types)
  434. (for-each (match-lambda
  435. ((and import ($ <import> mode name kind id type))
  436. (hash-set! imports (cons id kind) import)))
  437. std-imports)
  438. (for-each (match-lambda
  439. ((and func ($ <func> id type locals body))
  440. (hashq-set! funcs id func)))
  441. std-funcs)
  442. (for-each (match-lambda
  443. ((and table ($ <table> id type init))
  444. (hashq-set! tables id table)))
  445. std-tables)
  446. (for-each (match-lambda
  447. ((and memory ($ <memory> id type))
  448. (hashq-set! memories id memory)))
  449. std-memories)
  450. (for-each (match-lambda
  451. ((and global ($ <global> id type init))
  452. (hashq-set! globals id global)))
  453. std-globals)
  454. (for-each (match-lambda
  455. ((and elem ($ <elem> id mode table type offset init))
  456. (hashq-set! elems id elem)))
  457. std-elems)
  458. (for-each (match-lambda
  459. ((and data ($ <data> id mode mem offset init))
  460. (hashq-set! datas id data)))
  461. std-datas)
  462. (for-each (match-lambda
  463. ((and tag ($ <tag> id type))
  464. (hashq-set! tags id tag)))
  465. std-tags)
  466. (link wasm
  467. #:link-type (lambda (id)
  468. (or (hashq-ref types id)
  469. (synthesize-type id)))
  470. #:link-import (lambda (id kind)
  471. (or (hash-ref imports (cons id kind))
  472. (synthesize-import id kind)))
  473. #:link-func (lambda (id) (hashq-ref funcs id))
  474. #:link-table (lambda (id) (hashq-ref tables id))
  475. #:link-memory (lambda (id) (hashq-ref memories id))
  476. #:link-global (lambda (id) (hashq-ref globals id))
  477. #:link-elem (lambda (id) (hashq-ref elems id))
  478. #:link-data (lambda (id) (hashq-ref datas id))
  479. #:link-tag (lambda (id) (hashq-ref tags id))))))