Skip to content

[membehavior] Fix base memory behavior/releasing of Load/Store for ownership #34543

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
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions include/swift/SIL/SILNodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,7 @@ ABSTRACT_VALUE_AND_INST(SingleValueInstruction, ValueBase, SILInstruction)

// Accessing memory
SINGLE_VALUE_INST(LoadInst, load,
SingleValueInstruction, MayRead, DoesNotRelease)
SingleValueInstruction, MayHaveSideEffects, DoesNotRelease)
SINGLE_VALUE_INST(LoadBorrowInst, load_borrow,
SingleValueInstruction, MayRead, DoesNotRelease)
SINGLE_VALUE_INST(BeginBorrowInst, begin_borrow,
Expand Down Expand Up @@ -852,7 +852,7 @@ NON_VALUE_INST(BeginUnpairedAccessInst, begin_unpaired_access,
NON_VALUE_INST(EndUnpairedAccessInst, end_unpaired_access,
SILInstruction, MayHaveSideEffects, DoesNotRelease)
NON_VALUE_INST(StoreInst, store,
SILInstruction, MayWrite, DoesNotRelease)
SILInstruction, MayHaveSideEffects, MayRelease)
NON_VALUE_INST(AssignInst, assign,
SILInstruction, MayWrite, DoesNotRelease)
NON_VALUE_INST(AssignByWrapperInst, assign_by_wrapper,
Expand Down
42 changes: 42 additions & 0 deletions lib/SIL/IR/SILInstruction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1029,6 +1029,36 @@ SILInstruction::MemoryBehavior SILInstruction::getMemoryBehavior() const {
MemoryBehavior::MayHaveSideEffects;
}

if (auto *li = dyn_cast<LoadInst>(this)) {
switch (li->getOwnershipQualifier()) {
case LoadOwnershipQualifier::Unqualified:
case LoadOwnershipQualifier::Trivial:
return MemoryBehavior::MayRead;
case LoadOwnershipQualifier::Take:
// Take deinitializes the underlying memory. Until we separate notions of
// memory writing from deinitialization (since a take doesn't actually
// write to the memory), lets be conservative and treat it as may read
// write.
return MemoryBehavior::MayReadWrite;
case LoadOwnershipQualifier::Copy:
return MemoryBehavior::MayHaveSideEffects;
}
llvm_unreachable("Covered switch isn't covered?!");
}

if (auto *si = dyn_cast<StoreInst>(this)) {
switch (si->getOwnershipQualifier()) {
case StoreOwnershipQualifier::Unqualified:
case StoreOwnershipQualifier::Trivial:
case StoreOwnershipQualifier::Init:
return MemoryBehavior::MayWrite;
case StoreOwnershipQualifier::Assign:
// For the release.
return MemoryBehavior::MayHaveSideEffects;
}
llvm_unreachable("Covered switch isn't covered?!");
}

switch (getKind()) {
#define FULL_INST(CLASS, TEXTUALNAME, PARENT, MEMBEHAVIOR, RELEASINGBEHAVIOR) \
case SILInstructionKind::CLASS: \
Expand Down Expand Up @@ -1138,6 +1168,18 @@ bool SILInstruction::mayRelease() const {
}
return true;
}
case SILInstructionKind::StoreInst:
switch (cast<StoreInst>(this)->getOwnershipQualifier()) {
case StoreOwnershipQualifier::Unqualified:
case StoreOwnershipQualifier::Init:
case StoreOwnershipQualifier::Trivial:
return false;
case StoreOwnershipQualifier::Assign:
// Assign destroys the old value that was in the memory location before we
// write the new value into the location.
return true;
}
llvm_unreachable("Covered switch isn't covered?!");
}
}

