Skip to content

[opt] Support begin/end access instructions in store/load elimination. #31078

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

Closed
wants to merge 7 commits into from
Closed
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: 4 additions & 0 deletions include/swift/SIL/Projection.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ enum class ProjectionKind : unsigned {
Class = PointerIntEnumIndexKindValue<3, ProjectionKind>::value,
Enum = PointerIntEnumIndexKindValue<4, ProjectionKind>::value,
Box = PointerIntEnumIndexKindValue<5, ProjectionKind>::value,
Access = PointerIntEnumIndexKindValue<6, ProjectionKind>::value,
LastIndexKind = Enum,
};

Expand All @@ -129,6 +130,7 @@ static inline bool isCastProjectionKind(ProjectionKind Kind) {
case ProjectionKind::Enum:
case ProjectionKind::Box:
case ProjectionKind::TailElems:
case ProjectionKind::Access:
return false;
}
}
Expand Down Expand Up @@ -428,6 +430,7 @@ class Projection {
case ProjectionKind::TailElems:
case ProjectionKind::Enum:
case ProjectionKind::Box:
case ProjectionKind::Access:
return false;
}

Expand All @@ -439,6 +442,7 @@ class Projection {
case ProjectionKind::Class:
case ProjectionKind::Enum:
case ProjectionKind::Struct:
case ProjectionKind::Access:
return true;
case ProjectionKind::BitwiseCast:
case ProjectionKind::Index:
Expand Down
1 change: 1 addition & 0 deletions lib/SIL/Utils/InstructionUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ SILValue swift::stripOwnershipInsts(SILValue v) {
return v;
case ValueKind::CopyValueInst:
case ValueKind::BeginBorrowInst:
case ValueKind::BeginAccessInst:
v = cast<SingleValueInstruction>(v)->getOperand(0);
}
}
Expand Down
15 changes: 15 additions & 0 deletions lib/SIL/Utils/Projection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,11 @@ Projection::Projection(SingleValueInstruction *I) : Value() {
assert(getKind() == ProjectionKind::BitwiseCast);
break;
}
case SILInstructionKind::BeginAccessInst: {
Value = ValueTy(ProjectionKind::Access, uintptr_t(0));
assert(getKind() == ProjectionKind::Access);
break;
}
}
}

