-
Notifications
You must be signed in to change notification settings - Fork 39
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
prevent RVO and in-place construction via an MIR pass #815
prevent RVO and in-place construction via an MIR pass #815
Conversation
`mnkConsume` nodes were incorrectly ignored, leading to the `unreachable` statement being erroneously triggered.
It's purpose was to prevent problematic in-place constructions, but the logic is obsolete now with the introduction of the MIR pass.
This is the responsibility of the new MIR pass now.
A temporary is now injected by the MIR pass, meaning that a `genObjConstr` must only create one for refs and when there's no destination. In-place construction for seq constructions (e.g., `a = @[1, 2]`) is removed. It had little to no benefit (assigning the sequence is just a pointer + int assignment).
This is not fit for merging yet -- I've found some implementation bugs that need to be fixed first. The PR is still ready for review, however. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only found some doc typos, they aren't blockers.
The tests are nice and thorough, the new pass seems tighter and easier to follow.
The path operator were still missing.
The set was missing various operators, and the documentation was missing a note about only the immediate operator being looked at. While `mnkNone` isn't really something that produces an r-value, including it here makes handling `mnkNone` arguments easier.
The set of allowed nodes was missing a lot of nodes. Empty arg-blocks weren't properly considered, too.
It is dead code.
Thank you for the review, @saem! The logic unfortunately ended up more complex than I had hoped, but I think that this can be alleviated/remedied through some adjustments of the MIR's design. I've fixed the issues I mentioned I had noticed. They were mostly with sets missing some node kinds. |
/merge |
Merge requested by: @zerbina Contents after the first section break of the PR description has been removed and preserved below:
|
Summary
Add a MIR pass that injects write-to-temporaries where necessary, which,
if enabled, guarantees to the C code generator that it can always use
RVO and in-place aggregate construction. This removes the dependency on
isPartOf
fromcgen
, makes the return-value and in-place-constructionoptimization related logic backend-agnostic, and also adds a basic
framework for MIR passes.
In theory, the VM code-generator could also use the in-place aggregate
construction optimization now.
In addition, multiple bugs where the in-place construction optimization
was not correctly prevented when using the C backend are fixed:
Details
In preparation for the introduction of more MIR passes, a dedicated
module with the very basic framework is added. The only public routine
is the
applyPasses
procedure, which runs all passes enabled for thespecified backend. Since the targeted backend can be different from the
one selected by the user (because of compile-time execution), a
dedicated enum is used.
Due to their similarity in processing, the temporary injection for calls
and aggregate constructions is combined into a single pass. The pass
works by searching for assignments where the source operand is the
result of a call or aggregate construction and then running an analysis
for whether the assignment destination is used in a way that prevents
RVO or in-place construction. If the destination does, the source r-
value is assigned to an intermediate temporary first.
In the abstract, the used analysis works similar to
isPartOf
, but itis, apart from the type-analysis, implemented from scratch for the MIR.
transf
For the purpose of making assignments like
x = (x.a, x.b)
work whenusing the C backend,
transf
previously unpacked the literal tupleconstruction expression and then repacked it, like so:
While this does what is intended, it introduced an left-to-right
evaluation-order violation when the
x
has side effects. The new MIRpass taking care of introducing a temporary allows for the removal of
transformAsgn
, fixing the aforementioned issue and slightly reducingthe amount of work for the
injectdestructors
pass (because there areless locals to analyze).
The removal does have an unintended side-effect: r-value inputs to
literal tuple construction expressions are not properly destroyed when
an exception is raised (see the changed
ttuple_unpacking.nim
test).This is a known and documented issue for array and object constructions,
which now also affects tuple constructions.
cgen
Calls that are the source operand to an assignment and return the result
via an out parameter always use the assignment destination as their
Result
argument now; thepreventNrvo
procedure is renamed toreportObservableStore
and the related injection of temporariesremoved.
In
genObjConstr
, a temporary now only has to be created forref
s andwhen the destination is an unset location.
For literal
seq
construction expressions (e.g.,@[1, 2]
), in-placeconstruction cannot be used anymore, since
isPartOf
usage is phasedout and the new MIR pass doesn't special-case
mArrToSeq
. However, withthe removal of the legacy GCs, the in-place
seq
construction had onlyvery little benefit, as using a temporary there only implies an
additional
(int, pointer)
local plus assignment thereof.Misc
Two internal bugs in the
mirtree
module are fixed:operand
incorrectly ignoredmnkConsume
nodesSingleInputNode
set was missingmnkStdConv
,mnkPathNamed
,mnkPathPos
, andmnkPathVariant
. The aforementioned nodes now workwith
unaryOperand
, as they should