disassembler.scm 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717
  1. ;;; Guile bytecode disassembler
  2. ;;; Copyright (C) 2001, 2009-2010, 2012-2015, 2017-2020, 2022 Free Software Foundation, Inc.
  3. ;;;
  4. ;;; This library is free software; you can redistribute it and/or
  5. ;;; modify it under the terms of the GNU Lesser General Public
  6. ;;; License as published by the Free Software Foundation; either
  7. ;;; version 3 of the License, or (at your option) any later version.
  8. ;;;
  9. ;;; This library is distributed in the hope that it will be useful,
  10. ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. ;;; Lesser General Public License for more details.
  13. ;;;
  14. ;;; You should have received a copy of the GNU Lesser General Public
  15. ;;; License along with this library; if not, write to the Free Software
  16. ;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  17. ;;; Code:
  18. (define-module (system vm disassembler)
  19. #:use-module (language bytecode)
  20. #:use-module (system vm elf)
  21. #:use-module (system vm debug)
  22. #:use-module (system vm program)
  23. #:use-module (system vm loader)
  24. #:use-module (system base types internal)
  25. #:use-module (system foreign)
  26. #:use-module (rnrs bytevectors)
  27. #:use-module (ice-9 format)
  28. #:use-module (ice-9 match)
  29. #:use-module (ice-9 vlist)
  30. #:use-module (srfi srfi-1)
  31. #:use-module (srfi srfi-4)
  32. #:export (disassemble-program
  33. fold-program-code
  34. disassemble-image
  35. disassemble-file
  36. instruction-length
  37. instruction-has-fallthrough?
  38. instruction-relative-jump-targets
  39. instruction-stack-size-after
  40. instruction-slot-clobbers))
  41. (define (unpack-scm n)
  42. (pointer->scm (make-pointer n)))
  43. (define (unpack-s24 s)
  44. (if (zero? (logand s (ash 1 23)))
  45. s
  46. (- s (ash 1 24))))
  47. (define (unpack-s12 s)
  48. (if (zero? (logand s (ash 1 11)))
  49. s
  50. (- s (ash 1 12))))
  51. (define (unpack-s32 s)
  52. (if (zero? (logand s (ash 1 31)))
  53. s
  54. (- s (ash 1 32))))
  55. (eval-when (expand)
  56. (define-syntax-rule (u32-ref buf n)
  57. (bytevector-u32-native-ref buf (* n 4)))
  58. (define-syntax-rule (s32-ref buf n)
  59. (bytevector-s32-native-ref buf (* n 4)))
  60. (define-syntax-rule (define-op-handlers handlers make-handler)
  61. (define handlers
  62. (let ((handlers (make-vector 256 #f)))
  63. (define-syntax init-handlers
  64. (lambda (stx)
  65. #`(begin
  66. #,@(filter-map
  67. (match-lambda
  68. ((name opcode kind . word-types)
  69. (match (make-handler name kind word-types)
  70. (#f #f)
  71. (init #`(vector-set! handlers #,opcode #,init)))))
  72. (instruction-list)))))
  73. (init-handlers)
  74. handlers))))
  75. (define-op-handlers disassemblers
  76. (lambda (name kind word-types)
  77. (define (parse-first-word word type)
  78. (with-syntax ((word word))
  79. (case type
  80. ((X32)
  81. #'())
  82. ((X8_S24 X8_F24 X8_C24)
  83. #'((ash word -8)))
  84. ((X8_L24)
  85. #'((unpack-s24 (ash word -8))))
  86. ((X8_S8_I16 X8_S8_ZI16)
  87. #'((logand (ash word -8) #xff)
  88. (ash word -16)))
  89. ((X8_S12_S12
  90. X8_S12_C12
  91. X8_C12_C12
  92. X8_F12_F12)
  93. #'((logand (ash word -8) #xfff)
  94. (ash word -20)))
  95. ((X8_S12_Z12)
  96. #'((logand (ash word -8) #xfff)
  97. (unpack-s12 (ash word -20))))
  98. ((X8_S8_S8_S8
  99. X8_S8_S8_C8
  100. X8_S8_C8_S8)
  101. #'((logand (ash word -8) #xff)
  102. (logand (ash word -16) #xff)
  103. (ash word -24)))
  104. (else
  105. (error "bad head kind" type)))))
  106. (define (parse-tail-word word type n)
  107. (with-syntax ((word word) (n n))
  108. (case type
  109. ((C32 I32 A32 B32 AU32 BU32 AS32 BS32 AF32 BF32)
  110. #'(1 word))
  111. ((N32 R32 L32 LO32)
  112. #'(1 (unpack-s32 word)))
  113. ((C8_C24 C8_S24)
  114. #'(1
  115. (logand word #xff)
  116. (ash word -8)))
  117. ((C16_C16)
  118. #'(1
  119. (logand word #xffff)
  120. (ash word -16)))
  121. ((B1_C7_L24)
  122. #'(1
  123. (not (zero? (logand word #x1)))
  124. (logand (ash word -1) #x7f)
  125. (unpack-s24 (ash word -8))))
  126. ((B1_X7_S24 B1_X7_F24 B1_X7_C24)
  127. #'(1
  128. (not (zero? (logand word #x1)))
  129. (ash word -8)))
  130. ((B1_X7_L24)
  131. #'(1
  132. (not (zero? (logand word #x1)))
  133. (unpack-s24 (ash word -8))))
  134. ((B1_X31)
  135. #'(1 (not (zero? (logand word #x1)))))
  136. ((X8_S24 X8_F24 X8_C24)
  137. #'(1 (ash word -8)))
  138. ((X8_L24)
  139. #'(1 (unpack-s24 (ash word -8))))
  140. ((V32_X8_L24)
  141. #'((+ 1 word)
  142. (let ((v (make-vector word))
  143. (base (+ offset n 1)))
  144. (let lp ((i 0))
  145. (when (< i word)
  146. (vector-set! v i
  147. (unpack-s24 (ash (u32-ref buf (+ base i)) -8)))
  148. (lp (1+ i))))
  149. v)))
  150. (else
  151. (error "bad tail kind" type)))))
  152. (match word-types
  153. ((first-word . tail-words)
  154. (let ((vars (generate-temporaries tail-words))
  155. (word-offsets (map 1+ (iota (length tail-words)))))
  156. (with-syntax ((name (datum->syntax #'nowhere name))
  157. ((word* ...) vars)
  158. ((n ...) word-offsets)
  159. ((asm ...)
  160. (parse-first-word #'first first-word))
  161. (((len asm* ...) ...)
  162. (map parse-tail-word vars tail-words word-offsets)))
  163. #'(lambda (buf offset first)
  164. (let ((word* (u32-ref buf (+ offset n)))
  165. ...)
  166. (values (+ 1 len ...)
  167. (list 'name asm ... asm* ... ...))))))))))
  168. ;; -> len list
  169. (define (disassemble-one buf offset)
  170. (let ((first (u32-ref buf offset)))
  171. (match (vector-ref disassemblers (logand first #xff))
  172. (#f (error "bad instruction" (logand first #xff) first buf offset))
  173. (disassemble (disassemble buf offset first)))))
  174. (define (u32-offset->addr offset context)
  175. "Given an offset into an image in 32-bit units, return the absolute
  176. address of that offset."
  177. (+ (debug-context-base context) (* offset 4)))
  178. (define immediate-tag-annotations '())
  179. (define-syntax-rule (define-immediate-tag-annotation name pred mask tag)
  180. (set! immediate-tag-annotations
  181. (cons `((,mask ,tag)
  182. ,(cond
  183. ('pred => symbol->string)
  184. (else (string-append "eq-" (symbol->string 'name) "?"))))
  185. immediate-tag-annotations)))
  186. (visit-immediate-tags define-immediate-tag-annotation)
  187. (define heap-tag-annotations '())
  188. (define-syntax-rule (define-heap-tag-annotation name pred mask tag)
  189. (set! heap-tag-annotations
  190. (cons `((,mask ,tag) ,(symbol->string 'pred)) heap-tag-annotations)))
  191. (visit-heap-tags define-heap-tag-annotation)
  192. (define (sign-extended-immediate uimm n)
  193. (unpack-scm
  194. (if (>= uimm (ash 1 (- n 1)))
  195. (let ((word-bits (* (sizeof '*) 8))) ; FIXME
  196. (logand (1- (ash 1 word-bits))
  197. (- uimm (ash 1 n))))
  198. uimm)))
  199. (define (code-annotation code len offset start labels context push-addr!)
  200. ;; FIXME: Print names for register loads and stores that correspond to
  201. ;; access to named locals.
  202. (define (reference-scm target)
  203. (unpack-scm (u32-offset->addr (+ offset target) context)))
  204. (define (dereference-scm target)
  205. (let ((addr (u32-offset->addr (+ offset target)
  206. context)))
  207. (pointer->scm
  208. (dereference-pointer (make-pointer addr)))))
  209. (define (intrinsic-name index)
  210. (and=> (intrinsic-index->name index)
  211. (compose list symbol->string)))
  212. (match code
  213. (((or 'j 'je 'jl 'jge 'jne 'jnl 'jnge) target)
  214. (list "-> ~A" (vector-ref labels (- (+ offset target) start))))
  215. (('immediate-tag=? _ mask tag)
  216. (assoc-ref immediate-tag-annotations (list mask tag)))
  217. (('heap-tag=? _ mask tag)
  218. (assoc-ref heap-tag-annotations (list mask tag)))
  219. (('prompt tag escape-only? proc-slot handler)
  220. ;; The H is for handler.
  221. (list "H -> ~A" (vector-ref labels (- (+ offset handler) start))))
  222. (((or 'make-immediate 'eq-immediate?) _ imm)
  223. (list "~S" (sign-extended-immediate imm 16)))
  224. (((or 'make-short-immediate 'make-long-immediate) _ imm)
  225. (list "~S" (unpack-scm imm)))
  226. (('make-long-long-immediate _ high low)
  227. (list "~S" (unpack-scm (logior (ash high 32) low))))
  228. (('assert-nargs-ee/locals nargs locals)
  229. ;; The nargs includes the procedure.
  230. (list "~a slot~:p (~a arg~:p)" (+ locals nargs) (1- nargs)))
  231. (('bind-optionals nargs)
  232. (list "~a args~:p" (1- nargs)))
  233. (('alloc-frame nlocals)
  234. (list "~a slot~:p" nlocals))
  235. (('reset-frame nlocals)
  236. (list "~a slot~:p" nlocals))
  237. (('bind-rest dst)
  238. (list "~a slot~:p" (1+ dst)))
  239. (('make-closure dst target nfree)
  240. (let* ((addr (u32-offset->addr (+ offset target) context))
  241. (pdi (find-program-debug-info addr context))
  242. (name (or (and pdi (program-debug-info-name pdi))
  243. "anonymous procedure")))
  244. (push-addr! addr name)
  245. (list "~A at #x~X (~A free var~:p)" name addr nfree)))
  246. (('load-label dst src)
  247. (let* ((addr (u32-offset->addr (+ offset src) context))
  248. (pdi (find-program-debug-info addr context))
  249. (name (or (and pdi (program-debug-info-name pdi))
  250. "anonymous procedure")))
  251. (push-addr! addr name)
  252. (list "~A at #x~X" name addr)))
  253. (('call-label closure nlocals target)
  254. (let* ((addr (u32-offset->addr (+ offset target) context))
  255. (pdi (find-program-debug-info addr context))
  256. (name (or (and pdi (program-debug-info-name pdi))
  257. "anonymous procedure")))
  258. (push-addr! addr name)
  259. (list "~A at #x~X" name addr)))
  260. (('tail-call-label target)
  261. (let* ((addr (u32-offset->addr (+ offset target) context))
  262. (pdi (find-program-debug-info addr context))
  263. (name (or (and pdi (program-debug-info-name pdi))
  264. "anonymous procedure")))
  265. (push-addr! addr name)
  266. (list "~A at #x~X" name addr)))
  267. ;; intrinsics
  268. (('call-thread index)
  269. (intrinsic-name index))
  270. (('call-thread-scm _ index)
  271. (intrinsic-name index))
  272. (('call-thread-scm-scm _ _ index)
  273. (intrinsic-name index))
  274. (('call-scm-sz-u32 _ _ index)
  275. (intrinsic-name index))
  276. (('call-scm<-thread _ index)
  277. (intrinsic-name index))
  278. (('call-scm<-u64 _ _ index)
  279. (intrinsic-name index))
  280. (('call-scm<-s64 _ _ index)
  281. (intrinsic-name index))
  282. (('call-scm<-scm _ _ index)
  283. (intrinsic-name index))
  284. (('call-u64<-scm _ _ index)
  285. (intrinsic-name index))
  286. (('call-s64<-scm _ _ index)
  287. (intrinsic-name index))
  288. (('call-f64<-scm _ _ index)
  289. (intrinsic-name index))
  290. (('call-scm<-scm-scm _ _ _ index)
  291. (intrinsic-name index))
  292. (('call-scm<-scm-uim _ _ _ index)
  293. (intrinsic-name index))
  294. (('call-scm<-scm-u64 _ _ _ index)
  295. (intrinsic-name index))
  296. (('call-scm-scm _ _ index)
  297. (intrinsic-name index))
  298. (('call-scm-scm-scm _ _ _ index)
  299. (intrinsic-name index))
  300. (('call-scm-uimm-scm _ _ _ index)
  301. (intrinsic-name index))
  302. (('call-scm<-scm-uimm _ _ _ index)
  303. (intrinsic-name index))
  304. (('call-scm<-scmn-scmn _ _ _ index)
  305. (intrinsic-name index))
  306. (('make-non-immediate dst target)
  307. (let ((val (reference-scm target)))
  308. (when (program? val)
  309. (push-addr! (program-code val) val))
  310. (list "~@Y" val)))
  311. (((or 'throw/value 'throw/value+data) dst target)
  312. (list "~@Y" (reference-scm target)))
  313. (('builtin-ref dst idx)
  314. (list "~A" (builtin-index->name idx)))
  315. (((or 'static-ref 'static-set!) _ target)
  316. (list "~@Y" (dereference-scm target)))
  317. (('resolve-module dst name public)
  318. (list "~a" (if (zero? public) "private" "public")))
  319. (('load-typed-array dst type shape target len)
  320. (let ((addr (u32-offset->addr (+ offset target) context)))
  321. (list "~a bytes from #x~X" len addr)))
  322. (_ #f)))
  323. (define (compute-labels bv start end)
  324. (let ((labels (make-vector (- end start) #f)))
  325. (define (add-label! pos header)
  326. (unless (vector-ref labels (- pos start))
  327. (vector-set! labels (- pos start) header)))
  328. (let lp ((offset start))
  329. (when (< offset end)
  330. (call-with-values (lambda () (disassemble-one bv offset))
  331. (lambda (len elt)
  332. (match elt
  333. ((inst arg ...)
  334. (case inst
  335. ((j je jl jge jne jnl jnge)
  336. (match arg
  337. ((_ ... target)
  338. (add-label! (+ offset target) "L"))))
  339. ((prompt)
  340. (match arg
  341. ((_ ... target)
  342. (add-label! (+ offset target) "H"))))
  343. ((jtable)
  344. (match arg
  345. ((_ ... targets)
  346. (let ((len (vector-length targets)))
  347. (let lp ((i 0))
  348. (when (< i len)
  349. (add-label! (+ offset (vector-ref targets i)) "L")
  350. (lp (1+ i)))))))))))
  351. (lp (+ offset len))))))
  352. (let lp ((offset start) (n 1))
  353. (when (< offset end)
  354. (let* ((pos (- offset start))
  355. (label (vector-ref labels pos)))
  356. (if label
  357. (begin
  358. (vector-set! labels
  359. pos
  360. (string->symbol
  361. (string-append label (number->string n))))
  362. (lp (1+ offset) (1+ n)))
  363. (lp (1+ offset) n)))))
  364. labels))
  365. (define (print-info port addr label info extra src)
  366. (when label
  367. (format port "~A:\n" label))
  368. (format port "~4@S ~32S~@[;; ~1{~@?~}~]~@[~61t at ~a~]\n"
  369. addr info extra src))
  370. (define (disassemble-buffer port bv start end context push-addr!)
  371. (let ((labels (compute-labels bv start end))
  372. (sources (find-program-sources (u32-offset->addr start context)
  373. context)))
  374. (define (lookup-source addr)
  375. (let lp ((sources sources))
  376. (match sources
  377. (() #f)
  378. ((source . sources)
  379. (let ((pc (source-pre-pc source)))
  380. (cond
  381. ((< pc addr) (lp sources))
  382. ((= pc addr)
  383. (format #f "~a:~a:~a"
  384. (or (source-file source) "(unknown file)")
  385. (source-line-for-user source)
  386. (source-column source)))
  387. (else #f)))))))
  388. (let lp ((offset start))
  389. (when (< offset end)
  390. (call-with-values (lambda () (disassemble-one bv offset))
  391. (lambda (len elt)
  392. (let ((pos (- offset start))
  393. (addr (u32-offset->addr offset context))
  394. (annotation (code-annotation elt len offset start labels
  395. context push-addr!)))
  396. (print-info port pos (vector-ref labels pos) elt annotation
  397. (lookup-source addr))
  398. (lp (+ offset len)))))))))
  399. (define* (disassemble-addr addr label port #:optional (seen (make-hash-table)))
  400. (format port "Disassembly of ~A at #x~X:\n\n" label addr)
  401. (cond
  402. ((find-program-debug-info addr)
  403. => (lambda (pdi)
  404. (let ((worklist '()))
  405. (define (push-addr! addr label)
  406. (unless (hashv-ref seen addr)
  407. (hashv-set! seen addr #t)
  408. (set! worklist (acons addr label worklist))))
  409. (disassemble-buffer port
  410. (program-debug-info-image pdi)
  411. (program-debug-info-u32-offset pdi)
  412. (program-debug-info-u32-offset-end pdi)
  413. (program-debug-info-context pdi)
  414. push-addr!)
  415. (for-each (match-lambda
  416. ((addr . label)
  417. (display "\n----------------------------------------\n"
  418. port)
  419. (disassemble-addr addr label port seen)))
  420. worklist))))
  421. (else
  422. (format port "Debugging information unavailable.~%")))
  423. (values))
  424. (define* (disassemble-program program #:optional (port (current-output-port)))
  425. (disassemble-addr (program-code program) program port))
  426. (define (fold-code-range proc seed bv start end context raw?)
  427. (define (cook code offset)
  428. (define (reference-scm target)
  429. (unpack-scm (u32-offset->addr (+ offset target) context)))
  430. (define (dereference-scm target)
  431. (let ((addr (u32-offset->addr (+ offset target)
  432. context)))
  433. (pointer->scm
  434. (dereference-pointer (make-pointer addr)))))
  435. (match code
  436. (((or 'make-short-immediate 'make-long-immediate) dst imm)
  437. `(,(car code) ,dst ,(unpack-scm imm)))
  438. (('make-long-long-immediate dst high low)
  439. `(make-long-long-immediate ,dst
  440. ,(unpack-scm (logior (ash high 32) low))))
  441. (('make-closure dst target nfree)
  442. `(make-closure ,dst
  443. ,(u32-offset->addr (+ offset target) context)
  444. ,nfree))
  445. (('load-label dst src)
  446. `(load-label ,dst ,(u32-offset->addr (+ offset src) context)))
  447. (('make-non-immediate dst target)
  448. `(make-non-immediate ,dst ,(reference-scm target)))
  449. (('builtin-ref dst idx)
  450. `(builtin-ref ,dst ,(builtin-index->name idx)))
  451. (((or 'static-ref 'static-set!) dst target)
  452. `(,(car code) ,dst ,(dereference-scm target)))
  453. (_ code)))
  454. (let lp ((offset start) (seed seed))
  455. (cond
  456. ((< offset end)
  457. (call-with-values (lambda () (disassemble-one bv offset))
  458. (lambda (len elt)
  459. (lp (+ offset len)
  460. (proc (if raw? elt (cook elt offset))
  461. seed)))))
  462. (else seed))))
  463. (define* (fold-program-code proc seed program-or-addr #:key raw?)
  464. (cond
  465. ((find-program-debug-info (if (program? program-or-addr)
  466. (program-code program-or-addr)
  467. program-or-addr))
  468. => (lambda (pdi)
  469. (fold-code-range proc seed
  470. (program-debug-info-image pdi)
  471. (program-debug-info-u32-offset pdi)
  472. (program-debug-info-u32-offset-end pdi)
  473. (program-debug-info-context pdi)
  474. raw?)))
  475. (else seed)))
  476. (define* (disassemble-image bv #:optional (port (current-output-port)))
  477. (let* ((ctx (debug-context-from-image bv))
  478. (base (debug-context-text-base ctx)))
  479. (for-each-elf-symbol
  480. ctx
  481. (lambda (sym)
  482. (let ((name (elf-symbol-name sym))
  483. (value (elf-symbol-value sym))
  484. (size (elf-symbol-size sym)))
  485. (format port "Disassembly of ~A at #x~X:\n\n"
  486. (if (and (string? name) (not (string-null? name)))
  487. name
  488. "<unnamed function>")
  489. (+ base value))
  490. (disassemble-buffer port
  491. bv
  492. (/ (+ base value) 4)
  493. (/ (+ base value size) 4)
  494. ctx
  495. (lambda (addr name) #t))
  496. (display "\n\n" port)))))
  497. (values))
  498. (define* (disassemble-file file #:optional (port (current-output-port)))
  499. (let* ((thunk (load-thunk-from-file file))
  500. (elf (find-mapped-elf-image (program-code thunk))))
  501. (disassemble-image elf port)))
  502. (define-syntax instruction-lengths-vector
  503. (lambda (x)
  504. (syntax-case x ()
  505. ((_)
  506. (let ((lengths (make-vector 256 #f)))
  507. (for-each (match-lambda
  508. ((name opcode kind word ... 'V32_X8_L24)
  509. ;; Indicate variable-length instruction by setting
  510. ;; statically known length to 0.
  511. (vector-set! lengths opcode 0))
  512. ((name opcode kind words ...)
  513. (vector-set! lengths opcode (* 4 (length words)))))
  514. (instruction-list))
  515. (datum->syntax x lengths))))))
  516. (define (instruction-length code pos)
  517. (unless (zero? (modulo pos 4))
  518. (error "invalid pos"))
  519. (let ((opcode (logand (bytevector-u32-native-ref code pos) #xff)))
  520. (match (vector-ref (instruction-lengths-vector) opcode)
  521. (#f (error "Unknown opcode" opcode))
  522. (0 (call-with-values (lambda ()
  523. (let ((offset (/ pos 4)))
  524. (disassemble-one code offset)))
  525. (lambda (u32-len disasm)
  526. (* u32-len 4))))
  527. (len len))))
  528. (define-syntax static-opcode-set
  529. (lambda (x)
  530. (define (instruction-opcode inst)
  531. (cond
  532. ((assq inst (instruction-list))
  533. => (match-lambda ((name opcode . _) opcode)))
  534. (else
  535. (error "unknown instruction" inst))))
  536. (syntax-case x ()
  537. ((static-opcode-set inst ...)
  538. (let ((bv (make-bitvector 256 #f)))
  539. (for-each (lambda (inst)
  540. (bitvector-set-bit! bv (instruction-opcode inst)))
  541. (syntax->datum #'(inst ...)))
  542. (datum->syntax #'static-opcode-set bv))))))
  543. (define (instruction-has-fallthrough? code pos)
  544. (define non-fallthrough-set
  545. (static-opcode-set halt
  546. throw throw/value throw/value+data
  547. tail-call tail-call-label
  548. return-values
  549. subr-call foreign-call continuation-call
  550. j jtable))
  551. (let ((opcode (logand (bytevector-u32-native-ref code pos) #xff)))
  552. (bitvector-bit-clear? non-fallthrough-set opcode)))
  553. (define (word-offset->byte-offset n)
  554. (* n 4))
  555. (define-op-handlers jump-parsers
  556. (lambda (op kind word-types)
  557. (case op
  558. ((prompt j je jl jge jne jnl jnge)
  559. #'(lambda (code pos)
  560. (call-with-values (lambda () (disassemble-one code (/ pos 4)))
  561. (lambda (len disasm)
  562. (match disasm
  563. ;; Assume that the target is in the last word, as a
  564. ;; word offset.
  565. ((_ ___ target) (list (word-offset->byte-offset target))))))))
  566. ((jtable)
  567. #'(lambda (code pos)
  568. (call-with-values (lambda () (disassemble-one code (/ pos 4)))
  569. (lambda (len disasm)
  570. (match disasm
  571. ;; Assume that the target is in the last word, as a
  572. ;; vector of word offsets.
  573. ((_ ___ targets)
  574. (map word-offset->byte-offset (vector->list targets))))))))
  575. (else #f))))
  576. (define (instruction-relative-jump-targets code pos)
  577. (let ((opcode (logand (bytevector-u32-native-ref code pos) #xff)))
  578. (match (vector-ref jump-parsers opcode)
  579. (#f '())
  580. (proc (proc code pos)))))
  581. (define-op-handlers stack-effect-parsers
  582. (lambda (name kind word-types)
  583. (case name
  584. ((push)
  585. #'(lambda (code pos size) (and size (+ size 1))))
  586. ((pop)
  587. #'(lambda (code pos size) (and size (- size 1))))
  588. ((drop)
  589. #'(lambda (code pos size)
  590. (let ((count (ash (bytevector-u32-native-ref code pos) -8)))
  591. (and size (- size count)))))
  592. ((alloc-frame reset-frame bind-optionals)
  593. #'(lambda (code pos size)
  594. (let ((nlocals (ash (bytevector-u32-native-ref code pos) -8)))
  595. nlocals)))
  596. ((receive)
  597. #'(lambda (code pos size)
  598. (let ((nlocals (ash (bytevector-u32-native-ref code (+ pos 4))
  599. -8)))
  600. nlocals)))
  601. ((bind-kwargs)
  602. #'(lambda (code pos size)
  603. (let ((ntotal (ash (bytevector-u32-native-ref code (+ pos 8)) -8)))
  604. ntotal)))
  605. ((bind-rest)
  606. #'(lambda (code pos size)
  607. (let ((dst (ash (bytevector-u32-native-ref code pos) -8)))
  608. (+ dst 1))))
  609. ((assert-nargs-ee/locals)
  610. #'(lambda (code pos size)
  611. (let ((nargs (logand (ash (bytevector-u32-native-ref code pos) -8)
  612. #xfff))
  613. (nlocals (ash (bytevector-u32-native-ref code pos) -20)))
  614. (+ nargs nlocals))))
  615. ((call call-label tail-call tail-call-label expand-apply-argument)
  616. #'(lambda (code pos size) #f))
  617. ((shuffle-down)
  618. #'(lambda (code pos size)
  619. (let ((from (logand (ash (bytevector-u32-native-ref code pos) -8)
  620. #xfff))
  621. (to (ash (bytevector-u32-native-ref code pos) -20)))
  622. (and size (- size (- from to))))))
  623. (else
  624. #f))))
  625. (define (instruction-stack-size-after code pos size)
  626. (let ((opcode (logand (bytevector-u32-native-ref code pos) #xff)))
  627. (match (vector-ref stack-effect-parsers opcode)
  628. (#f size)
  629. (proc (proc code pos size)))))
  630. (define-op-handlers clobber-parsers
  631. (lambda (name kind word-types)
  632. (match kind
  633. ('!
  634. (case name
  635. ((call call-label)
  636. #'(lambda (code pos nslots-in nslots-out)
  637. (call-with-values
  638. (lambda ()
  639. (disassemble-one code (/ pos 4)))
  640. (lambda (len elt)
  641. (define frame-size 3)
  642. (match elt
  643. ((_ proc . _)
  644. (let lp ((slot (- proc frame-size)))
  645. (if (and nslots-in (< slot nslots-in))
  646. (cons slot (lp (1+ slot)))
  647. '()))))))))
  648. (else #f)))
  649. ('<-
  650. #`(lambda (code pos nslots-in nslots-out)
  651. (call-with-values (lambda ()
  652. (disassemble-one code (/ pos 4)))
  653. (lambda (len elt)
  654. (match elt
  655. ((_ dst . _)
  656. #,(match word-types
  657. (((or 'X8_F24 'X8_F12_F12) . _)
  658. #'(list dst))
  659. (else
  660. #'(if nslots-out
  661. (list (- nslots-out 1 dst))
  662. '()))))))))))))
  663. (define (instruction-slot-clobbers code pos nslots-in nslots-out)
  664. (let ((opcode (logand (bytevector-u32-native-ref code pos) #xff)))
  665. (match (vector-ref clobber-parsers opcode)
  666. (#f '())
  667. (proc (proc code pos nslots-in nslots-out)))))