Skip to content

[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

Merged

Conversation

ravikandhadai
Copy link
Contributor

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.

@ravikandhadai
Copy link
Contributor Author

@swift-ci Please test

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - 641cc8b660d623b8c4b3684806102e12c9ead76b

@ravikandhadai
Copy link
Contributor Author

@swift-ci Please test Linux Platform

Copy link
Contributor

@gottesmm gottesmm left a 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()) {
Copy link
Contributor

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

Copy link
Contributor Author

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.

@ravikandhadai
Copy link
Contributor Author

ravikandhadai commented Oct 19, 2019

@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 replaceAllUsesAndFixLifetimes has the new implementation.
@atrick The constant folding logic for strings is very similar to what we discussed. Basically, it looks at all uses of the originalValue that are consuming and releases the originalValue before the consuming use and copies the newValue using copy_value (or retain in non OSSA) and uses it in place of the originalValue. This also required factoring out a utility function from ConstantPropagation.cpp that checks whether a use of non-trivial (non-address) SILValue is consuming or not. I have made a separate PR on this: #27794.

@ravikandhadai
Copy link
Contributor Author

@swift-ci Please test

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - 641cc8b660d623b8c4b3684806102e12c9ead76b

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - 641cc8b660d623b8c4b3684806102e12c9ead76b

if (fun->hasOwnership()) {
builder.createDestroyValue(loc, val);
} else {
builder.createReleaseValue(loc, val, builder.getDefaultAtomicity());
Copy link
Contributor

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.

Copy link
Contributor Author

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.
Copy link
Contributor

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).

Copy link
Contributor Author

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

Copy link
Contributor

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.

Copy link
Contributor

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,
Copy link
Contributor

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").

Copy link
Contributor Author

@ravikandhadai ravikandhadai Oct 21, 2019

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) {
Copy link
Contributor

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?

Copy link
Contributor Author

@ravikandhadai ravikandhadai Oct 21, 2019

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.

Copy link
Contributor

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.

Copy link
Contributor Author

@ravikandhadai ravikandhadai Oct 22, 2019

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);
Copy link
Contributor

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.

@ravikandhadai ravikandhadai force-pushed the oslog-optimization-bug-fixes branch from 8d51a57 to 011829e Compare October 25, 2019 02:30
@ravikandhadai
Copy link
Contributor Author

ravikandhadai commented Oct 25, 2019

@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 replaceAllUsesAndFixLifetimes and getEndPointsOfDataDependentChain of OSLogOptimizations.cpp. I have also added a SIL test: testPostdominatorComputation in OSLogPrototypeCompileTest.sil that specifically tests the post dominator aspects of the OSLogOptimization pass.

@ravikandhadai
Copy link
Contributor Author

@swift-ci Please test

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - 8d51a579f3d1bd710d33d91f0302069be730dde3

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - 8d51a579f3d1bd710d33d91f0302069be730dde3

SmallPtrSet<SILInstruction *, 1> defs = {foldedValInst};

llvm::SmallVector<SILInstruction *, 4> postDominators;
computePostDominatorsOfUsers(lifetimeRangeOfFoldedVal, defs, postDominators);
Copy link
Contributor

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.

@ravikandhadai ravikandhadai force-pushed the oslog-optimization-bug-fixes branch 2 times, most recently from ea3ef03 to 028c54f Compare October 26, 2019 02:16
@ravikandhadai
Copy link
Contributor Author

@swift-ci Please test

@ravikandhadai
Copy link
Contributor Author

@atrick I updated this pass to use ValueLifetimeAnalysis. It depends on this PR: #27892 , and have removed all dependency on completJointPostdominatorSet utility.

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - 011829e93e49f96ae81ce4b676dc2eee5b4ed991

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - 011829e93e49f96ae81ce4b676dc2eee5b4ed991

@atrick
Copy link
Contributor

atrick commented Oct 28, 2019

completeJointPostdominatorSet is fine in theory as a CFG utility, but I don't know of any need for it and I think there's a high potential for misuse. If you want to remove it in this PR or a separate PR, that's fine with me.

/// non-address.
///
/// TODO: Review the semantics of operations that extend the lifetime *without*
/// propagating the value. Ideally, that never happens without borrowing first.
Copy link
Contributor

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()) {
Copy link
Contributor

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>

Copy link
Contributor Author

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;
Copy link
Contributor

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)) {
Copy link
Contributor

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.

Copy link
Contributor Author

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

@ravikandhadai ravikandhadai force-pushed the oslog-optimization-bug-fixes branch from 028c54f to 8627fc2 Compare October 28, 2019 22:07
@ravikandhadai
Copy link
Contributor Author

@atrick @gottesmm I have pushed a commit that avoids having to use isConsuming. It handle non-OSSA SIL by just folding only struct_extract and giving up on other cases. This suffices for this optimization pass. Anyway eventually this pass should only be seeing OSSA.

@ravikandhadai
Copy link
Contributor Author

@swift-ci Please test

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - 028c54f4ef9861d2482568f2cbf71d054e6b0a4f

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - 028c54f4ef9861d2482568f2cbf71d054e6b0a4f

Copy link
Contributor

@atrick atrick left a 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.
@ravikandhadai ravikandhadai force-pushed the oslog-optimization-bug-fixes branch from 8627fc2 to ffd3fef Compare October 28, 2019 22:34
@ravikandhadai
Copy link
Contributor Author

@swift-ci Please test and merge

1 similar comment
@ravikandhadai
Copy link
Contributor Author

@swift-ci Please test and merge

@swift-ci swift-ci merged commit c71957c into swiftlang:master Oct 29, 2019
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.

4 participants