compile.scm 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. ;;; WebAssembly compiler
  2. ;;; Copyright (C) 2023, 2024 Igalia, S.L.
  3. ;;; Copyright (C) 2023 Robin Templeton <robin@spritely.institute>
  4. ;;; Copyright (C) 2023 David Thompson <dave@spritely.institute>
  5. ;;;
  6. ;;; Licensed under the Apache License, Version 2.0 (the "License");
  7. ;;; you may not use this file except in compliance with the License.
  8. ;;; You may obtain a copy of the License at
  9. ;;;
  10. ;;; http://www.apache.org/licenses/LICENSE-2.0
  11. ;;;
  12. ;;; Unless required by applicable law or agreed to in writing, software
  13. ;;; distributed under the License is distributed on an "AS IS" BASIS,
  14. ;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. ;;; See the License for the specific language governing permissions and
  16. ;;; limitations under the License.
  17. ;;; Commentary:
  18. ;;;
  19. ;;; Scheme to WebAssembly compiler.
  20. ;;;
  21. ;;; Code:
  22. (define-module (hoot compile)
  23. #:use-module (ice-9 binary-ports)
  24. #:use-module (ice-9 match)
  25. #:use-module ((system base compile)
  26. #:select ((compile . %compile)
  27. default-warning-level
  28. default-optimization-level))
  29. #:use-module (system base target)
  30. #:use-module (hoot library-group)
  31. #:use-module (hoot inline-wasm)
  32. #:use-module (hoot backend)
  33. #:use-module (wasm assemble)
  34. #:export (%default-program-imports
  35. scheme->sealed-tree-il
  36. read-and-compile
  37. compile-file
  38. compile
  39. library-load-path-extension))
  40. (define-syntax-rule (with-hoot-target . body)
  41. (with-target "wasm32-unknown-hoot"
  42. (lambda ()
  43. (parameterize ((target-runtime 'hoot))
  44. . body))))
  45. (define (%include-from-path filename)
  46. (let ((filename
  47. (or (%search-load-path filename)
  48. (error 'include "file not found in path" filename))))
  49. (call-with-include-port
  50. (datum->syntax #f (canonicalize-path filename))
  51. (lambda (p)
  52. (let lp ()
  53. (match (read-syntax p)
  54. ((? eof-object?) #'())
  55. (x (cons x (lp)))))))))
  56. (define (include-relative-to-file base)
  57. (lambda (filename)
  58. (let ((filename (if (absolute-file-name? filename)
  59. filename
  60. (in-vicinity (dirname (canonicalize-path base))
  61. filename))))
  62. (unless (file-exists? filename)
  63. (error "file not found" filename))
  64. (call-with-include-port
  65. (datum->syntax #f filename)
  66. (lambda (p)
  67. (let lp ()
  68. (match (read-syntax p)
  69. ((? eof-object?) #'())
  70. (x (cons x (lp))))))))))
  71. (define (include-relative-to-port port)
  72. (cond
  73. ((port-filename port) => include-relative-to-file)
  74. (else (lambda (filename) (error "port has no file name" port)))))
  75. (define* (hoot-features #:key (import-abi? #f))
  76. (let ((features '(r7rs exact-closed ieee-float full-unicode ratios
  77. wasm hoot hoot-1.0)))
  78. (cons (if import-abi? 'hoot-aux 'hoot-main) features)))
  79. (define %default-program-imports
  80. '((hoot match)
  81. (scheme base)
  82. (scheme case-lambda)
  83. (scheme char)
  84. (scheme complex)
  85. (scheme cxr)
  86. (scheme eval)
  87. (scheme file)
  88. (scheme inexact)
  89. (scheme lazy)
  90. (scheme load)
  91. (scheme read)
  92. (scheme repl)
  93. (scheme process-context)
  94. (scheme time)
  95. (scheme write)
  96. (only (hoot syntax) lambda* case-lambda* define* define-syntax-rule)
  97. (only (hoot primitives) %inline-wasm %wasm-export)
  98. (only (hoot numbers) 1+ 1-)
  99. (only (hoot pairs) cons*)
  100. (only (hoot debug) pk)))
  101. (define (builtin-module-loader import-abi?)
  102. (define (load-module-from-path filename)
  103. (define trusted? #t)
  104. (match (%include-from-path filename)
  105. ((form) (parse-r6rs-library form trusted?))
  106. (forms (error "expected exactly one form" forms))))
  107. (define <- load-module-from-path)
  108. (define-syntax-rule (library-name-case x (name exp) ...)
  109. (cond
  110. ((equal? x 'name) exp)
  111. ...
  112. (else #f)))
  113. (lambda (name)
  114. (library-name-case
  115. name
  116. ((hoot features)
  117. (let ((trusted? #t))
  118. (parse-r6rs-library
  119. `(library (hoot features)
  120. (export features)
  121. (import (hoot syntax))
  122. (define (features)
  123. ',(hoot-features #:import-abi? import-abi?)))
  124. trusted?)))
  125. ((hoot assoc) (<- "hoot/assoc"))
  126. ((hoot atomics) (<- "hoot/atomics"))
  127. ((hoot bitvectors) (<- "hoot/bitvectors"))
  128. ((hoot bitwise) (<- "hoot/bitwise"))
  129. ((hoot boxes) (<- "hoot/boxes"))
  130. ((hoot bytevectors) (<- "hoot/bytevectors"))
  131. ((hoot char) (<- "hoot/char"))
  132. ((hoot cond-expand) (<- "hoot/cond-expand"))
  133. ((hoot control) (<- "hoot/control"))
  134. ((hoot debug) (<- "hoot/debug"))
  135. ((hoot dynamic-wind) (<- "hoot/dynamic-wind"))
  136. ((hoot eq) (<- "hoot/eq"))
  137. ((hoot equal) (<- "hoot/equal"))
  138. ((hoot error-handling) (<- "hoot/error-handling"))
  139. ((hoot errors) (<- "hoot/errors"))
  140. ((hoot exceptions) (<- "hoot/exceptions"))
  141. ((hoot ffi) (<- "hoot/ffi"))
  142. ((hoot fluids) (<- "hoot/fluids"))
  143. ((hoot hashtables) (<- "hoot/hashtables"))
  144. ((hoot keywords) (<- "hoot/keywords"))
  145. ((hoot lists) (<- "hoot/lists"))
  146. ((hoot match) (<- "hoot/match"))
  147. ((hoot not) (<- "hoot/not"))
  148. ((hoot numbers) (<- "hoot/numbers"))
  149. ((hoot pairs) (<- "hoot/pairs"))
  150. ((hoot parameters) (<- "hoot/parameters"))
  151. ((hoot ports) (<- "hoot/ports"))
  152. ((hoot procedures) (<- "hoot/procedures"))
  153. ((hoot read) (<- "hoot/read"))
  154. ((hoot records) (<- "hoot/records"))
  155. ((hoot strings) (<- "hoot/strings"))
  156. ((hoot symbols) (<- "hoot/symbols"))
  157. ((hoot syntax) (<- "hoot/syntax"))
  158. ((hoot values) (<- "hoot/values"))
  159. ((hoot vectors) (<- "hoot/vectors"))
  160. ((hoot write) (<- "hoot/write"))
  161. ((scheme base) (<- "hoot/r7rs-base"))
  162. ((scheme case-lambda) (<- "hoot/r7rs-case-lambda"))
  163. ((scheme char) (<- "hoot/r7rs-char"))
  164. ((scheme complex) (<- "hoot/r7rs-complex"))
  165. ((scheme cxr) (<- "hoot/r7rs-cxr"))
  166. ((scheme eval) (<- "hoot/r7rs-eval"))
  167. ((scheme file) (<- "hoot/r7rs-file"))
  168. ((scheme inexact) (<- "hoot/r7rs-inexact"))
  169. ((scheme lazy) (<- "hoot/r7rs-lazy"))
  170. ((scheme load) (<- "hoot/r7rs-load"))
  171. ((scheme process-context) (<- "hoot/r7rs-process-context"))
  172. ((scheme r5rs) (<- "hoot/r7rs-r5rs"))
  173. ((scheme read) (<- "hoot/r7rs-read"))
  174. ((scheme repl) (<- "hoot/r7rs-repl"))
  175. ((scheme time) (<- "hoot/r7rs-time"))
  176. ((scheme write) (<- "hoot/r7rs-write")))))
  177. (define* (scheme->sealed-tree-il expr #:key
  178. (imports %default-program-imports)
  179. (import-abi? #f)
  180. (include-file %include-from-path)
  181. (extend-load-library (lambda (f) f))
  182. (load-library
  183. (extend-load-library
  184. (builtin-module-loader import-abi?))))
  185. (define group
  186. (match expr
  187. ((? library-group?) expr)
  188. (_ (parse-library-group `(library-group (import . ,imports) ,expr)
  189. #:include-file include-file))))
  190. (define linked
  191. (link-library-group group
  192. #:load-library load-library
  193. #:allow-dangling-import?
  194. (lambda (name)
  195. (equal? name '(hoot primitives)))))
  196. (expand-library-group linked
  197. #:primitives '(hoot primitives)
  198. #:call-with-target (lambda (f)
  199. (with-hoot-target (f)))))
  200. (define* (compile expr #:key
  201. (imports %default-program-imports)
  202. (import-abi? #f)
  203. (export-abi? #t)
  204. (include-file %include-from-path)
  205. (extend-load-library (lambda (f) f))
  206. (load-library
  207. (extend-load-library (builtin-module-loader import-abi?)))
  208. (optimization-level (default-optimization-level))
  209. (warning-level (default-warning-level))
  210. (dump-cps? #f)
  211. (dump-wasm? #f)
  212. (emit-names? #f)
  213. (opts '()))
  214. (define tree-il
  215. (scheme->sealed-tree-il expr #:imports imports
  216. #:import-abi? import-abi?
  217. #:include-file include-file
  218. #:load-library load-library))
  219. (with-hoot-target
  220. (define cps
  221. (%compile tree-il #:env #f #:from 'tree-il #:to 'cps
  222. #:optimization-level optimization-level
  223. #:warning-level warning-level))
  224. (high-level-cps->wasm cps
  225. #:import-abi? import-abi?
  226. #:export-abi? export-abi?
  227. #:optimization-level optimization-level
  228. #:warning-level warning-level
  229. #:dump-cps? dump-cps?
  230. #:dump-wasm? dump-wasm?
  231. #:emit-names? emit-names?
  232. #:opts opts)))
  233. (define* (read-and-compile port #:key
  234. (import-abi? #f)
  235. (export-abi? #t)
  236. (optimization-level (default-optimization-level))
  237. (warning-level (default-warning-level))
  238. (include-file (include-relative-to-port port))
  239. (extend-load-library (lambda (f) f))
  240. (load-library
  241. (extend-load-library (builtin-module-loader import-abi?)))
  242. (dump-cps? #f)
  243. (dump-wasm? #f)
  244. (emit-names? #f)
  245. (opts '()))
  246. (define (name-matches? stx sym)
  247. (eq? (syntax->datum stx) sym))
  248. (define-syntax-rule (symbolic-match? name)
  249. (name-matches? #'name 'name))
  250. (define forms
  251. (let lp ()
  252. (let ((expr (read-syntax port)))
  253. (if (eof-object? expr)
  254. '()
  255. (cons expr (lp))))))
  256. (define group
  257. (syntax-case forms ()
  258. (((library-group . _))
  259. (symbolic-match? library-group)
  260. (parse-library-group (car forms) #:include-file include-file))
  261. (((import . imports) . body)
  262. (symbolic-match? import)
  263. (parse-library-group #'(library-group (import . imports) . body)))
  264. (_
  265. (parse-library-group
  266. `(library-group (import . ,%default-program-imports) . ,forms)))))
  267. (compile group
  268. #:import-abi? import-abi?
  269. #:export-abi? export-abi?
  270. #:optimization-level optimization-level
  271. #:warning-level warning-level
  272. #:load-library load-library
  273. #:dump-cps? dump-cps?
  274. #:dump-wasm? dump-wasm?
  275. #:emit-names? emit-names?
  276. #:opts opts))
  277. (define* (compile-file input-file #:key
  278. (output-file (error "missing output file"))
  279. (import-abi? #f)
  280. (export-abi? #t)
  281. (optimization-level (default-optimization-level))
  282. (warning-level (default-warning-level))
  283. (include-file (include-relative-to-file input-file))
  284. (extend-load-library (lambda (f) f))
  285. (load-library
  286. (extend-load-library (builtin-module-loader import-abi?)))
  287. (dump-cps? #f)
  288. (dump-wasm? #f)
  289. (emit-names? #f)
  290. (opts '()))
  291. (call-with-input-file input-file
  292. (lambda (in)
  293. (set-port-encoding! in (or (file-encoding in) "UTF-8"))
  294. (let ((wasm (read-and-compile in
  295. #:import-abi? import-abi?
  296. #:export-abi? export-abi?
  297. #:optimization-level optimization-level
  298. #:warning-level warning-level
  299. #:include-file include-file
  300. #:load-library load-library
  301. #:dump-cps? dump-cps?
  302. #:dump-wasm? dump-wasm?
  303. #:emit-names? emit-names?
  304. #:opts opts)))
  305. (let ((bytes (assemble-wasm wasm)))
  306. (call-with-output-file output-file
  307. (lambda (out)
  308. (put-bytevector out bytes))))))))
  309. (define (library-load-path-extension load-path)
  310. (define (read-forms-from-file filename)
  311. (call-with-include-port
  312. (datum->syntax #f (canonicalize-path filename))
  313. (lambda (p)
  314. (let lp ()
  315. (match (read-syntax p)
  316. ((? eof-object?) #'())
  317. (x (cons x (lp))))))))
  318. (define (load-library-from-file filename)
  319. (define trusted? #f)
  320. (match (read-forms-from-file filename)
  321. ((form) (parse-r6rs-library form trusted?))
  322. (forms (error "expected exactly one form" forms))))
  323. (define (library-name->file-name name)
  324. (string-join (map symbol->string name) file-name-separator-string))
  325. (define (locate-library name)
  326. (search-path load-path (library-name->file-name name) %load-extensions))
  327. (lambda (load-library)
  328. (lambda (name)
  329. (cond
  330. ((load-library name))
  331. ((locate-library name) => load-library-from-file)
  332. (else #f)))))
  333. (install-inline-wasm!)