During semantic analysis, there's a lot more info we can track about our functions. We can then use this info for various optimization techniques, both on the AST and during IR generation.
We can track these per-arity, using the function_context. Then we can aggregate them into the final function, merging them conservatively.
Attributes
[ ] is_pure: bool
[ ] can_throw: bool
[ ] captures_args: bool
is_pure
A function can be tagged with :jank/pure? metadata, which will have the jank compiler assume this is true. We'll do this for all of the pure clojure.core fns. From there, we can infer it by looking at all the fns used within a fn. If any are not pure, neither is the current fn. We should also update the meta accordingly, when we infer.
Pure fn calls to primitive literals can be folded, during AOT compilation.
can_throw
We can tag the core fns which throw using :jank/throws? and infer from there, including usage of throw. We should also update the meta accordingly, when we infer. Any fn without the key in the meta is likely native and should be assumed to throw.
captures_args
We can use the :jank/captures-args? meta to keep track of whether args can be captured within the fn. Capturing means persisting in some fashion, such as putting it in an atom, capturing it in a closure, etc.
By knowing if a value is not captured by any fns, we can avoid boxing it and just pass its stack address around. If it were to be captured, with the stack address, that object pointer would no longer be valid as soon as that stack frame unwound.
During semantic analysis, there's a lot more info we can track about our functions. We can then use this info for various optimization techniques, both on the AST and during IR generation.
We can track these per-arity, using the
function_context
. Then we can aggregate them into the final function, merging them conservatively.Attributes
is_pure
: boolcan_throw
: boolcaptures_args
: boolis_pure
A function can be tagged with
:jank/pure?
metadata, which will have the jank compiler assume this is true. We'll do this for all of the pure clojure.core fns. From there, we can infer it by looking at all the fns used within a fn. If any are not pure, neither is the current fn. We should also update the meta accordingly, when we infer.Pure fn calls to primitive literals can be folded, during AOT compilation.
can_throw
We can tag the core fns which throw using
:jank/throws?
and infer from there, including usage ofthrow
. We should also update the meta accordingly, when we infer. Any fn without the key in the meta is likely native and should be assumed to throw.captures_args
We can use the
:jank/captures-args?
meta to keep track of whether args can be captured within the fn. Capturing means persisting in some fashion, such as putting it in an atom, capturing it in a closure, etc.By knowing if a value is not captured by any fns, we can avoid boxing it and just pass its stack address around. If it were to be captured, with the stack address, that object pointer would no longer be valid as soon as that stack frame unwound.