Expand Down Expand Up @@ -196,6 +201,7 @@ SILType Projection::getType(SILType BaseType, SILModule &M,
case ProjectionKind::BitwiseCast:
case ProjectionKind::TailElems:
return getCastType(BaseType);
case ProjectionKind::Access:
case ProjectionKind::Index:
// Index types do not change the underlying type.
return BaseType;
Expand Down Expand Up @@ -237,6 +243,8 @@ Projection::createObjectProjection(SILBuilder &B, SILLocation Loc,
return B.createUncheckedRefCast(Loc, Base, getCastType(BaseTy));
case ProjectionKind::BitwiseCast:
return B.createUncheckedBitwiseCast(Loc, Base, getCastType(BaseTy));
case ProjectionKind::Access:
return nullptr;
}

llvm_unreachable("Unhandled ProjectionKind in switch.");
Expand Down Expand Up @@ -281,6 +289,8 @@ Projection::createAddressProjection(SILBuilder &B, SILLocation Loc,
case ProjectionKind::RefCast:
case ProjectionKind::BitwiseCast:
return B.createUncheckedAddrCast(Loc, Base, getCastType(BaseTy));
case ProjectionKind::Access:
return nullptr;
}

llvm_unreachable("Unhandled ProjectionKind in switch.");
Expand Down Expand Up @@ -835,6 +845,10 @@ SILValue Projection::getOperandForAggregate(SILInstruction *I) const {
}
}
break;
case ProjectionKind::Access:
if (auto access = dyn_cast<BeginAccessInst>(I))
return access->getOperand();
break;
case ProjectionKind::Class:
case ProjectionKind::TailElems:
case ProjectionKind::Box:
Expand Down Expand Up @@ -892,6 +906,7 @@ static bool isSupportedProjection(const Projection &p) {
switch (p.getKind()) {
case ProjectionKind::Struct:
case ProjectionKind::Tuple:
case ProjectionKind::Access:
return true;
case ProjectionKind::Class:
case ProjectionKind::Enum:
Expand Down
2 changes: 2 additions & 0 deletions lib/SILOptimizer/PassManager/PassPipeline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,8 @@ static void addLowLevelPassPipeline(SILPassPipelinePlan &P) {
P.addDeadObjectElimination();
P.addObjectOutliner();
P.addDeadStoreElimination();
P.addDCE();
P.addDeadObjectElimination();

// We've done a lot of optimizations on this function, attempt to FSO.
P.addFunctionSignatureOpts();
Expand Down
6 changes: 6 additions & 0 deletions lib/SILOptimizer/Transforms/DeadCodeElimination.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ namespace {
// FIXME: Reconcile the similarities between this and
// isInstructionTriviallyDead.
static bool seemsUseful(SILInstruction *I) {
if (auto access = dyn_cast<BeginAccessInst>(I))
return access->getSingleUse() == nullptr && !access->use_empty();

if (isa<EndAccessInst>(I))
return I->getOperand(0)->getSingleUse() == nullptr;

if (I->mayHaveSideEffects())
return true;

Expand Down
45 changes: 42 additions & 3 deletions lib/SILOptimizer/Transforms/DeadStoreElimination.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,8 @@ static bool isDeadStoreInertInstruction(SILInstruction *Inst) {
case SILInstructionKind::DeallocRefInst:
case SILInstructionKind::CondFailInst:
case SILInstructionKind::FixLifetimeInst:
case SILInstructionKind::EndAccessInst:
case SILInstructionKind::SetDeallocatingInst:
return true;
default:
return false;
Expand Down Expand Up @@ -888,8 +890,13 @@ bool DSEContext::processWriteForDSE(BlockState *S, unsigned bit) {
continue;
// If 2 locations may alias, we can still keep both stores.
LSLocation &L = LocationVault[i];
if (!L.isMustAliasLSLocation(R, AA))
if (!L.isMustAliasLSLocation(R, AA)) {
// If this is a reference type, then the *live* store counts as an unkown
// read.
if (L.getBase()->getType().isAnyClassReferenceType())
S->stopTrackingLocation(S->BBWriteSetMid, i);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was always an issue. We just never ran into it because before this patch we struggled with reference types.

continue;
}
// There is a must alias store. No need to check further.
StoreDead = true;
break;
Expand Down Expand Up @@ -1087,7 +1094,12 @@ void DSEContext::processUnknownReadInstForGenKillSet(SILInstruction *I) {
for (unsigned i = 0; i < S->LocationNum; ++i) {
if (!S->BBMaxStoreSet.test(i))
continue;
if (!AA->mayReadFromMemory(I, LocationVault[i].getBase()))
auto val = LocationVault[i].getBase();
if (!AA->mayReadFromMemory(I, val))
continue;
if (llvm::all_of(I->getAllOperands(), [&AA = AA, &val](Operand &op) {
return AA->isNoAlias(op.get(), val);
}))
continue;
// Update the genset and kill set.
S->startTrackingLocation(S->BBKillSet, i);
Expand All @@ -1100,7 +1112,12 @@ void DSEContext::processUnknownReadInstForDSE(SILInstruction *I) {
for (unsigned i = 0; i < S->LocationNum; ++i) {
if (!S->isTrackingLocation(S->BBWriteSetMid, i))
continue;
if (!AA->mayReadFromMemory(I, LocationVault[i].getBase()))
auto val = LocationVault[i].getBase();
if (!AA->mayReadFromMemory(I, val))
continue;
if (llvm::all_of(I->getAllOperands(), [&AA = AA, &val](Operand &op) {
return AA->isNoAlias(op.get(), val);
}))
continue;
S->stopTrackingLocation(S->BBWriteSetMid, i);
}
Expand Down Expand Up @@ -1140,6 +1157,28 @@ void DSEContext::processInstruction(SILInstruction *I, DSEKind Kind) {
processStoreInst(I, Kind);
} else if (isa<DebugValueAddrInst>(I)) {
processDebugValueAddrInst(I, Kind);
} else if (isa<BeginAccessInst>(I)) {
// Do the same thing here as in RLE. Begin access writes/reads memory only
// if one of its users writes/reads memory. We will look at all of its users
// and we can project to the source memory location so, if there are any
// actual writes/reads of memory we will catch them there so, we can ignore
// begin_access here.
return;
} else if (auto *release = dyn_cast<StrongReleaseInst>(I)) {
// If the strong release operand type can't have a custom destructor it's
// OK.
if (release->getOperand()->getType().getClassOrBoundGenericClass() ==
nullptr)
return;
// For strong releases, we have to prove that the strong release won't
// envoke the destructor. For now, we just try to find a set_deallocating
// instruction that indicates the life-ending strong_release has been
// devirtualized. TODO: there should be a better way to do this.
for (auto *use : release->getOperand()->getUses()) {
if (isa<SetDeallocatingInst>(use->getUser()))
return;
}
processUnknownReadInst(I, Kind);
} else if (I->mayReadFromMemory()) {
processUnknownReadInst(I, Kind);
}
Expand Down
23 changes: 23 additions & 0 deletions lib/SILOptimizer/Transforms/RedundantLoadElimination.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@ static bool isRLEInertInstruction(SILInstruction *Inst) {
case SILInstructionKind::IsEscapingClosureInst:
case SILInstructionKind::IsUniqueInst:
case SILInstructionKind::FixLifetimeInst:
case SILInstructionKind::EndAccessInst:
case SILInstructionKind::SetDeallocatingInst:
case SILInstructionKind::DeallocRefInst:
return true;
default:
return false;
Expand Down Expand Up @@ -977,6 +980,10 @@ void BlockState::processUnknownWriteInstForGenKillSet(RLEContext &Ctx,
LSLocation &R = Ctx.getLocation(i);
if (!AA->mayWriteToMemory(I, R.getBase()))
continue;
if (llvm::all_of(I->getAllOperands(), [&AA, &R](Operand &op) {
return AA->isNoAlias(op.get(), R.getBase());
}))
continue;
// MayAlias.
stopTrackingLocation(BBGenSet, i);
startTrackingLocation(BBKillSet, i);
Expand All @@ -996,6 +1003,10 @@ void BlockState::processUnknownWriteInstForRLE(RLEContext &Ctx,
LSLocation &R = Ctx.getLocation(i);
if (!AA->mayWriteToMemory(I, R.getBase()))
continue;
if (llvm::all_of(I->getAllOperands(), [&AA, &R](Operand &op) {
return AA->isNoAlias(op.get(), R.getBase());
}))
continue;
// MayAlias.
stopTrackingLocation(ForwardSetIn, i);
stopTrackingValue(ForwardValIn, i);
Expand Down Expand Up @@ -1089,6 +1100,18 @@ void BlockState::processInstructionWithKind(RLEContext &Ctx,
return;
}

// Begin access writes to memory only if one of its users writes to memory.
// We will look at all of its users and we can project to the source memory
// location so, if there are any actual writes to memory we will catch them
// there so, we can ignore begin_access here.
if (isa<BeginAccessInst>(Inst))
return;

// If the load is valid, then this strong release won't invoke the destructor.
// So we can ignore it.
if (isa<StrongReleaseInst>(Inst))
return;

// If this instruction has side effects, but is inert from a load store
// perspective, skip it.
if (isRLEInertInstruction(Inst))
Expand Down
8 changes: 3 additions & 5 deletions lib/SILOptimizer/Transforms/ReleaseDevirtualizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,11 @@ void ReleaseDevirtualizer::run() {
SILFunction *F = getFunction();
RCIA = PM->getAnalysis<RCIdentityAnalysis>()->get(F);

// The last release_value or strong_release instruction before the
// deallocation.
SILInstruction *LastRelease = nullptr;
bool Changed = false;
for (SILBasicBlock &BB : *F) {

// The last release_value or strong_release instruction before the
// deallocation.
SILInstruction *LastRelease = nullptr;

for (SILInstruction &I : BB) {
if (LastRelease) {
if (auto *DRI = dyn_cast<DeallocRefInst>(&I)) {
Expand Down
6 changes: 6 additions & 0 deletions lib/SILOptimizer/Transforms/StackPromotion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,12 @@ bool StackPromotion::tryPromoteAlloc(AllocRefInst *ARI, EscapeAnalysis *EA,
LLVM_DEBUG(llvm::dbgs() << " uses don't post-dom allocation -> don't promote");
return false;
}

if (Frontier.empty()) {
// TODO: assert(false);
return false;
}

NumStackPromoted++;

// We set the [stack] attribute in the alloc_ref.
Expand Down