pass.lisp 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. "The pass system provides a way of reading and modifying resolved trees.
  2. Passes are split into two categories: optimisations and warnings. The
  3. former should attempt to simplify code, making it more
  4. performant. Warnings attempt to find potential bugs or stylistic issues
  5. in your code.
  6. Each pass is defined and registered with [[defpass]].
  7. ### State
  8. Every pass receives a state object. This contains various bits of
  9. information about the current compiler. Some important fields include:
  10. - `:meta`: Contains information about native definitions. This is just
  11. a mapping of variable's full names to the information given in a
  12. `.meta.*` file.
  13. - `:libs`: A list of all loaded libraries. Each library is a struct
  14. containing the library's nodes (`:out`), documentation (`:docs`),
  15. display name (`:name`) and path (`:path`).
  16. ### Usage analysis
  17. Sometimes you will need to get the definitions or usages of a
  18. variable. Firstly you'll need to include `\"usage\"` in the category
  19. list in [[defpass]]. You can then access information about the variable
  20. by using [[var-usage]]."
  21. (define pass-arg :hidden (gensym))
  22. (defmacro defpass (name args &body)
  23. "Define a pass with the given NAME and BODY taking the specified ARGS.
  24. BODY can contain key-value pairs (like [[struct]]) which will be set
  25. as options for this pass.
  26. Inside the BODY you can call [[changed!]] to mark this pass as
  27. modifying something."
  28. (let* [(main `(define ,name))
  29. (options '())
  30. (running true)
  31. (idx 1)
  32. (len (n body))]
  33. ;; If we start with a docstring then push it to both the definition
  34. ;; and struct
  35. (with (entry (car body))
  36. (when (string? entry)
  37. (push! main entry)
  38. (push! options `:help)
  39. (push! options entry)
  40. (inc! idx)))
  41. ;; Scan for all remaining entries
  42. (while (and running (<= idx len))
  43. (with (entry (nth body idx))
  44. (if (key? entry)
  45. (progn
  46. (push! options entry)
  47. (push! options (nth body (+ idx 1)))
  48. (set! idx (+ idx 2)))
  49. (set! running false))))
  50. (push! main `{ :name ,(symbol->string name)
  51. ,@options
  52. ,:run (lambda (,pass-arg ,@args) ,@(slice body idx))})
  53. main))
  54. (define-native add-pass!
  55. "Register a PASS created with [[defpass]]."
  56. :bind-to "_compiler['add-pass!']")
  57. (defmacro changed! ()
  58. "Mark this pass as having a side effect."
  59. `(.<! ,pass-arg :changed (succ (.> ,pass-arg :changed))))
  60. (define-native var-usage
  61. "Get usage information about the specified VAR. This returns a struct
  62. containing:
  63. - `:defs`: A list of all definitions. Each definition is a struct
  64. containing its type (`:tag`), the defining node `(:node`) and
  65. corresponding value (`:value`). Node that not all definitions have
  66. a value.
  67. - `:usages`: A list of most usages. This does not include usages
  68. from nodes which are considered \"dead\"."
  69. :bind-to "_compiler['var-usage']")