diff options
author | Kaz Kylheku <kaz@kylheku.com> | 2023-04-17 15:04:45 -0700 |
---|---|---|
committer | Kaz Kylheku <kaz@kylheku.com> | 2023-04-17 15:04:45 -0700 |
commit | 559e2cace50b534d7591d7bb6f0ba68afe09dc68 (patch) | |
tree | 9c11bbdc94e44b5cd15650c57fd0ddf2d670a5e1 /stdlib | |
parent | 3508483456416a42f26bffec9c02cdad47e18765 (diff) | |
download | txr-559e2cace50b534d7591d7bb6f0ba68afe09dc68.tar.gz txr-559e2cace50b534d7591d7bb6f0ba68afe09dc68.tar.bz2 txr-559e2cace50b534d7591d7bb6f0ba68afe09dc68.zip |
compiler: better handling for mutated locals in funargs.
Instead of the conservative strategy in compiler comp-var of
loading variables into t-registers, and relying on optimization
to remove them, let's just go back to the old way: variables
are just registers. For function calls, we can detect mutated
variables and generate the conservative code.
* stdlib/compiler.tl (frag): New slots vbin and alt-oreg.
When a variable access is compiled, the binding is recorded
in vbin, and the desired output register in alt-oreg.
(simplify-var-spy): New struct type, used for detecting
mutated lexical variables when we compile a function argument
list.
(compiler comp-var): Revert to the old compilation strategy
for lexicals: the code fragment is empty, and the output
register is just the v-reg. However, we record the variable
binding and remember the caller's desired register in the
new frag fields.
(compiler comp-setq): Also revert the strategy here.
Here we get our frag from a recursive compilation, so
we just annotate it.
(compiler comp-call-impl): Use the simplify-var-spy to
obtain a list of the lexical variables that were mutated.
This is used for rewriting the frags, if necessary.
(handle-mutated-var-args): New function. If the mutated-vars
list is non-empty, it rewrites the frag list. Every element
in the frag which is a compiled reference to a lexical
variable which is mutated over the evaluation of the arg list
is substituted with a conservative frag which loads the
variable into a temporary register. That register thus
samples the value of the variable at the correct point in the
left-to-right evaluation, so the function is called with
the correct values.
Diffstat (limited to 'stdlib')
-rw-r--r-- | stdlib/compiler.tl | 69 |
1 files changed, 49 insertions, 20 deletions
diff --git a/stdlib/compiler.tl b/stdlib/compiler.tl index 9e99c9c1..b09324f9 100644 --- a/stdlib/compiler.tl +++ b/stdlib/compiler.tl @@ -60,7 +60,9 @@ code pars fvars - ffuns) + ffuns + vbin + alt-oreg) (defstruct binding nil sym @@ -216,6 +218,15 @@ (when (neq spy me) spy.(captured vbin sym))))) +(defstruct simplify-var-spy () + mutated-vars + (:method accessed (me vbin sym) + (ignore me vbin sym)) + + (:method assigned (me vbin sym) + (ignore sym) + (pushnew vbin me.mutated-vars))) + (compile-only (defstruct compiler nil (treg-cntr 2) @@ -570,7 +581,9 @@ (vbin (each ((spy me.access-spies)) spy.(accessed vbin sym)) - (new (frag oreg (maybe-mov oreg vbin.loc) (list sym)))) + (new (frag vbin.loc nil (list sym)) + vbin vbin + alt-oreg oreg)) ((special-var-p sym) (let ((dreg me.(get-dreg sym))) (new (frag oreg ^((getv ,oreg ,dreg)) (list sym))))) @@ -584,8 +597,10 @@ (bind bind.loc) (spec me.(get-dreg sym)) (t me.(get-sidx sym)))) - (vfrag me.(compile oreg env value))) + (vfrag me.(compile (if bind vloc oreg) env value))) (when bind + (set vfrag.vbin bind + vfrag.alt-oreg oreg) (each ((spy me.access-spies)) spy.(assigned bind sym))) (new (frag vfrag.oreg @@ -1476,23 +1491,25 @@ (uni ffrag.ffuns cfrag.ffuns)))))) (defmeth compiler comp-call-impl (me oreg env opcode freg args : extra-ffun) - (let* ((aoregs nil) - (afrags (collect-each ((arg args)) - (let* ((aoreg me.(alloc-treg)) - (afrag me.(compile aoreg env arg))) - (if (nequal afrag.oreg aoreg) - me.(free-treg aoreg) - (push aoreg aoregs)) - afrag))) - (fvars [reduce-left uni afrags nil .fvars]) - (ffuns [reduce-left uni afrags nil .ffuns])) - me.(free-tregs aoregs) - (when extra-ffun - (pushnew extra-ffun ffuns)) - (new (frag oreg - ^(,*(mappend .code afrags) - (,opcode ,oreg ,freg ,*(mapcar .oreg afrags))) - fvars ffuns)))) + (with-access-spy me t spy (new simplify-var-spy) + (let* ((aoregs nil) + (afrags0 (collect-each ((arg args)) + (let* ((aoreg me.(alloc-treg)) + (afrag me.(compile aoreg env arg))) + (if (nequal afrag.oreg aoreg) + me.(free-treg aoreg) + (push aoreg aoregs)) + afrag))) + (afrags (handle-mutated-var-args afrags0 spy.mutated-vars)) + (fvars [reduce-left uni afrags nil .fvars]) + (ffuns [reduce-left uni afrags nil .ffuns])) + me.(free-tregs aoregs) + (when extra-ffun + (pushnew extra-ffun ffuns)) + (new (frag oreg + ^(,*(mappend .code afrags) + (,opcode ,oreg ,freg ,*(mapcar .oreg afrags))) + fvars ffuns))))) (defmeth compiler comp-inline-lambda (me oreg env opcode lambda args) (let ((reg-args args) apply-list-arg) @@ -1673,6 +1690,18 @@ (xend ,bfrag.oreg)) bfrag.fvars bfrag.ffuns))))) +(defun handle-mutated-var-args (frags mutated-vars) + (if mutated-vars + (build + (each ((frag frags)) + (let* ((vbin frag.vbin) + (oreg frag.alt-oreg)) + (add (if (and vbin (memq vbin mutated-vars)) + (new (frag oreg (append frag.code (maybe-mov oreg vbin.loc)) + frag.fvars frag.ffuns frag.pars)) + frag))))) + frags)) + (defun misleading-ref-check (frag env form) (each ((v frag.fvars)) (when env.(lookup-var v) |