Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clean up frame->work lifetime #487

Merged
merged 16 commits into from
Jan 10, 2017
Merged

Clean up frame->work lifetime #487

merged 16 commits into from
Jan 10, 2017

Conversation

jnthn
Copy link
Member

@jnthn jnthn commented Jan 6, 2017

This work ensures that the lifetime of ->work (a frame's working space) is strictly that of its lifetime on the call stack, modulo the case where the frame is not on the stack but its working storage lives on due to it having been captured in a continuation.

To achieve this, two VM features unused by Perl 6 have been eliminated:

  • Taking register references (unused; intended for localref which was also unused and never implemented by any other backend; this will likely be a relief to those implementing said other backends)
  • The partial (but already decidedly broken) bits of work towards supporting multi-shot continuations

With the lifetime clarified, it was possible to simplify a number of code paths, some of them hot. This also means memory can be freed sooner, reducing overhead and memory pressure.

This work leads to around 20% less time and 10% less memory in the CORE.setting build.

if (frame->continuation_tags)
MVM_continuation_free_tags(tc, frame);
if (frame->env)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could make the diff a tiny bit smaller (easier to digest?) by moving this back up before the continuation_free_tags part.

These meant that we could not rely on ->work no longer being needed
after the frame's execution was completed. This implied that, short of
static analysis to check these ops were not used, we could not be sure
it was OK to throw out ->work right after the frame's dynamic scope
ended. The ops were not being used in Rakudo, nor in NQP besides in
the code that compiled into them, which was already been removed. They
only existed to allow lowering of lexicalref => localref at some point
in the future, but an optimization that pessimizes the world is hardly
a win. It would also have been a huge pain for the JVM backend, and
likely the JS backend too, to implement localref (neither did so).
If the frame is on the call stack, we don't need to NULL stuff in it;
it's going away anyway. We also eliminate a duplicate call to the
function MVM_args_proc_cleanup_for_cache for the callstack case (the
MVM_args_proc_cleanup in MVM_frame_destory calls it anyway). We may
have continuation tags in a frame that was never promoted, since we
may add tags but never actually take a continuation (a `gather` that
never does `take` would be such an example), so we take care not to
leak those. That duplication can be cleaned up a little later.
Unused by anything in Rakudo, and in NQP only used in a test case. It
was added because nqp-j had the op, and MoarVM continuations were done
to the same API. However, we never used it besides that test case, and
it didn't really work properly anyway because we didn't need it, so it
is a bit hard to imagine any unknown users seriously relying on it.
The removal of this will help to simplify ->work lifetime management.
One-shot is all that Rakudo needs for gather/take, and will suffice
for non-blocking `await` also. We never really properly handled the
multi-shot case anyway, out of not needing it. And our incomplete
attempts to support it came with some nasty memory management risks.
The only non-trivial test for this in the NQP test suite already was
failing, so there's no loss there. This protection will also be useful
for catching bugs when we introduce non-blocking `await` in Rakudo, as
any double invocation there will surely be a screw-up.
We no longer need the `in_continuation` flag, which eliminates the
code to set it, the storage (though due to alignment we won't really
save anything) and the code to check it. It was checked every time a
callframe was taken off the stack, which adds up over a program's
lifetime. Furthermore, we can now unconditionally clear up any
continuation tags.
This is safe to do unconditionally now that continuations are one-shot
and register references are no more. This brings forward when memory
can be released, and also prevents things from lingering in the
inter-generational set for so long (we now really only ever do that
when the frame is still in dynamic scope - for very long-lived and
heap-promoted frames - since we don't enforce write barriers on
registers).
It now reliably provides this information.
We used to use it to track whether a frame is in dynamic scope, but
now the `work` field can be reliably used for that. This shaves a
memory write off every frame setup, but more significantly cuts the
size of every MVMFrame by 4 (32-bit) or 8 (64-bit) bytes, which will
reduce memory pressure.
Reduce to one function, removing the confusingly-named "for_cache"
form that did only part of the work.
If the frame is no longer in dynamic socpe, we can't possibly have any
args to care about.
Thanks to GC using this to guard against all scanning of things that
are only alive in dynamic scope, there's no reason to also NULL out
cur_args_callsite.
We never look at this number to decide anything except how much memory
to free, which only happens once.
The ->work flag prevents us ever looking at these in GC after the
lifetime of a frame is over, so this isn't needed.
Since if the frame was taken as part of a continuation that was then
never invoked, its work needs cleaning up.
@jnthn jnthn merged commit f712c6a into master Jan 10, 2017
@jnthn jnthn deleted the work-lifetime branch January 10, 2017 12:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants