-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[ownership] Enable SILMem2Reg for OSSA #34276
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
Conversation
A dealloc_stack ends the lifetime of an alloc_stack on a path. We don't have to pass RunningVal beyond the dealloc_stack as phi argument to the post dominating block.
@swift-ci test |
Build failed |
@swift-ci test |
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.
Thanks for working on this! I know first hand how complicated this pass can be (esspecially with ownership).
I think part of supporting ownership in this pass is adding support for access instructions, especially if you're adding those instructions in the pass. Although, that could probably be its own patch. When adding support for access instructions, please update isAddressForLoad
, isLoadFromStack
, collectLoads
, isDeadAddrProjection
, removeSingleBlockAllocation
, and isCaptured
. I think after these instructions are supported, you will get much more ossa test coverage (for example, while building the stdlib).
Add a case for DestroyAddrInst
to isWriteOnlyAllocation
.
Also, if isWriteOnlyAllocation
is true
, line 883 will remove all uses of alloc
. This includes lifetime ending stores which may cause problems.
Last, when I created a (much worse) version of this patch (which I ended up closing: #34209), I added quite a few tests which might be helpful. Some of the tests are just OSSA versions of the existing tests (which it looks like you've already added) but most of them are new (lines 465 and beyond).
Edit: #30812 is a patch for supporting begin_access
in mem2reg. If you'd like to land it (as its own patch or part of this one) that's fine with me. I'm also happy to land it myself (but I'll need someone to review it 😉).
@@ -348,21 +350,52 @@ static void collectLoads(SILInstruction *I, SmallVectorImpl<LoadInst *> &Loads) | |||
static void replaceLoad(LoadInst *LI, SILValue val, AllocStackInst *ASI) { | |||
ProjectionPath projections(val->getType()); | |||
SILValue op = LI->getOperand(); | |||
SILBuilder builder(LI); | |||
|
|||
while (op != ASI) { |
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.
We should look through access instructions too. Maybe you can use @atrick's AccessPath
utility.
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! Thanks for pointing out.
borrowedVals.push_back(opVal); | ||
} | ||
} | ||
else { |
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.
Nit: no newline.
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! Will fix.
// If we don't know the content of the AllocStack then the loaded | ||
// value *is* the new value; | ||
// Don't use result of load [copy] as a RunningVal, it necessitates | ||
// additional logic for cleanup of consuming instructions of the result. |
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 day we could support this. Can you please add a TODO?
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'll update the comment, needs more clarification. This doesn't in any way weaken the optimization. It just adds more work for StackAllocationPromoter::fixBranchesAndUses
because we prune less in StackAllocationPromoter::promoteAllocationInBlock
now.
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.
OK, fair enough.
@@ -433,16 +470,51 @@ StackAllocationPromoter::promoteAllocationInBlock(SILBasicBlock *BB) { | |||
if (SI->getDest() != ASI) | |||
continue; | |||
|
|||
// The stored value is the new running value. | |||
RunningVal = SI->getSrc(); |
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 think we want to keep the old behavior in non-ossa.
if (BB->isEntry()) { | ||
if (SI->getOwnershipQualifier() == StoreOwnershipQualifier::Assign) { | ||
assert(RunningVal); | ||
SILBuilder builder(SI); |
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.
Nit: this can be SILBuilder(SI).create...
. (There are a few other places like this.)
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! Will fix.
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.
Thanks for your comments @zoecarver. I'll try to incorporate the new tests you've added if they are not already covered here.
I'll probably make another PR for access instructions or am happy to review your PR : ) thanks for pointing it out!
@@ -348,21 +350,52 @@ static void collectLoads(SILInstruction *I, SmallVectorImpl<LoadInst *> &Loads) | |||
static void replaceLoad(LoadInst *LI, SILValue val, AllocStackInst *ASI) { | |||
ProjectionPath projections(val->getType()); | |||
SILValue op = LI->getOperand(); | |||
SILBuilder builder(LI); | |||
|
|||
while (op != ASI) { |
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! Thanks for pointing out.
// If we don't know the content of the AllocStack then the loaded | ||
// value *is* the new value; | ||
// Don't use result of load [copy] as a RunningVal, it necessitates | ||
// additional logic for cleanup of consuming instructions of the result. |
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'll update the comment, needs more clarification. This doesn't in any way weaken the optimization. It just adds more work for StackAllocationPromoter::fixBranchesAndUses
because we prune less in StackAllocationPromoter::promoteAllocationInBlock
now.
if (BB->isEntry()) { | ||
if (SI->getOwnershipQualifier() == StoreOwnershipQualifier::Assign) { | ||
assert(RunningVal); | ||
SILBuilder builder(SI); |
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! Will fix.
borrowedVals.push_back(opVal); | ||
} | ||
} | ||
else { |
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! Will fix.
@zoecarver I am going to test #30812 with this branch and see how it goes. |
Sounds good! You should make sure you can also build the stdlib with both patches applied. |
51d1450
to
f8d863d
Compare
No new bugs with access instructions. I think its best to handle it separately from this PR. #30812 will need some OSSA tests. |
Great.
SGTM.
Happy to add them. But they're useless until this patch lands (I can rebase that patch on this one until then). |
Looked into this. This is not okay because we just erase instructions in the case of |
Fair enough. |
f8d863d
to
454040f
Compare
7dcf44b
to
8e42060
Compare
@gottesmm cleaned up history and added dead phi arg removal in the pass. |
@@ -348,21 +350,50 @@ static void collectLoads(SILInstruction *I, SmallVectorImpl<LoadInst *> &Loads) | |||
static void replaceLoad(LoadInst *LI, SILValue val, AllocStackInst *ASI) { | |||
ProjectionPath projections(val->getType()); | |||
SILValue op = LI->getOperand(); | |||
SILBuilder builder(LI); |
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 should be SILBuilderWithScope.
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.
Done
@meg-gupta one thing that you should look into is changing all SILBuilder -> SILBuilderWithScope with this change. |
5bd11af
to
5800a0a
Compare
|
||
// If we met a store before this one, delete it. | ||
// If the LastStore was a store with [assign], delete it only if we know | ||
// the RunningValue to destroy. If not, it will be deleted in | ||
// StackAllocationPromoter::fixBranchesAndUses. |
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.
Great comment!
5800a0a
to
84f17c0
Compare
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 with my comments fixed.
@@ -613,3 +613,20 @@ bb0(%0 : @owned $(Builtin.BridgeObject, Builtin.Int32)): | |||
%7 = tuple () | |||
return %7 : $() | |||
} | |||
|
|||
// CHECK-LABEL: sil [ossa] @multiple_debug_value : | |||
// CHECK-NOT: alloc_stack |
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.
Nice fix! We have seen this bug before I think where someone had a test case that hit this somehow!
Block->getArgument(Block->getNumArguments() - 1)); | ||
if (phiArg->getOwnershipKind() == ValueOwnershipKind::Owned && | ||
phiArg->use_empty()) { | ||
assert(hasOnlyUndefIncomingValues(phiArg)); |
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.
Nice defensive assert!
@@ -679,6 +806,16 @@ void StackAllocationPromoter::fixBranchesAndUses(BlockSet &PhiBlocks) { | |||
// on. | |||
SILBasicBlock *BB = Inst->getParent(); | |||
|
|||
if (!BB->isEntry()) { | |||
if (auto *SI = dyn_cast<StoreInst>(Inst)) { |
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.
If I am understanding this correctly, this is here b/c of the code https://github.com/apple/swift/pull/34276/files#diff-a0cdc552f97b0b66a7c8f63788292539566ab2a53affa110bc00f904df39ecf9R504. Can you put a comment here explaining that?
// guaranteed ownership. For replacing load's users, we need an owned value. | ||
// We will need a new copy and destroy of the running val placed after the | ||
// last use. This is not implemented currently. | ||
if (hasGuaranteedOwnership && cast<LoadInst>(I)->getOwnershipQualifier() == |
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.
Can you give an example of this in SIL that shows this problem?
@@ -433,16 +491,51 @@ StackAllocationPromoter::promoteAllocationInBlock(SILBasicBlock *BB) { | |||
if (SI->getDest() != ASI) | |||
continue; | |||
|
|||
// The stored value is the new running value. | |||
RunningVal = SI->getSrc(); | |||
// Special handling of entry block |
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.
Can you make this pop more? Maybe put a newline? This line is a bit of a title.
@swift-ci test |
Build failed |
Build failed |
SILMem2Reg has roughly 2 central algorithms, removal of alloc_stack with uses in a single block vs multiple blocks. While replacing loads and stores to the stack location in each of the 2 algorithms, new handling of qualifiers like [assign], [copy] and [take] which are new to OSSA are needed. Also Disable SILMem2Reg when we see this pattern: load [take] (struct_element_addr/tuple_element_addr %ASI) Convert SILMem2Reg tests into ossa And add new SILMem2Reg tests for non-trivial values. Thanks to zoecarver for additional tests.
…nningVal is already found
… OSSA unchecked_ref_cast is a forwarding cast while unchecked_bitwise_cast is not. We cannot just convert one to other in OSSA. Disable it now.
84f17c0
to
06eaf6b
Compare
Had to force push to fix an aggressive assert. Only difference after the approval :
|
@swift-ci test |
@swift-ci test source compatibility |
I'll fix the remaining comments in a follow up PR as suggested. |
SILMem2Reg is currently not enabled for OSSA. This PR enables it for OSSA, and adds support for composite loads and stores with ownership qualifiers [copy], [assign] and [take] that are new to OSSA.
This PR currently does not add support for load_borrow/store_borrow and access instructions.