effects.scm 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  1. ;;; Effects analysis on Tree-IL
  2. ;; Copyright (C) 2011, 2012, 2013, 2021 Free Software Foundation, Inc.
  3. ;;;; This library is free software; you can redistribute it and/or
  4. ;;;; modify it under the terms of the GNU Lesser General Public
  5. ;;;; License as published by the Free Software Foundation; either
  6. ;;;; version 3 of the License, or (at your option) any later version.
  7. ;;;;
  8. ;;;; This library is distributed in the hope that it will be useful,
  9. ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. ;;;; Lesser General Public License for more details.
  12. ;;;;
  13. ;;;; You should have received a copy of the GNU Lesser General Public
  14. ;;;; License along with this library; if not, write to the Free Software
  15. ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  16. (define-module (language tree-il effects)
  17. #:use-module (language tree-il)
  18. #:use-module (language tree-il primitives)
  19. #:use-module (ice-9 match)
  20. #:export (make-effects-analyzer
  21. &mutable-lexical
  22. &toplevel
  23. &fluid
  24. &definite-bailout
  25. &possible-bailout
  26. &zero-values
  27. &allocation
  28. &type-check
  29. &all-effects
  30. effects-commute?
  31. exclude-effects
  32. effect-free?
  33. constant?
  34. depends-on-effects?
  35. causes-effects?))
  36. ;;;
  37. ;;; Hey, it's some effects analysis! If you invoke
  38. ;;; `make-effects-analyzer', you get a procedure that computes the set
  39. ;;; of effects that an expression depends on and causes. This
  40. ;;; information is useful when writing algorithms that move code around,
  41. ;;; while preserving the semantics of an input program.
  42. ;;;
  43. ;;; The effects set is represented by a bitfield, as a fixnum. The set
  44. ;;; of possible effects is modelled rather coarsely. For example, a
  45. ;;; toplevel reference to FOO is modelled as depending on the &toplevel
  46. ;;; effect, and causing a &type-check effect. If any intervening code
  47. ;;; sets any toplevel variable, that will block motion of FOO.
  48. ;;;
  49. ;;; For each effect, two bits are reserved: one to indicate that an
  50. ;;; expression depends on the effect, and the other to indicate that an
  51. ;;; expression causes the effect.
  52. ;;;
  53. ;;; Since we have more bits in a fixnum on 64-bit systems, we can be
  54. ;;; more precise without losing efficiency. On a 32-bit system, some of
  55. ;;; the more precise effects map to fewer bits.
  56. ;;;
  57. (define-syntax define-effects
  58. (lambda (x)
  59. (syntax-case x ()
  60. ((_ all name ...)
  61. (with-syntax (((n ...) (iota (length #'(name ...)))))
  62. #'(begin
  63. (define-syntax name (identifier-syntax (ash 1 (* n 2))))
  64. ...
  65. (define-syntax all (identifier-syntax (logior name ...)))))))))
  66. (define-syntax compile-time-cond
  67. (lambda (x)
  68. (syntax-case x (else)
  69. ((_ (else body ...))
  70. #'(begin body ...))
  71. ((_ (exp body ...) clause ...)
  72. (if (eval (syntax->datum #'exp) (current-module))
  73. #'(begin body ...)
  74. #'(compile-time-cond clause ...))))))
  75. ;; Here we define the effects, indicating the meaning of the effect.
  76. ;;
  77. ;; Effects that are described in a "depends on" sense can also be used
  78. ;; in the "causes" sense.
  79. ;;
  80. ;; Effects that are described as causing an effect are not usually used
  81. ;; in a "depends-on" sense. Although the "depends-on" sense is used
  82. ;; when checking for the existence of the "causes" effect, the effects
  83. ;; analyzer will not associate the "depends-on" sense of these effects
  84. ;; with any expression.
  85. ;;
  86. (compile-time-cond
  87. ((>= (logcount most-positive-fixnum) 60)
  88. (define-effects &all-effects
  89. ;; Indicates that an expression depends on the value of a mutable
  90. ;; lexical variable.
  91. &mutable-lexical
  92. ;; Indicates that an expression depends on the value of a toplevel
  93. ;; variable.
  94. &toplevel
  95. ;; Indicates that an expression depends on the value of a fluid
  96. ;; variable.
  97. &fluid
  98. ;; Indicates that an expression definitely causes a non-local,
  99. ;; non-resumable exit -- a bailout. Only used in the "changes" sense.
  100. &definite-bailout
  101. ;; Indicates that an expression may cause a bailout.
  102. &possible-bailout
  103. ;; Indicates than an expression may return zero values -- a "causes"
  104. ;; effect.
  105. &zero-values
  106. ;; Indicates that an expression may return a fresh object -- a
  107. ;; "causes" effect.
  108. &allocation
  109. ;; Indicates that an expression depends on the value of the car of a
  110. ;; pair.
  111. &car
  112. ;; Indicates that an expression depends on the value of the cdr of a
  113. ;; pair.
  114. &cdr
  115. ;; Indicates that an expression depends on the value of a vector
  116. ;; field. We cannot be more precise, as vectors may alias other
  117. ;; vectors.
  118. &vector
  119. ;; Indicates that an expression depends on the value of a variable
  120. ;; cell.
  121. &variable
  122. ;; Indicates that an expression depends on the value of a particular
  123. ;; struct field.
  124. &struct-0 &struct-1 &struct-2 &struct-3 &struct-4 &struct-5 &struct-6+
  125. ;; Indicates that an expression depends on the contents of a string.
  126. &string
  127. ;; Indicates that an expression depends on the contents of a
  128. ;; bytevector. We cannot be more precise, as bytevectors may alias
  129. ;; other bytevectors.
  130. &bytevector
  131. ;; Indicates that an expression may cause a type check. A type check,
  132. ;; for the purposes of this analysis, is the possibility of throwing
  133. ;; an exception the first time an expression is evaluated. If the
  134. ;; expression did not cause an exception to be thrown, users can
  135. ;; assume that evaluating the expression again will not cause an
  136. ;; exception to be thrown.
  137. ;;
  138. ;; For example, (+ x y) might throw if X or Y are not numbers. But if
  139. ;; it doesn't throw, it should be safe to elide a dominated, common
  140. ;; subexpression (+ x y).
  141. &type-check)
  142. ;; Indicates that an expression depends on the contents of an unknown
  143. ;; struct field.
  144. (define-syntax &struct
  145. (identifier-syntax
  146. (logior &struct-1 &struct-2 &struct-3 &struct-4 &struct-5 &struct-6+))))
  147. (else
  148. ;; For systems with smaller fixnums, be less precise regarding struct
  149. ;; fields.
  150. (define-effects &all-effects
  151. &mutable-lexical
  152. &toplevel
  153. &fluid
  154. &definite-bailout
  155. &possible-bailout
  156. &zero-values
  157. &allocation
  158. &car
  159. &cdr
  160. &vector
  161. &variable
  162. &struct
  163. &string
  164. &bytevector
  165. &type-check)
  166. (define-syntax &struct-0 (identifier-syntax &struct))
  167. (define-syntax &struct-1 (identifier-syntax &struct))
  168. (define-syntax &struct-2 (identifier-syntax &struct))
  169. (define-syntax &struct-3 (identifier-syntax &struct))
  170. (define-syntax &struct-4 (identifier-syntax &struct))
  171. (define-syntax &struct-5 (identifier-syntax &struct))
  172. (define-syntax &struct-6+ (identifier-syntax &struct))))
  173. (define-syntax &no-effects (identifier-syntax 0))
  174. ;; Definite bailout is an oddball effect. Since it indicates that an
  175. ;; expression definitely causes bailout, it's not in the set of effects
  176. ;; of a call to an unknown procedure. At the same time, it's also
  177. ;; special in that a definite bailout in a subexpression doesn't always
  178. ;; cause an outer expression to include &definite-bailout in its
  179. ;; effects. For that reason we have to treat it specially.
  180. ;;
  181. (define-syntax &all-effects-but-bailout
  182. (identifier-syntax
  183. (logand &all-effects (lognot &definite-bailout))))
  184. (define-inlinable (cause effect)
  185. (ash effect 1))
  186. (define-inlinable (&depends-on a)
  187. (logand a &all-effects))
  188. (define-inlinable (&causes a)
  189. (logand a (cause &all-effects)))
  190. (define (exclude-effects effects exclude)
  191. (logand effects (lognot (cause exclude))))
  192. (define (effect-free? effects)
  193. (zero? (&causes effects)))
  194. (define (constant? effects)
  195. (zero? effects))
  196. (define-inlinable (depends-on-effects? x effects)
  197. (not (zero? (logand (&depends-on x) effects))))
  198. (define-inlinable (causes-effects? x effects)
  199. (not (zero? (logand (&causes x) (cause effects)))))
  200. (define-inlinable (effects-commute? a b)
  201. (and (not (causes-effects? a (&depends-on b)))
  202. (not (causes-effects? b (&depends-on a)))))
  203. (define (make-effects-analyzer assigned-lexical?)
  204. "Returns a procedure of type EXP -> EFFECTS that analyzes the effects
  205. of an expression."
  206. (let ((cache (make-hash-table)))
  207. (define* (compute-effects exp #:optional (lookup (lambda (x) #f)))
  208. (define (compute-effects exp)
  209. (or (hashq-ref cache exp)
  210. (let ((effects (visit exp)))
  211. (hashq-set! cache exp effects)
  212. effects)))
  213. (define (accumulate-effects exps)
  214. (let lp ((exps exps) (out &no-effects))
  215. (if (null? exps)
  216. out
  217. (lp (cdr exps) (logior out (compute-effects (car exps)))))))
  218. (define (visit exp)
  219. (match exp
  220. (($ <const>)
  221. &no-effects)
  222. (($ <void>)
  223. &no-effects)
  224. (($ <lexical-ref> _ _ gensym)
  225. (if (assigned-lexical? gensym)
  226. &mutable-lexical
  227. &no-effects))
  228. (($ <lexical-set> _ name gensym exp)
  229. (logior (cause &mutable-lexical)
  230. (compute-effects exp)))
  231. (($ <let> _ names gensyms vals body)
  232. (logior (if (or-map assigned-lexical? gensyms)
  233. (cause &allocation)
  234. &no-effects)
  235. (accumulate-effects vals)
  236. (compute-effects body)))
  237. (($ <letrec> _ in-order? names gensyms vals body)
  238. (logior (if (or-map assigned-lexical? gensyms)
  239. (cause &allocation)
  240. &no-effects)
  241. (accumulate-effects vals)
  242. (compute-effects body)))
  243. (($ <fix> _ names gensyms vals body)
  244. (logior (if (or-map assigned-lexical? gensyms)
  245. (cause &allocation)
  246. &no-effects)
  247. (accumulate-effects vals)
  248. (compute-effects body)))
  249. (($ <let-values> _ producer consumer)
  250. (logior (compute-effects producer)
  251. (compute-effects consumer)
  252. (cause &type-check)))
  253. (($ <toplevel-ref>)
  254. (logior &toplevel
  255. (cause &type-check)))
  256. (($ <module-ref>)
  257. (logior &toplevel
  258. (cause &type-check)))
  259. (($ <module-set> _ mod name public? exp)
  260. (logior (cause &toplevel)
  261. (cause &type-check)
  262. (compute-effects exp)))
  263. (($ <toplevel-define> _ _ name exp)
  264. (logior (cause &toplevel)
  265. (compute-effects exp)))
  266. (($ <toplevel-set> _ _ name exp)
  267. (logior (cause &toplevel)
  268. (compute-effects exp)))
  269. (($ <primitive-ref>)
  270. &no-effects)
  271. (($ <conditional> _ test consequent alternate)
  272. (let ((tfx (compute-effects test))
  273. (cfx (compute-effects consequent))
  274. (afx (compute-effects alternate)))
  275. (if (causes-effects? (logior tfx (logand afx cfx))
  276. &definite-bailout)
  277. (logior tfx cfx afx)
  278. (exclude-effects (logior tfx cfx afx)
  279. &definite-bailout))))
  280. ;; Zero values.
  281. (($ <primcall> _ 'values ())
  282. (cause &zero-values))
  283. ;; Effect-free primitives.
  284. (($ <primcall> _ (or 'values 'eq? 'eqv? 'equal?) args)
  285. (accumulate-effects args))
  286. (($ <primcall> _ (or 'not 'pair? 'null? 'list? 'symbol?
  287. 'vector? 'struct? 'string? 'number?
  288. 'char?)
  289. (arg))
  290. (compute-effects arg))
  291. ;; Primitives that allocate memory.
  292. (($ <primcall> _ 'cons (x y))
  293. (logior (compute-effects x) (compute-effects y)
  294. &allocation))
  295. (($ <primcall> _ (or 'list 'vector) args)
  296. (logior (accumulate-effects args) &allocation))
  297. (($ <primcall> _ 'make-prompt-tag ())
  298. &allocation)
  299. (($ <primcall> _ 'make-prompt-tag (arg))
  300. (logior (compute-effects arg) &allocation))
  301. (($ <primcall> _ 'fluid-ref (fluid))
  302. (logior (compute-effects fluid)
  303. (cause &type-check)
  304. &fluid))
  305. (($ <primcall> _ 'fluid-set! (fluid exp))
  306. (logior (compute-effects fluid)
  307. (compute-effects exp)
  308. (cause &type-check)
  309. (cause &fluid)))
  310. (($ <primcall> _ 'push-fluid (fluid val))
  311. (logior (compute-effects fluid)
  312. (compute-effects val)
  313. (cause &type-check)
  314. (cause &fluid)))
  315. (($ <primcall> _ 'pop-fluid ())
  316. (logior (cause &fluid)))
  317. (($ <primcall> _ 'push-dynamic-state (state))
  318. (logior (compute-effects state)
  319. (cause &type-check)
  320. (cause &fluid)))
  321. (($ <primcall> _ 'pop-dynamic-state ())
  322. (logior (cause &fluid)))
  323. (($ <primcall> _ 'car (x))
  324. (logior (compute-effects x)
  325. (cause &type-check)
  326. &car))
  327. (($ <primcall> _ 'set-car! (x y))
  328. (logior (compute-effects x)
  329. (compute-effects y)
  330. (cause &type-check)
  331. (cause &car)))
  332. (($ <primcall> _ 'cdr (x))
  333. (logior (compute-effects x)
  334. (cause &type-check)
  335. &cdr))
  336. (($ <primcall> _ 'set-cdr! (x y))
  337. (logior (compute-effects x)
  338. (compute-effects y)
  339. (cause &type-check)
  340. (cause &cdr)))
  341. (($ <primcall> _ (or 'memq 'memv) (x y))
  342. (logior (compute-effects x)
  343. (compute-effects y)
  344. (cause &type-check)
  345. &car &cdr))
  346. (($ <primcall> _ 'vector-ref (v n))
  347. (logior (compute-effects v)
  348. (compute-effects n)
  349. (cause &type-check)
  350. &vector))
  351. (($ <primcall> _ 'vector-set! (v n x))
  352. (logior (compute-effects v)
  353. (compute-effects n)
  354. (compute-effects x)
  355. (cause &type-check)
  356. (cause &vector)))
  357. (($ <primcall> _ 'variable-ref (v))
  358. (logior (compute-effects v)
  359. (cause &type-check)
  360. &variable))
  361. (($ <primcall> _ 'variable-set! (v x))
  362. (logior (compute-effects v)
  363. (compute-effects x)
  364. (cause &type-check)
  365. (cause &variable)))
  366. (($ <primcall> _ '%variable-ref (v))
  367. (logior (compute-effects v)
  368. (cause &type-check) ;; For the unbound check.
  369. &variable))
  370. (($ <primcall> _ '%variable-set! (v x))
  371. (logior (compute-effects v)
  372. (compute-effects x)
  373. (cause &variable)))
  374. (($ <primcall> _ 'struct-ref (s n))
  375. (logior (compute-effects s)
  376. (compute-effects n)
  377. (cause &type-check)
  378. (match n
  379. (($ <const> _ 0) &struct-0)
  380. (($ <const> _ 1) &struct-1)
  381. (($ <const> _ 2) &struct-2)
  382. (($ <const> _ 3) &struct-3)
  383. (($ <const> _ 4) &struct-4)
  384. (($ <const> _ 5) &struct-5)
  385. (($ <const> _ _) &struct-6+)
  386. (_ &struct))))
  387. (($ <primcall> _ 'struct-set! (s n x))
  388. (logior (compute-effects s)
  389. (compute-effects n)
  390. (compute-effects x)
  391. (cause &type-check)
  392. (match n
  393. (($ <const> _ 0) (cause &struct-0))
  394. (($ <const> _ 1) (cause &struct-1))
  395. (($ <const> _ 2) (cause &struct-2))
  396. (($ <const> _ 3) (cause &struct-3))
  397. (($ <const> _ 4) (cause &struct-4))
  398. (($ <const> _ 5) (cause &struct-5))
  399. (($ <const> _ _) (cause &struct-6+))
  400. (_ (cause &struct)))))
  401. (($ <primcall> _ 'string-ref (s n))
  402. (logior (compute-effects s)
  403. (compute-effects n)
  404. (cause &type-check)
  405. &string))
  406. (($ <primcall> _ 'string-set! (s n c))
  407. (logior (compute-effects s)
  408. (compute-effects n)
  409. (compute-effects c)
  410. (cause &type-check)
  411. (cause &string)))
  412. (($ <primcall> _
  413. (or 'bytevector-u8-ref 'bytevector-s8-ref
  414. 'bytevector-u16-ref 'bytevector-u16-native-ref
  415. 'bytevector-s16-ref 'bytevector-s16-native-ref
  416. 'bytevector-u32-ref 'bytevector-u32-native-ref
  417. 'bytevector-s32-ref 'bytevector-s32-native-ref
  418. 'bytevector-u64-ref 'bytevector-u64-native-ref
  419. 'bytevector-s64-ref 'bytevector-s64-native-ref
  420. 'bytevector-ieee-single-ref 'bytevector-ieee-single-native-ref
  421. 'bytevector-ieee-double-ref 'bytevector-ieee-double-native-ref)
  422. (bv n))
  423. (logior (compute-effects bv)
  424. (compute-effects n)
  425. (cause &type-check)
  426. &bytevector))
  427. (($ <primcall> _
  428. (or 'bytevector-u8-set! 'bytevector-s8-set!
  429. 'bytevector-u16-set! 'bytevector-u16-native-set!
  430. 'bytevector-s16-set! 'bytevector-s16-native-set!
  431. 'bytevector-u32-set! 'bytevector-u32-native-set!
  432. 'bytevector-s32-set! 'bytevector-s32-native-set!
  433. 'bytevector-u64-set! 'bytevector-u64-native-set!
  434. 'bytevector-s64-set! 'bytevector-s64-native-set!
  435. 'bytevector-ieee-single-set! 'bytevector-ieee-single-native-set!
  436. 'bytevector-ieee-double-set! 'bytevector-ieee-double-native-set!)
  437. (bv n x))
  438. (logior (compute-effects bv)
  439. (compute-effects n)
  440. (compute-effects x)
  441. (cause &type-check)
  442. (cause &bytevector)))
  443. ;; Primitives that are normally effect-free, but which might
  444. ;; cause type checks or allocate memory. Nota bene,
  445. ;; primitives that access mutable memory should be given their
  446. ;; own inline cases above!
  447. (($ <primcall> _ (and name (? effect-free-primitive?)) args)
  448. (logior (accumulate-effects args)
  449. (cause &type-check)
  450. (if (constructor-primitive? name)
  451. (cause &allocation)
  452. &no-effects)))
  453. ;; Lambda applications might throw wrong-number-of-args.
  454. (($ <call> _ ($ <lambda> _ _ body) args)
  455. (logior (accumulate-effects args)
  456. (match body
  457. (($ <lambda-case> _ req #f #f #f () syms body #f)
  458. (logior (compute-effects body)
  459. (if (= (length req) (length args))
  460. 0
  461. (cause &type-check))))
  462. (($ <lambda-case>)
  463. (logior (compute-effects body)
  464. (cause &type-check)))
  465. (#f
  466. ;; Calling a case-lambda with no clauses
  467. ;; definitely causes bailout.
  468. (logior (cause &definite-bailout)
  469. (cause &possible-bailout))))))
  470. ;; Bailout primitives.
  471. (($ <primcall> _ (? bailout-primitive? name) args)
  472. (logior (accumulate-effects args)
  473. (cause &definite-bailout)
  474. (cause &possible-bailout)))
  475. (($ <call> _
  476. (and proc
  477. ($ <module-ref> _ mod name public?)
  478. (? (lambda (_)
  479. (false-if-exception
  480. (procedure-property
  481. (module-ref (if public?
  482. (resolve-interface mod)
  483. (resolve-module mod))
  484. name)
  485. 'definite-bailout?)))))
  486. args)
  487. (logior (compute-effects proc)
  488. (accumulate-effects args)
  489. (cause &definite-bailout)
  490. (cause &possible-bailout)))
  491. ;; A call to a lexically bound procedure, perhaps labels
  492. ;; allocated.
  493. (($ <call> _ (and proc ($ <lexical-ref> _ _ sym)) args)
  494. (cond
  495. ((lookup sym)
  496. => (lambda (proc)
  497. (compute-effects (make-call #f proc args))))
  498. (else
  499. (logior &all-effects-but-bailout
  500. (cause &all-effects-but-bailout)))))
  501. ;; A call to an unknown procedure can do anything.
  502. (($ <primcall> _ name args)
  503. (logior &all-effects-but-bailout
  504. (cause &all-effects-but-bailout)))
  505. (($ <call> _ proc args)
  506. (logior &all-effects-but-bailout
  507. (cause &all-effects-but-bailout)))
  508. (($ <lambda> _ meta body)
  509. &no-effects)
  510. (($ <lambda-case> _ req opt rest kw inits gensyms body alt)
  511. (logior (exclude-effects (accumulate-effects inits)
  512. &definite-bailout)
  513. (if (or-map assigned-lexical? gensyms)
  514. (cause &allocation)
  515. &no-effects)
  516. (compute-effects body)
  517. (if alt (compute-effects alt) &no-effects)))
  518. (($ <seq> _ head tail)
  519. (logior
  520. ;; Returning zero values to a for-effect continuation is
  521. ;; not observable.
  522. (exclude-effects (compute-effects head)
  523. (cause &zero-values))
  524. (compute-effects tail)))
  525. (($ <prompt> _ escape-only? tag body handler)
  526. (logior (compute-effects tag)
  527. (compute-effects (if escape-only?
  528. body
  529. (make-call #f body '())))
  530. ;; Calls handler with probably wrong argument count,
  531. ;; but that will just add a &type-check effect.
  532. (compute-effects (make-call #f handler '()))))
  533. (($ <abort> _ tag args tail)
  534. (logior &all-effects-but-bailout
  535. (cause &all-effects-but-bailout)))))
  536. (compute-effects exp))
  537. compute-effects))