inlinable-exports.scm 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876
  1. ;;; Attaching inlinable definitions of exported bindings to modules
  2. ;;; Copyright (C) 2021, 2022
  3. ;;; Free Software Foundation, Inc.
  4. ;;;
  5. ;;; This library is free software: you can redistribute it and/or modify
  6. ;;; it under the terms of the GNU Lesser General Public License as
  7. ;;; published by the Free Software Foundation, either version 3 of the
  8. ;;; License, or (at your option) any later version.
  9. ;;;
  10. ;;; This library is distributed in the hope that it will be useful, but
  11. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. ;;; Lesser General Public License for more details.
  14. ;;;
  15. ;;; You should have received a copy of the GNU Lesser General Public
  16. ;;; License along with this program. If not, see
  17. ;;; <http://www.gnu.org/licenses/>.
  18. (define-module (language tree-il inlinable-exports)
  19. #:use-module (ice-9 control)
  20. #:use-module (ice-9 match)
  21. #:use-module (ice-9 binary-ports)
  22. #:use-module (language tree-il)
  23. #:use-module (language tree-il primitives)
  24. #:use-module (language tree-il fix-letrec)
  25. #:use-module (language scheme compile-tree-il)
  26. #:use-module ((srfi srfi-1) #:select (filter-map))
  27. #:use-module (srfi srfi-9)
  28. #:use-module (system syntax)
  29. #:use-module (rnrs bytevectors)
  30. #:export (inlinable-exports))
  31. ;;;
  32. ;;; Inlining, as implemented by peval, is the mother of all
  33. ;;; optimizations. It opens up space for other optimizations to work,
  34. ;;; such as constant folding, conditional branch folding, and so on.
  35. ;;;
  36. ;;; Inlining works naturally for lexical bindings. Inlining of
  37. ;;; top-level binding is facilitated by letrectification, which turns
  38. ;;; top-level definition sequences to letrec*. Here we facilitate
  39. ;;; inlining across module boundaries, so that module boundaries aren't
  40. ;;; necessarily optimization boundaries.
  41. ;;;
  42. ;;; The high-level idea is to attach a procedure to the module being
  43. ;;; compiled, which when called with a name of an export of that module
  44. ;;; will return a Tree-IL expression that can be copied into the use
  45. ;;; site. There are two parts: first we determine the set of inlinable
  46. ;;; bindings, and then we compile that mapping to a procedure and attach
  47. ;;; it to the program being compiled.
  48. ;;;
  49. ;;; Because we don't want inter-module inlining to inhibit intra-module
  50. ;;; inlining, this pass is designed to run late in the Tree-IL
  51. ;;; optimization pipeline -- after letrectification, after peval, and so
  52. ;;; on. Unfortunately this does mean that we have to sometimes
  53. ;;; pattern-match to determine higher-level constructs from lower-level
  54. ;;; residual code, for example to map back from
  55. ;;; module-ensure-local-variable! + %variable-set! to toplevel-define,
  56. ;;; as reduced by letrectification. Ah well.
  57. ;;;
  58. ;;; Ultimately we want to leave the decision to peval as to what to
  59. ;;; inline or not to inline, based on its size and effort counters. But
  60. ;;; still we do need to impose some limits -- there's no sense in
  61. ;;; copying a large constant from one module to another, for example.
  62. ;;; Similarly there's no sense in copying a very large procedure.
  63. ;;; Inspired by peval, we bound size growth via a counter that will
  64. ;;; abort an inlinable attempt if the term is too large.
  65. ;;;
  66. ;;; Note that there are some semantic limitations -- you wouldn't want
  67. ;;; to copy a mutable value, nor would you want to copy a closure with
  68. ;;; free variables.
  69. ;;;
  70. ;;; Once the set of inlinables is determined, we copy them and rename
  71. ;;; their lexicals. Any reference to an exported binding by lexical
  72. ;;; variable is rewritten in terms of a reference to the exported
  73. ;;; binding.
  74. ;;;
  75. ;;; The result is then compiled to a procedure, which internally has a
  76. ;;; small interpreter for a bytecode, along with a set of constants.
  77. ;;; The assumption is that most of the constants will be written to the
  78. ;;; object file anyway, so we aren't taking up more space there. Any
  79. ;;; non-immediate is built on demand, so we limit the impact of
  80. ;;; including inlinable definitions on load-time relocations,
  81. ;;; allocations, and heap space.
  82. ;;;
  83. (define (compute-assigned-lexicals exp)
  84. (define assigned-lexicals '())
  85. (define (add-assigned-lexical! var)
  86. (set! assigned-lexicals (cons var assigned-lexicals)))
  87. ((make-tree-il-folder)
  88. exp
  89. (lambda (exp)
  90. (match exp
  91. (($ <lexical-set> _ _ var _)
  92. (add-assigned-lexical! var)
  93. (values))
  94. (_ (values))))
  95. (lambda (exp)
  96. (values)))
  97. assigned-lexicals)
  98. (define (compute-assigned-toplevels exp)
  99. (define assigned-toplevels '())
  100. (define (add-assigned-toplevel! mod name)
  101. (set! assigned-toplevels (acons mod name assigned-toplevels)))
  102. ((make-tree-il-folder)
  103. exp
  104. (lambda (exp)
  105. (match exp
  106. (($ <toplevel-set> _ mod name _)
  107. (add-assigned-toplevel! mod name)
  108. (values))
  109. (($ <module-set> src mod name public? exp)
  110. (unless public?
  111. (add-assigned-toplevel! mod name))
  112. (values))
  113. (_ (values))))
  114. (lambda (exp)
  115. (values)))
  116. assigned-toplevels)
  117. ;;; FIXME: Record all bindings in a module, to know whether a
  118. ;;; toplevel-ref is an import or not. If toplevel-ref to imported
  119. ;;; variable, transform to module-ref or primitive-ref. New pass before
  120. ;;; peval.
  121. (define (compute-module-bindings exp)
  122. (define assigned-lexicals (compute-assigned-lexicals exp))
  123. (define assigned-toplevels (compute-assigned-toplevels exp))
  124. (define module-definitions '())
  125. (define lexicals (make-hash-table))
  126. (define module-lexicals '())
  127. (define variable-lexicals '())
  128. (define binding-lexicals '())
  129. (define binding-values '())
  130. (define (add-module-definition! mod args)
  131. (set! module-definitions (acons mod args module-definitions)))
  132. (define (add-lexical! var val)
  133. (unless (memq var assigned-lexicals)
  134. (hashq-set! lexicals var val)))
  135. (define (add-module-lexical! var mod)
  136. (unless (memq var assigned-lexicals)
  137. (set! module-lexicals (acons var mod module-lexicals))))
  138. (define (add-variable-lexical! var mod name)
  139. (unless (memq var assigned-lexicals)
  140. (set! variable-lexicals (acons var (cons mod name) variable-lexicals))))
  141. (define (add-binding-lexical! var mod name)
  142. (unless (memq var assigned-lexicals)
  143. (set! binding-lexicals (acons var (cons mod name) binding-lexicals))))
  144. (define (add-binding-value! mod name val)
  145. (set! binding-values (acons (cons mod name) val binding-values)))
  146. (define (record-bindings! mod gensyms vals)
  147. (for-each
  148. (lambda (var val)
  149. (add-lexical! var val)
  150. (match val
  151. (($ <call> _ ($ <module-ref> _ '(guile) 'define-module* #f)
  152. (($ <const> _ mod) . args))
  153. (add-module-definition! mod args)
  154. (add-module-lexical! var mod))
  155. (($ <primcall> _ 'current-module ())
  156. (when mod
  157. (add-module-lexical! var mod)))
  158. (($ <primcall> _ 'module-ensure-local-variable!
  159. (($ <lexical-ref> _ _ mod-var) ($ <const> _ name)))
  160. (let ((mod (assq-ref module-lexicals mod-var)))
  161. (when mod
  162. (add-variable-lexical! var mod name))))
  163. (_ #f)))
  164. gensyms vals))
  165. ;; Thread a conservative idea of what the current module is through
  166. ;; the visit. Visiting an expression returns the name of the current
  167. ;; module when the expression completes, or #f if unknown. Record the
  168. ;; define-module* forms, if any, and note any assigned or
  169. ;; multiply-defined variables. Record definitions by matching
  170. ;; toplevel-define forms, but also by matching separate
  171. ;; module-ensure-local-variable! + %variable-set, as residualized by
  172. ;; letrectification.
  173. (define (visit exp) (visit/mod exp #f))
  174. (define (visit* exps)
  175. (unless (null? exps)
  176. (visit (car exps))
  177. (visit* (cdr exps))))
  178. (define (visit+ exps mod)
  179. (match exps
  180. (() mod)
  181. ((exp . exps)
  182. (let lp ((mod' (visit/mod exp mod)) (exps exps))
  183. (match exps
  184. (() mod')
  185. ((exp . exps)
  186. (lp (and (equal? mod' (visit/mod exp mod)) mod')
  187. exps)))))))
  188. (define (visit/mod exp mod)
  189. (match exp
  190. ((or ($ <void>) ($ <const>) ($ <primitive-ref>) ($ <lexical-ref>)
  191. ($ <module-ref>) ($ <toplevel-ref>))
  192. mod)
  193. (($ <call> _ ($ <module-ref> _ '(guile) 'set-current-module #f)
  194. (($ <lexical-ref> _ _ var)))
  195. (assq-ref module-lexicals var))
  196. (($ <primcall> src '%variable-set! (($ <lexical-ref> _ _ var)
  197. val))
  198. (match (assq-ref variable-lexicals var)
  199. ((mod . name)
  200. (add-binding-value! mod name val)
  201. ;; Also record lexical for eta-expanded bindings.
  202. (match val
  203. (($ <lambda> _ _
  204. ($ <lambda-case> _ req #f #f #f () (arg ...)
  205. ($ <call> _
  206. (and eta ($ <lexical-ref> _ _ var))
  207. (($ <lexical-ref> _ _ arg) ...))
  208. #f))
  209. (add-binding-lexical! var mod name))
  210. (($ <lambda> _ _
  211. ($ <lambda-case> _ req #f (not #f) #f () (arg ...)
  212. ($ <primcall> _ 'apply
  213. ((and eta ($ <lexical-ref> _ _ var))
  214. ($ <lexical-ref> _ _ arg) ...))
  215. #f))
  216. (add-binding-lexical! var mod name))
  217. (($ <lexical-ref> _ _ var)
  218. (add-binding-lexical! var mod name))
  219. (_ #f)))
  220. (_ #f))
  221. (visit/mod val mod))
  222. (($ <call> _ proc args)
  223. (visit proc)
  224. (visit* args)
  225. #f)
  226. (($ <primcall> _ _ args)
  227. ;; There is no primcall that sets the current module.
  228. (visit+ args mod))
  229. (($ <conditional> src test consequent alternate)
  230. (visit+ (list consequent alternate) (visit/mod test mod)))
  231. (($ <lexical-set> src name gensym exp)
  232. (visit/mod exp mod))
  233. (($ <toplevel-set> src mod name exp)
  234. (visit/mod exp mod))
  235. (($ <module-set> src mod name public? exp)
  236. (visit/mod exp mod))
  237. (($ <toplevel-define> src mod name exp)
  238. (add-binding-value! mod name exp)
  239. (visit/mod exp mod))
  240. (($ <lambda> src meta body)
  241. (when body (visit body))
  242. mod)
  243. (($ <lambda-case> src req opt rest kw inits gensyms body alternate)
  244. (visit* inits)
  245. (visit body)
  246. (when alternate (visit alternate))
  247. (values))
  248. (($ <seq> src head tail)
  249. (visit/mod tail (visit/mod head mod)))
  250. (($ <let> src names gensyms vals body)
  251. (record-bindings! mod gensyms vals)
  252. (visit/mod body (visit+ vals mod)))
  253. (($ <letrec> src in-order? names gensyms vals body)
  254. (record-bindings! mod gensyms vals)
  255. (visit/mod body (visit+ vals mod)))
  256. (($ <fix> src names gensyms vals body)
  257. (record-bindings! mod gensyms vals)
  258. (visit/mod body (visit+ vals mod)))
  259. (($ <let-values> src exp body)
  260. (visit/mod body (visit/mod exp mod))
  261. #f)
  262. (($ <prompt> src escape-only? tag body handler)
  263. (visit tag)
  264. (visit body)
  265. (visit handler)
  266. #f)
  267. (($ <abort> src tag args tail)
  268. (visit tag)
  269. (visit* args)
  270. (visit tail)
  271. #f)))
  272. (visit exp)
  273. (values module-definitions lexicals binding-lexicals binding-values))
  274. ;; - define inlinable? predicate:
  275. ;; exported && declarative && only references public vars && not too big
  276. ;;
  277. ;; - public := exported from a module, at -O2 and less.
  278. ;; at -O3 and higher public just means defined in any module.
  279. (define (inlinable-exp mod exports lexicals binding-lexicals exp)
  280. (define fresh-var!
  281. (let ((counter 0))
  282. (lambda ()
  283. (let ((name (string-append "t" (number->string counter))))
  284. (set! counter (1+ counter))
  285. (string->symbol name)))))
  286. (define (fresh-vars vars)
  287. (match vars
  288. (() '())
  289. ((_ . vars) (cons (fresh-var!) (fresh-vars vars)))))
  290. (define (add-bound-vars old new bound)
  291. (match (vector old new)
  292. (#(() ()) bound)
  293. (#((old . old*) (new . new*))
  294. (add-bound-vars old* new* (acons old new bound)))))
  295. (let/ec return
  296. (define (abort!) (return #f))
  297. (define count!
  298. ;; Same as default operator size limit for peval.
  299. (let ((counter 40))
  300. (lambda ()
  301. (set! counter (1- counter))
  302. (when (zero? counter) (abort!)))))
  303. (define (residualize-module-private-ref src mod' name)
  304. ;; TODO: At -O3, we could residualize a private
  305. ;; reference. But that could break peoples'
  306. ;; expectations.
  307. (abort!))
  308. (define (eta-reduce exp)
  309. ;; Undo the result of eta-expansion pass.
  310. (match exp
  311. (($ <lambda> _ _
  312. ($ <lambda-case> _ req #f #f #f () (sym ...)
  313. ($ <call> _
  314. (and eta ($ <lexical-ref>)) (($ <lexical-ref> _ _ sym) ...))
  315. #f))
  316. eta)
  317. (($ <lambda> _ _
  318. ($ <lambda-case> _ req #f (not #f) #f () (sym ...)
  319. ($ <primcall> _ 'apply
  320. ((and eta ($ <lexical-ref>)) ($ <lexical-ref> _ _ sym) ...))
  321. #f))
  322. eta)
  323. (_ exp)))
  324. (let copy ((exp (eta-reduce exp)) (bound '()) (in-lambda? #f))
  325. (define (recur exp) (copy exp bound in-lambda?))
  326. (count!)
  327. (match exp
  328. ((or ($ <void>) ($ <primitive-ref>) ($ <module-ref>))
  329. exp)
  330. (($ <const> src val)
  331. (match val
  332. ;; Don't copy values that could be "too big".
  333. ((? string?) exp) ; Oddly, (array? "") => #t.
  334. ((or (? pair?) (? syntax?) (? array?))
  335. (abort!))
  336. (_ exp)))
  337. (($ <lexical-ref> src name var)
  338. (cond
  339. ;; Rename existing lexicals.
  340. ((assq-ref bound var)
  341. => (lambda (var)
  342. (make-lexical-ref src name var)))
  343. ;; A free variable reference to a lambda, outside a lambda.
  344. ;; Could be the lexical-ref residualized by letrectification.
  345. ;; Copy and rely on size limiter to catch runaways.
  346. ((and (not in-lambda?) (lambda? (hashq-ref lexicals var)))
  347. (recur (hashq-ref lexicals var)))
  348. ((not in-lambda?)
  349. ;; No advantage to "inline" a toplevel to another toplevel.
  350. (abort!))
  351. ;; Some letrectified toplevels will be bound to lexical
  352. ;; variables, but unless the module has sealed private
  353. ;; bindings, there may be an associated top-level variable
  354. ;; as well.
  355. ((assq-ref binding-lexicals var)
  356. => (match-lambda
  357. ((mod' . name)
  358. (cond
  359. ((and (equal? mod' mod) (assq-ref exports name))
  360. => (lambda (public-name)
  361. (make-module-ref src mod public-name #t)))
  362. (else
  363. (residualize-module-private-ref src mod' name))))))
  364. ;; A free variable reference. If it's in the program at this
  365. ;; point, that means that peval didn't see fit to copy it, so
  366. ;; there's no point in trying to do so here.
  367. (else (abort!))))
  368. (($ <toplevel-ref> src mod' name)
  369. (cond
  370. ;; Rewrite private references to exported bindings into public
  371. ;; references. Peval can decide whether to continue inlining
  372. ;; or not.
  373. ((and (equal? mod mod') (assq-ref exports name))
  374. => (lambda (public-name)
  375. (make-module-ref src mod public-name #t)))
  376. (else
  377. (residualize-module-private-ref src mod' name))))
  378. (($ <call> src proc args)
  379. (unless in-lambda? (abort!))
  380. (make-call src (recur proc) (map recur args)))
  381. (($ <primcall> src name args)
  382. (unless in-lambda? (abort!))
  383. (make-primcall src name (map recur args)))
  384. (($ <conditional> src test consequent alternate)
  385. (unless in-lambda? (abort!))
  386. (make-conditional src (recur test)
  387. (recur consequent) (recur alternate)))
  388. (($ <lexical-set> src name var exp)
  389. (unless in-lambda? (abort!))
  390. (cond
  391. ((assq-ref bound var)
  392. => (lambda (var)
  393. (make-lexical-set src name var (recur exp))))
  394. (else
  395. (abort!))))
  396. ((or ($ <toplevel-set>)
  397. ($ <module-set>)
  398. ($ <toplevel-define>))
  399. (abort!))
  400. (($ <lambda> src meta body)
  401. ;; Remove any lengthy docstring.
  402. (let ((meta (filter-map (match-lambda
  403. (('documentation . _) #f)
  404. (pair pair))
  405. meta)))
  406. (make-lambda src meta (and body (copy body bound #t)))))
  407. (($ <lambda-case> src req opt rest kw inits vars body alternate)
  408. (unless in-lambda? (abort!))
  409. (let* ((vars* (fresh-vars vars))
  410. (bound (add-bound-vars vars vars* bound)))
  411. (define (recur* exp) (copy exp bound #t))
  412. (make-lambda-case src req opt rest
  413. (match kw
  414. (#f #f)
  415. ((aok? . kws)
  416. (cons aok?
  417. (map
  418. (match-lambda
  419. ((kw name var)
  420. (list kw name (assq-ref var bound))))
  421. kws))))
  422. (map recur* inits)
  423. vars*
  424. (recur* body)
  425. (and alternate (recur alternate)))))
  426. (($ <seq> src head tail)
  427. (unless in-lambda? (abort!))
  428. (make-seq src (recur head) (recur tail)))
  429. (($ <let> src names vars vals body)
  430. (unless in-lambda? (abort!))
  431. (let* ((vars* (fresh-vars vars))
  432. (bound (add-bound-vars vars vars* bound)))
  433. (define (recur* exp) (copy exp bound #t))
  434. (make-let src names vars* (map recur vals) (recur* body))))
  435. (($ <letrec> src in-order? names vars vals body)
  436. (unless in-lambda? (abort!))
  437. (let* ((vars* (fresh-vars vars))
  438. (bound (add-bound-vars vars vars* bound)))
  439. (define (recur* exp) (copy exp bound #t))
  440. (make-letrec src in-order? names vars* (map recur* vals)
  441. (recur* body))))
  442. (($ <fix> src names vars vals body)
  443. (unless in-lambda? (abort!))
  444. (let* ((vars* (fresh-vars vars))
  445. (bound (add-bound-vars vars vars* bound)))
  446. (define (recur* exp) (copy exp bound #t))
  447. (make-fix src names vars* (map recur* vals)
  448. (recur* body))))
  449. (($ <let-values> src exp body)
  450. (unless in-lambda? (abort!))
  451. (make-let-values src (recur exp) (recur body)))
  452. (($ <prompt> src escape-only? tag body handler)
  453. (unless in-lambda? (abort!))
  454. (make-prompt src escape-only?
  455. (recur tag) (recur body) (recur handler)))
  456. (($ <abort> src tag args tail)
  457. (unless in-lambda? (abort!))
  458. (make-abort src (recur tag) (map recur args) (recur tail)))))))
  459. (define (compute-inlinable-bindings exp)
  460. "Traverse @var{exp}, extracting module-level definitions."
  461. (define-values (modules lexicals binding-lexicals bindings)
  462. (compute-module-bindings exp))
  463. (define (kwarg-ref args kw kt kf)
  464. (let lp ((args args))
  465. (match args
  466. (() (kf))
  467. ((($ <const> _ (? keyword? kw')) val . args)
  468. (if (eq? kw' kw)
  469. (kt val)
  470. (lp args)))
  471. ((_ _ . args)
  472. (lp args)))))
  473. (define (kwarg-ref/const args kw kt kf)
  474. (kwarg-ref args kw
  475. (lambda (exp)
  476. (match exp
  477. (($ <const> _ val') (kt val'))
  478. (_ (kf))))
  479. kf))
  480. (define (has-constant-initarg? args kw val)
  481. (kwarg-ref/const args kw
  482. (lambda (val')
  483. (equal? val val'))
  484. (lambda () #f)))
  485. ;; Collect declarative modules defined once in this compilation unit.
  486. (define modules-with-inlinable-exports
  487. (let lp ((defs modules) (not-inlinable '()) (inlinable '()))
  488. (match defs
  489. (() inlinable)
  490. (((mod . args) . defs)
  491. (cond ((member mod not-inlinable)
  492. (lp defs not-inlinable inlinable))
  493. ((or (assoc mod defs) ;; doubly defined?
  494. (not (has-constant-initarg? args #:declarative? #t)))
  495. (lp defs (cons mod not-inlinable) inlinable))
  496. (else
  497. (lp defs not-inlinable (cons mod inlinable))))))))
  498. ;; Omit multiply-defined bindings, and definitions not in declarative
  499. ;; modules.
  500. (define non-declarative-definitions
  501. (let lp ((bindings bindings) (non-declarative '()))
  502. (match bindings
  503. (() non-declarative)
  504. ((((and mod+name (mod . name)) . val) . bindings)
  505. (cond
  506. ((member mod+name non-declarative)
  507. (lp bindings non-declarative))
  508. ((or (assoc mod+name bindings)
  509. (not (member mod modules-with-inlinable-exports)))
  510. (lp bindings (cons mod+name non-declarative)))
  511. (else
  512. (lp bindings non-declarative)))))))
  513. (define exports
  514. (map (lambda (module)
  515. (define args (assoc-ref modules module))
  516. ;; Return list of (PRIVATE-NAME . PUBLIC-NAME) pairs.
  517. (define (extract-exports kw)
  518. (kwarg-ref/const args kw
  519. (lambda (val)
  520. (map (match-lambda
  521. ((and pair (private . public)) pair)
  522. (name (cons name name)))
  523. val))
  524. (lambda () '())))
  525. (cons module
  526. (append (extract-exports #:exports)
  527. (extract-exports #:replacements))))
  528. modules-with-inlinable-exports))
  529. ;; Compute ((PRIVATE-NAME . PUBLIC-NAME) . VALUE) pairs for each
  530. ;; module with inlinable bindings, for exported bindings only.
  531. (define inlinable-candidates
  532. (map
  533. (lambda (module)
  534. (define name-pairs (assoc-ref exports module))
  535. (define (name-pair private-name)
  536. (assq private-name name-pairs))
  537. (cons module
  538. (filter-map
  539. (match-lambda
  540. (((and mod+name (mod . name)) . val)
  541. (and (equal? module mod)
  542. (not (member mod+name non-declarative-definitions))
  543. (and=> (name-pair name)
  544. (lambda (pair) (cons pair val))))))
  545. bindings)))
  546. modules-with-inlinable-exports))
  547. (define inlinables
  548. (filter-map
  549. (match-lambda
  550. ((mod . exports)
  551. (let ((name-pairs (map car exports)))
  552. (match (filter-map
  553. (match-lambda
  554. (((private . public) . val)
  555. (match (inlinable-exp mod name-pairs lexicals
  556. binding-lexicals val)
  557. (#f #f)
  558. (val (cons public val)))))
  559. exports)
  560. (() #f)
  561. (exports (cons mod exports))))))
  562. inlinable-candidates))
  563. inlinables)
  564. (define (put-uleb port val)
  565. (let lp ((val val))
  566. (let ((next (ash val -7)))
  567. (if (zero? next)
  568. (put-u8 port val)
  569. (begin
  570. (put-u8 port (logior #x80 (logand val #x7f)))
  571. (lp next))))))
  572. (define (known-vtable vtable)
  573. (define-syntax-rule (tree-il-case vt ...)
  574. (cond
  575. ((eq? vtable vt) (values '(language tree-il) 'vt))
  576. ...
  577. (else (values #f #f))))
  578. (tree-il-case <void>
  579. <const>
  580. <primitive-ref>
  581. <lexical-ref>
  582. <lexical-set>
  583. <module-ref>
  584. <module-set>
  585. <toplevel-ref>
  586. <toplevel-set>
  587. <toplevel-define>
  588. <conditional>
  589. <call>
  590. <primcall>
  591. <seq>
  592. <lambda>
  593. <lambda-case>
  594. <let>
  595. <letrec>
  596. <fix>
  597. <let-values>
  598. <prompt>
  599. <abort>))
  600. (define-record-type <encoding>
  601. (%make-encoding constants vtables pair-code vector-code symbol-code next-code)
  602. encoding?
  603. (constants constants)
  604. (vtables vtables)
  605. (pair-code pair-code set-pair-code!)
  606. (vector-code vector-code set-vector-code!)
  607. (symbol-code symbol-code set-symbol-code!)
  608. (next-code next-code set-next-code!))
  609. (define (make-encoding)
  610. (%make-encoding (make-hash-table) (make-hash-table) #f #f #f 0))
  611. (define (vtable-nfields vtable)
  612. (define vtable-index-size 5) ; FIXME: pull from struct.h
  613. (struct-ref/unboxed vtable vtable-index-size))
  614. (define (build-encoding! term encoding)
  615. (define (next-code!)
  616. (let ((code (next-code encoding)))
  617. (set-next-code! encoding (1+ code))
  618. code))
  619. (define (intern-constant! x)
  620. (unless (hash-ref (constants encoding) x)
  621. (hash-set! (constants encoding) x (next-code!))))
  622. (define (intern-vtable! x)
  623. (unless (hashq-ref (vtables encoding) x)
  624. (hashq-set! (vtables encoding) x (next-code!))))
  625. (define (ensure-pair-code!)
  626. (unless (pair-code encoding)
  627. (set-pair-code! encoding (next-code!))))
  628. (define (ensure-vector-code!)
  629. (unless (vector-code encoding)
  630. (set-vector-code! encoding (next-code!))))
  631. (define (ensure-symbol-code!)
  632. (unless (symbol-code encoding)
  633. (set-symbol-code! encoding (next-code!))))
  634. (let visit ((term term))
  635. (cond
  636. ((pair? term)
  637. (ensure-pair-code!)
  638. (visit (car term))
  639. (visit (cdr term)))
  640. ((vector? term)
  641. (ensure-vector-code!)
  642. (visit (vector-length term))
  643. (let lp ((i 0))
  644. (when (< i (vector-length term))
  645. (visit (vector-ref term i))
  646. (lp (1+ i)))))
  647. ((symbol? term)
  648. (ensure-symbol-code!)
  649. (visit (symbol->string term)))
  650. ((struct? term)
  651. (let ((vtable (struct-vtable term)))
  652. (unless (known-vtable vtable)
  653. (error "struct of unknown type" term))
  654. (intern-vtable! vtable)
  655. (let ((nfields (vtable-nfields vtable)))
  656. (let lp ((i 0))
  657. (when (< i nfields)
  658. (visit (struct-ref term i))
  659. (lp (1+ i)))))))
  660. (else
  661. (intern-constant! term)))))
  662. (define (compute-decoder encoding)
  663. (define (pair-clause code)
  664. `((eq? code ,code)
  665. (let* ((car (lp))
  666. (cdr (lp)))
  667. (cons car cdr))))
  668. (define (vector-clause code)
  669. `((eq? code ,code)
  670. (let* ((len (lp))
  671. (v (make-vector len)))
  672. (let init ((i 0))
  673. (when (< i len)
  674. (vector-set! v i (lp))
  675. (init (1+ i))))
  676. v)))
  677. (define (symbol-clause code)
  678. `((eq? code ,code)
  679. (string->symbol (lp))))
  680. (define (vtable-clause vtable code)
  681. (call-with-values (lambda () (known-vtable vtable))
  682. (lambda (mod name)
  683. (let ((fields (map (lambda (i) (string->symbol (format #f "f~a" i)))
  684. (iota (vtable-nfields vtable)))))
  685. `((eq? code ,code)
  686. (let* (,@(map (lambda (field) `(,field (lp))) fields))
  687. (make-struct/simple (@ ,mod ,name) ,@fields)))))))
  688. (define (constant-clause constant code)
  689. `((eq? code ,code) ',constant))
  690. (define (map-encodings f table)
  691. (map (match-lambda
  692. ((value . code) (f value code)))
  693. (sort (hash-map->list cons table)
  694. (match-lambda*
  695. (((_ . code1) (_ . code2)) (< code1 code2))))))
  696. `(lambda (bv)
  697. (define pos 0)
  698. (define (next-u8!)
  699. (let ((u8 (bytevector-u8-ref bv pos)))
  700. (set! pos (1+ pos))
  701. u8))
  702. (define (next-uleb!)
  703. ,(if (< (next-code encoding) #x80)
  704. ;; No need for uleb decoding in this case.
  705. '(next-u8!)
  706. ;; FIXME: We have a maximum code length and probably we
  707. ;; should just inline the corresponding decoder instead of
  708. ;; looping.
  709. '(let lp ((n 0) (shift 0))
  710. (let ((b (next-u8!)))
  711. (if (zero? (logand b #x80))
  712. (logior (ash b shift) n)
  713. (lp (logior (ash (logxor #x80 b) shift) n)
  714. (+ shift 7)))))))
  715. (let lp ()
  716. (let ((code (next-uleb!)))
  717. (cond
  718. ,@(if (pair-code encoding)
  719. (list (pair-clause (pair-code encoding)))
  720. '())
  721. ,@(if (vector-code encoding)
  722. (list (vector-clause (vector-code encoding)))
  723. '())
  724. ,@(if (symbol-code encoding)
  725. (list (symbol-clause (symbol-code encoding)))
  726. '())
  727. ,@(map-encodings vtable-clause (vtables encoding))
  728. ,@(map-encodings constant-clause (constants encoding))
  729. (else (error "bad code" code)))))))
  730. (define (encode term encoding)
  731. (call-with-output-bytevector
  732. (lambda (port)
  733. (define (put x) (put-uleb port x))
  734. (let visit ((term term))
  735. (cond
  736. ((pair? term)
  737. (put (pair-code encoding))
  738. (visit (car term))
  739. (visit (cdr term)))
  740. ((vector? term)
  741. (put (vector-code encoding))
  742. (visit (vector-length term))
  743. (let lp ((i 0))
  744. (when (< i (vector-length term))
  745. (visit (vector-ref term i))
  746. (lp (1+ i)))))
  747. ((symbol? term)
  748. (put (symbol-code encoding))
  749. (visit (symbol->string term)))
  750. ((struct? term)
  751. (let* ((vtable (struct-vtable term))
  752. (nfields (vtable-nfields vtable)))
  753. (put (hashq-ref (vtables encoding) vtable))
  754. (let lp ((i 0))
  755. (when (< i nfields)
  756. (visit (struct-ref term i))
  757. (lp (1+ i))))))
  758. (else
  759. (put (hash-ref (constants encoding) term))))))))
  760. (define (compute-encoding bindings)
  761. (let ((encoding (make-encoding)))
  762. (for-each (match-lambda
  763. ((name . expr) (build-encoding! expr encoding)))
  764. bindings)
  765. (let ((encoded (map (match-lambda
  766. ((name . expr) (cons name (encode expr encoding))))
  767. bindings)))
  768. `(lambda (name)
  769. (define decode ,(compute-decoder encoding))
  770. (cond
  771. ,@(map (match-lambda
  772. ((name . bv)
  773. `((eq? name ',name) (decode ,bv))))
  774. encoded)
  775. (else #f))))))
  776. (define encoding-module (current-module))
  777. (define (compile-inlinable-exports bindings)
  778. (let ((exp (compute-encoding bindings)))
  779. (fix-letrec
  780. (expand-primitives
  781. (resolve-primitives
  782. (compile-tree-il exp encoding-module '())
  783. encoding-module)))))
  784. (define (attach-inlinables exp inlinables)
  785. (post-order
  786. (lambda (exp)
  787. (match exp
  788. (($ <call> src (and proc ($ <module-ref> _ '(guile) 'define-module* #f))
  789. ((and m ($ <const> _ mod)) . args))
  790. (cond
  791. ((assoc-ref inlinables mod)
  792. => (lambda (bindings)
  793. (let ((inlinables (compile-inlinable-exports bindings)))
  794. (make-call src proc
  795. (cons* m
  796. (make-const #f #:inlinable-exports)
  797. inlinables
  798. args)))))
  799. (else exp)))
  800. (exp exp)))
  801. exp))
  802. (define (inlinable-exports exp)
  803. (attach-inlinables exp (compute-inlinable-bindings exp)))