-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[SIL Opt][OSLogOptimization] Add -enable-ownership-stripping-after-serialization flag #27744
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
[SIL Opt][OSLogOptimization] Add -enable-ownership-stripping-after-serialization flag #27744
Conversation
@swift-ci Please test |
Build failed |
@swift-ci Please test Linux Platform |
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.
One small nit and also, what about a SIL test case?
return; | ||
// Here, the original value may be at +0 or + 1. In the latter case, the | ||
// original value will be released and so would be the folded value. | ||
for (Operand *use : originalVal->getUses()) { |
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.
In OSSA, you could just check that the ownership kind is owned, i.e.:
originalVal->getOwnershipKind() == ValueOwnershipKind::Owned
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.
what about a SIL test case?
Sure. I will add a SIL test suite for the OSLogOptimization pass.
a7611e1
to
8d51a57
Compare
@atrick @gottesmm I have added a SIL test for OSLogOptimization, especially for testing the constant folding logic used by the pass for folding constant strings. I have also updated the constant folding logic so that it uniformly handles ownership and non-ownership SIL. The function |
@swift-ci Please test |
Build failed |
Build failed |
if (fun->hasOwnership()) { | ||
builder.createDestroyValue(loc, val); | ||
} else { | ||
builder.createReleaseValue(loc, val, builder.getDefaultAtomicity()); |
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.
You can use emitDestroyValueOperation here.
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.
Sure.
/// non-address. | ||
/// | ||
/// TODO: Review the semantics of operations that extend the lifetime *without* | ||
/// propagating the value. Ideally, that never happens without borrowing first. |
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.
I don't have a good answer for this, but I am worried about finding a way to lexically disambiguate APIs that are only available in ownership SSA and ones in non-ownership SSA. I guess that asserts will guide us.
(The reason I am bringing this up is I almost asked you to put this into OwnershipUtils.h until I read the comment).
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.
I agree. Asserts and perhaps we can come up with a ownership-neutral naming convention for such operations. I was split between this and something like doesDecrementReferenceCount
but isConsuming
seemed much more succinct and nicer in this context, though it comes with an OSSA connotation
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.
Yeah, I also would expect such an API to be in OwnershipUtils. Eventually most of our utilities will work on ownership, maybe exclusively. So regardless of which file this API belongs in, it's still good to have a clear doc-comment convention and a convention for asserting at the top of the implementation.
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.
isConsumingUse
should actually assert OSSA. Note that eventually isConsumingUse
should just fall out naturally from the main ownership model. But only when we have a way of expressing it in the ownership model so that it's trivial to tell from code inspection whether a particular opcode consumes its operand(s). For now, it seems that you'll just drop this commit.
/// \param fun SILFunction containing the SILValue \c value | ||
/// \param endUsers buffer for storing the found end users of the SILValue. | ||
static void | ||
getEndUsersOfPlusOneSILValue(SILValue value, SILFunction *fun, |
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.
Is this a general function, or does it make specific assumptions?
The reason why I am asking is that I am writing something similar to fix an issue around destructures in SemanticARCOpts. I just haven't upstreamed it yet.
You should take a look at it for the ossa code, but it is doing the essentially last thing, computing a "lifetime hull" (by analogy to "convex hull").
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.
Actually, a generic utility that will subsume this function is "to compute the (intra-procedural) end points of the uses of a given SILValue including uses of its properties". So I do have to follow the use-chains of trivial values coming out of destructure_struct as well. I couldn't find a utility function that does that. Is there one?
While that would be a nice generic utility that can subsume this function, this function is taking a few short cuts as I am overfitting it a little for OSLog APIs as explained below. (Having said that, this current function itself is a little too general for the SIL that is emitted for the OSLog APIs as of now, so I haven't generalized this too much.) Here, one additional information that is known is that the input value is non-trivial and is at +1. So I was trying to exploit that and look at only consuming uses for such values, ignoring borrows and borrowed operations like struct_extract
, as a consuming use should always succeed borrowed uses.
However, I had to handle copy_value and destructure_struct as they extend the use chain of the value or of its properties. (I guess I should add this explanation to the comments).
Alternatively, I might as well look at all uses regardless of whether it is trivial, non-trivial, +1 or otherwise, and follow dependencies through struct_extracts as well. I was just thinking the first strategy was more efficient, but it does overfit for only +1 input values.
(There is also another assumption here which is that the input value is not an address, if that assumption does not hold in the future, this function has to be further generalized in the future to handle stores in to the SILValue. I should add an assert for that.)
// values only consider consuming uses as potential end uses. (Note that | ||
// trivial values may arise in this search due to destructure_struct | ||
// instructions.) | ||
if (isTrivial) { |
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.
Does this deal with addresses? If you are just worried about trivial values from destructures, I would just not add them to the worklist when adding results. Your thoughts?
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.
No it doesn't deal with addresses (see the above explanation). If there is a utility that does this including dealing with addresses, it would be great. But, at this point it would be an overkill for OSLog. However, it wouldn't hurt to use that utility especially if it brings more conceptual clarity.
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.
I wasn't expecting it to handle addresses. I was more asking if there was a reason why you were checking for trivial here. Seems like you dont need to visit the trivial things.
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.
As I mentioned above, I do need to track uses of trivial values. This pass is interested in them. It is supposed to track end of data dependent chains. I am updating this code to simplify and generalize it.
|
||
SILValue copiedVal; | ||
if (fun->hasOwnership()) { | ||
copiedVal = builder.createCopyValue(loc, foldedVal); |
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.
You can use emitCopyValueOperation here.
8d51a57
to
011829e
Compare
@atrick Rebased this work on top of the utility function created in this PR: #27876. This served quite handy and is used in a couple of places in this code base and also simplified/generalized the functions |
@swift-ci Please test |
Build failed |
Build failed |
SmallPtrSet<SILInstruction *, 1> defs = {foldedValInst}; | ||
|
||
llvm::SmallVector<SILInstruction *, 4> postDominators; | ||
computePostDominatorsOfUsers(lifetimeRangeOfFoldedVal, defs, postDominators); |
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.
This is just asking for the instructions at the lifetime boundary, which is exactly what ValueLifetimeAnalysis is supposed to do. That utility just needs to be slightly generalized to report the head of the "completion" blocks as part of the lifetime boundary. You don't need to do that in this PR, but as a heads up I'd like to change this call to computePostDominatorsOfUsers
to ValueLifetimeAnalysis
.
ea3ef03
to
028c54f
Compare
@swift-ci Please test |
Build failed |
Build failed |
|
/// non-address. | ||
/// | ||
/// TODO: Review the semantics of operations that extend the lifetime *without* | ||
/// propagating the value. Ideally, that never happens without borrowing first. |
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.
isConsumingUse
should actually assert OSSA. Note that eventually isConsumingUse
should just fall out naturally from the main ownership model. But only when we have a way of expressing it in the ownership model so that it's trivial to tell from code inspection whether a particular opcode consumes its operand(s). For now, it seems that you'll just drop this commit.
SmallVector<SILInstruction *, 16> worklist; | ||
// Initialize worklist with the uses of value. Note that uses should not | ||
// contain value's defining instruction. | ||
for (Operand *use : value->getUses()) { |
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.
This loop seems completely redundant with the loop below, if you just initialize worklist
to value. worklist
can be SmallVector<SILValue, 16>
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.
Sure. I have fixed this in the new commit.
frontier, ValueLifetimeAnalysis::DontModifyCFG); | ||
endUsers.append(frontier.begin(), frontier.end()); | ||
if (!hasCriticlEdges) | ||
return; |
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.
It would have been fine to avoid all the critical edges complexity and just split critical edges in the beginning, as other passes do. Eventually we'll just disallow them everywhere so we'll need to come back and cleanup code like this.
// Iterate over all non-arc uses of originalVal and replace it with foldedVal, | ||
// after emitting required ARC operation. The foldedVal should be at +1 even | ||
// after replacing it in all the uses of originalVal. | ||
while (Operand *use = getFirstNonARCUse(originalVal)) { |
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.
I guess this whole loop can go away if you limit the pass to OSSA.
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.
Yes. I have just pushed a commit that removes this loop
028c54f
to
8627fc2
Compare
@swift-ci Please test |
Build failed |
Build failed |
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.
LGTM.
There seems to be a presumption that struct_extract is never released. It's dangerous, but that code should just be replaced by assert(f.hasOwnership()
within a week or so.
…est folding logic, add -enable-ownership-stripping-after-serialization flag to OSLog optimization tests, and update the folding logic and end-of-use discovery logic to handle ownership and non-ownership SIL.
8627fc2
to
ffd3fef
Compare
@swift-ci Please test and merge |
1 similar comment
@swift-ci Please test and merge |
to OSLogPrototypeCompileTest and fix couple of bugs in the OSLogOptimization
pass that manifests on ownership SIL.
Also, update the constant folding of
Builtin.globalStringTablePointer
so that it can look through ownership instructions.