Expand Down
25 changes: 15 additions & 10 deletions lib/SILOptimizer/Analysis/MemoryBehavior.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -242,12 +242,15 @@ MemBehavior MemoryBehaviorVisitor::visitLoadInst(LoadInst *LI) {
if (!mayAlias(LI->getOperand()))
return MemBehavior::None;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@eeckstein one thing we need to think about is do we consider from a membehavior perspective that the retain from a load [copy] is an issue as well in terms of side-effects. I imagine probably not.

Copy link
Contributor

Choose a reason for hiding this comment

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

Right, it is not. Therefore the original version of visitLoad() is correct.

Copy link
Contributor

Choose a reason for hiding this comment

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

Exactly the sort of thing that needs to be specified in the definition of MemBehavior: writes to non-programmer visible memory, particularly refcounts, are not considered write or side effects.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If we are going to do that, we should model retain behavior in MemBehavior explicitly. That would be the right way to do that.

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 don't think that this is true. If you look in SILNodes.def we consider retain to have side-effects since we don't model retains separately in MemBehavior. See:

https://github.com/apple/swift/blob/f58c14335518e611aa8b8a29aafe9ea73848f919/include/swift/SIL/SILNodes.def#L793

I agree with you if we model retains separately in membehavior.


// A take is modelled as a write. See MemoryBehavior::MayWrite.
if (LI->getOwnershipQualifier() == LoadOwnershipQualifier::Take)
return MemBehavior::MayReadWrite;

LLVM_DEBUG(llvm::dbgs() << " Could not prove that load inst does not alias "
"pointer. Returning may read.\n");
"pointer. ");

if (LI->getOwnershipQualifier() == LoadOwnershipQualifier::Take) {
LLVM_DEBUG(llvm::dbgs() << "Is a take so return MayReadWrite.\n");
return MemBehavior::MayReadWrite;
}

LLVM_DEBUG(llvm::dbgs() << "Not a take so returning MayRead.\n");
return MemBehavior::MayRead;
}

Expand All @@ -257,15 +260,17 @@ MemBehavior MemoryBehaviorVisitor::visitStoreInst(StoreInst *SI) {
if (isLetValue() && (getAccessBase(SI->getDest()) != getValueAddress())) {
return MemBehavior::None;
}
// If the store dest cannot alias the pointer in question, then the
// specified value cannot be modified by the store.
if (!mayAlias(SI->getDest()))
// If the store dest cannot alias the pointer in question and we are not
// releasing anything due to an assign, then the specified value cannot be
// modified by the store.
if (!mayAlias(SI->getDest()) &&
SI->getOwnershipQualifier() != StoreOwnershipQualifier::Assign)
return MemBehavior::None;

// Otherwise, a store just writes.
LLVM_DEBUG(llvm::dbgs() << " Could not prove store does not alias inst. "
"Returning MayWrite.\n");
return MemBehavior::MayWrite;
"Returning default mem behavior.\n");
return SI->getMemoryBehavior();
}

MemBehavior MemoryBehaviorVisitor::visitCopyAddrInst(CopyAddrInst *CAI) {
Expand Down
25 changes: 21 additions & 4 deletions lib/SILOptimizer/Analysis/SideEffectAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -539,12 +539,29 @@ void FunctionSideEffects::analyzeInstruction(SILInstruction *I) {
true;
Traps = true;
return;
case SILInstructionKind::LoadInst:
getEffectsOn(cast<LoadInst>(I)->getOperand())->Reads = true;
case SILInstructionKind::LoadBorrowInst: {
auto *effects = getEffectsOn(cast<LoadBorrowInst>(I)->getOperand());
effects->Reads = true;
return;
case SILInstructionKind::StoreInst:
getEffectsOn(cast<StoreInst>(I)->getDest())->Writes = true;
}
case SILInstructionKind::LoadInst: {
auto *li = cast<LoadInst>(I);
auto *effects = getEffectsOn(cast<LoadInst>(I)->getOperand());
effects->Reads = true;
if (li->getOwnershipQualifier() == LoadOwnershipQualifier::Take)
effects->Writes = true;
if (li->getOwnershipQualifier() == LoadOwnershipQualifier::Copy)
effects->Retains = true;
return;
}
case SILInstructionKind::StoreInst: {
auto *si = cast<StoreInst>(I);
auto *effects = getEffectsOn(si->getDest());
effects->Writes = true;
if (si->getOwnershipQualifier() == StoreOwnershipQualifier::Assign)
effects->Releases = true;
return;
}
case SILInstructionKind::CondFailInst:
Traps = true;
return;
Expand Down