compile.scm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  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. (define-syntax-rule (with-hoot-target . body)
  40. (with-target "wasm32-unknown-hoot"
  41. (lambda ()
  42. (parameterize ((target-runtime 'hoot))
  43. . body))))
  44. (define (%include-from-path filename)
  45. (let ((filename
  46. (or (%search-load-path filename)
  47. (error 'include "file not found in path" filename))))
  48. (call-with-include-port
  49. (datum->syntax #f (canonicalize-path filename))
  50. (lambda (p)
  51. (let lp ()
  52. (match (read-syntax p)
  53. ((? eof-object?) #'())
  54. (x (cons x (lp)))))))))
  55. (define (include-relative-to-file base)
  56. (lambda (filename)
  57. (let ((filename (if (absolute-file-name? filename)
  58. filename
  59. (in-vicinity (dirname (canonicalize-path base))
  60. filename))))
  61. (unless (file-exists? filename)
  62. (error "file not found" filename))
  63. (call-with-include-port
  64. (datum->syntax #f filename)
  65. (lambda (p)
  66. (let lp ()
  67. (match (read-syntax p)
  68. ((? eof-object?) #'())
  69. (x (cons x (lp))))))))))
  70. (define (include-relative-to-port port)
  71. (cond
  72. ((port-filename port) => include-relative-to-file)
  73. (else (lambda (filename) (error "port has no file name" port)))))
  74. (define* (hoot-features #:key (import-abi? #f))
  75. (let ((features '(r7rs exact-closed ieee-float full-unicode ratios
  76. wasm hoot hoot-1.0)))
  77. (cons (if import-abi? 'hoot-aux 'hoot-main) features)))
  78. (define %default-program-imports
  79. '((hoot match)
  80. (scheme base)
  81. (scheme case-lambda)
  82. (scheme char)
  83. (scheme complex)
  84. (scheme cxr)
  85. (scheme eval)
  86. (scheme file)
  87. (scheme inexact)
  88. (scheme lazy)
  89. (scheme load)
  90. (scheme read)
  91. (scheme repl)
  92. (scheme process-context)
  93. (scheme time)
  94. (scheme write)
  95. (only (hoot syntax) lambda* case-lambda* define* define-syntax-rule)
  96. (only (hoot primitives) %inline-wasm %wasm-export)
  97. (only (hoot numbers) 1+ 1-)
  98. (only (hoot pairs) cons*)
  99. (only (hoot debug) pk)))
  100. (define (builtin-module-loader import-abi?)
  101. (define (load-module-from-path filename)
  102. (define trusted? #t)
  103. (match (%include-from-path filename)
  104. ((form) (parse-r6rs-library form trusted?))
  105. (forms (error "expected exactly one form" forms))))
  106. (define <- load-module-from-path)
  107. (define-syntax-rule (library-name-case x (name exp) ...)
  108. (cond
  109. ((equal? x 'name) exp)
  110. ...
  111. (else #f)))
  112. (lambda (name)
  113. (library-name-case
  114. name
  115. ((hoot features)
  116. (let ((trusted? #t))
  117. (parse-r6rs-library
  118. `(library (hoot features)
  119. (export features)
  120. (import (hoot syntax))
  121. (define (features)
  122. ',(hoot-features #:import-abi? import-abi?)))
  123. trusted?)))
  124. ((hoot assoc) (<- "hoot/assoc"))
  125. ((hoot atomics) (<- "hoot/atomics"))
  126. ((hoot bitvectors) (<- "hoot/bitvectors"))
  127. ((hoot bitwise) (<- "hoot/bitwise"))
  128. ((hoot boxes) (<- "hoot/boxes"))
  129. ((hoot bytevectors) (<- "hoot/bytevectors"))
  130. ((hoot char) (<- "hoot/char"))
  131. ((hoot cond-expand) (<- "hoot/cond-expand"))
  132. ((hoot control) (<- "hoot/control"))
  133. ((hoot debug) (<- "hoot/debug"))
  134. ((hoot dynamic-wind) (<- "hoot/dynamic-wind"))
  135. ((hoot eq) (<- "hoot/eq"))
  136. ((hoot equal) (<- "hoot/equal"))
  137. ((hoot error-handling) (<- "hoot/error-handling"))
  138. ((hoot errors) (<- "hoot/errors"))
  139. ((hoot exceptions) (<- "hoot/exceptions"))
  140. ((hoot ffi) (<- "hoot/ffi"))
  141. ((hoot fluids) (<- "hoot/fluids"))
  142. ((hoot hashtables) (<- "hoot/hashtables"))
  143. ((hoot keywords) (<- "hoot/keywords"))
  144. ((hoot lists) (<- "hoot/lists"))
  145. ((hoot match) (<- "hoot/match"))
  146. ((hoot not) (<- "hoot/not"))
  147. ((hoot numbers) (<- "hoot/numbers"))
  148. ((hoot pairs) (<- "hoot/pairs"))
  149. ((hoot parameters) (<- "hoot/parameters"))
  150. ((hoot ports) (<- "hoot/ports"))
  151. ((hoot procedures) (<- "hoot/procedures"))
  152. ((hoot read) (<- "hoot/read"))
  153. ((hoot records) (<- "hoot/records"))
  154. ((hoot strings) (<- "hoot/strings"))
  155. ((hoot symbols) (<- "hoot/symbols"))
  156. ((hoot syntax) (<- "hoot/syntax"))
  157. ((hoot values) (<- "hoot/values"))
  158. ((hoot vectors) (<- "hoot/vectors"))
  159. ((hoot write) (<- "hoot/write"))
  160. ((scheme base) (<- "hoot/r7rs-base"))
  161. ((scheme case-lambda) (<- "hoot/r7rs-case-lambda"))
  162. ((scheme char) (<- "hoot/r7rs-char"))
  163. ((scheme complex) (<- "hoot/r7rs-complex"))
  164. ((scheme cxr) (<- "hoot/r7rs-cxr"))
  165. ((scheme eval) (<- "hoot/r7rs-eval"))
  166. ((scheme file) (<- "hoot/r7rs-file"))
  167. ((scheme inexact) (<- "hoot/r7rs-inexact"))
  168. ((scheme lazy) (<- "hoot/r7rs-lazy"))
  169. ((scheme load) (<- "hoot/r7rs-load"))
  170. ((scheme process-context) (<- "hoot/r7rs-process-context"))
  171. ((scheme r5rs) (<- "hoot/r7rs-r5rs"))
  172. ((scheme read) (<- "hoot/r7rs-read"))
  173. ((scheme repl) (<- "hoot/r7rs-repl"))
  174. ((scheme time) (<- "hoot/r7rs-time"))
  175. ((scheme write) (<- "hoot/r7rs-write")))))
  176. (define* (scheme->sealed-tree-il expr #:key
  177. (imports %default-program-imports)
  178. (import-abi? #f)
  179. (include-file %include-from-path)
  180. (extend-load-library (lambda (f) f))
  181. (load-library
  182. (extend-load-library
  183. (builtin-module-loader import-abi?))))
  184. (define group
  185. (match expr
  186. ((? library-group?) expr)
  187. (_ (parse-library-group `(library-group (import . ,imports) ,expr)
  188. #:include-file include-file))))
  189. (define linked
  190. (link-library-group group
  191. #:load-library load-library
  192. #:allow-dangling-import?
  193. (lambda (name)
  194. (equal? name '(hoot primitives)))))
  195. (expand-library-group linked
  196. #:primitives '(hoot primitives)
  197. #:call-with-target (lambda (f)
  198. (with-hoot-target (f)))))
  199. (define* (compile expr #:key
  200. (imports %default-program-imports)
  201. (import-abi? #f)
  202. (export-abi? #t)
  203. (include-file %include-from-path)
  204. (extend-load-library (lambda (f) f))
  205. (load-library
  206. (extend-load-library (builtin-module-loader import-abi?)))
  207. (optimization-level (default-optimization-level))
  208. (warning-level (default-warning-level))
  209. (dump-cps? #f)
  210. (dump-wasm? #f)
  211. (emit-names? #f)
  212. (opts '()))
  213. (define tree-il
  214. (scheme->sealed-tree-il expr #:imports imports
  215. #:import-abi? import-abi?
  216. #:include-file include-file
  217. #:load-library load-library))
  218. (with-hoot-target
  219. (define cps
  220. (%compile tree-il #:env #f #:from 'tree-il #:to 'cps
  221. #:optimization-level optimization-level
  222. #:warning-level warning-level))
  223. (high-level-cps->wasm cps
  224. #:import-abi? import-abi?
  225. #:export-abi? export-abi?
  226. #:optimization-level optimization-level
  227. #:warning-level warning-level
  228. #:dump-cps? dump-cps?
  229. #:dump-wasm? dump-wasm?
  230. #:emit-names? emit-names?
  231. #:opts opts)))
  232. (define* (read-and-compile port #:key
  233. (import-abi? #f)
  234. (export-abi? #t)
  235. (optimization-level (default-optimization-level))
  236. (warning-level (default-warning-level))
  237. (include-file (include-relative-to-port port))
  238. (extend-load-library (lambda (f) f))
  239. (load-library
  240. (extend-load-library (builtin-module-loader import-abi?)))
  241. (dump-cps? #f)
  242. (dump-wasm? #f)
  243. (emit-names? #f)
  244. (opts '()))
  245. (define (name-matches? stx sym)
  246. (eq? (syntax->datum stx) sym))
  247. (define-syntax-rule (symbolic-match? name)
  248. (name-matches? #'name 'name))
  249. (define forms
  250. (let lp ()
  251. (let ((expr (read-syntax port)))
  252. (if (eof-object? expr)
  253. '()
  254. (cons expr (lp))))))
  255. (define group
  256. (syntax-case forms ()
  257. (((library-group . _))
  258. (symbolic-match? library-group)
  259. (parse-library-group (car forms) #:include-file include-file))
  260. (((import . imports) . body)
  261. (symbolic-match? import)
  262. (parse-library-group #'(library-group (import . imports) . body)))
  263. (_
  264. (parse-library-group
  265. `(library-group (import . ,%default-program-imports) . ,forms)))))
  266. (compile group
  267. #:import-abi? import-abi?
  268. #:export-abi? export-abi?
  269. #:optimization-level optimization-level
  270. #:warning-level warning-level
  271. #:load-library load-library
  272. #:dump-cps? dump-cps?
  273. #:dump-wasm? dump-wasm?
  274. #:emit-names? emit-names?
  275. #:opts opts))
  276. (define* (compile-file input-file #:key
  277. (output-file (error "missing output file"))
  278. (import-abi? #f)
  279. (export-abi? #t)
  280. (optimization-level (default-optimization-level))
  281. (warning-level (default-warning-level))
  282. (include-file (include-relative-to-file input-file))
  283. (extend-load-library (lambda (f) f))
  284. (load-library
  285. (extend-load-library (builtin-module-loader import-abi?)))
  286. (dump-cps? #f)
  287. (dump-wasm? #f)
  288. (emit-names? #f)
  289. (opts '()))
  290. (call-with-input-file input-file
  291. (lambda (in)
  292. (set-port-encoding! in (or (file-encoding in) "UTF-8"))
  293. (let ((wasm (read-and-compile in
  294. #:import-abi? import-abi?
  295. #:export-abi? export-abi?
  296. #:optimization-level optimization-level
  297. #:warning-level warning-level
  298. #:include-file include-file
  299. #:load-library load-library
  300. #:dump-cps? dump-cps?
  301. #:dump-wasm? dump-wasm?
  302. #:emit-names? emit-names?
  303. #:opts opts)))
  304. (let ((bytes (assemble-wasm wasm)))
  305. (call-with-output-file output-file
  306. (lambda (out)
  307. (put-bytevector out bytes))))))))
  308. (install-inline-wasm!)