Skip to content

[tmp] Access scope handling #31208

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 10 commits into from
Closed
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
24 changes: 24 additions & 0 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 @@ -1140,6 +1142,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
15 changes: 15 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 @@ -1089,6 +1092,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
18 changes: 18 additions & 0 deletions test/SILOptimizer/dead_code_elimination.sil
Original file line number Diff line number Diff line change
Expand Up @@ -259,3 +259,21 @@ bb2:
bb3:
br bb1
}

// Check that DCE eliminates dead access instructions.
// CHECK-LABEL: sil @dead_access
// CHECK: bb0
// CHECK-NEXT: tuple
// CHECK-NEXT: return
// CHECK-LABEL: end sil function 'dead_access'
sil @dead_access : $@convention(thin) (@in Container) -> () {
bb0(%0 : $*Container):
%1 = begin_access [modify] [dynamic] %0 : $*Container
end_access %1 : $*Container

%3 = begin_access [read] [static] %0 : $*Container
end_access %3 : $*Container

%999 = tuple ()
return %999 : $()
}
22 changes: 22 additions & 0 deletions test/SILOptimizer/dead_store_elim.sil
Original file line number Diff line number Diff line change
Expand Up @@ -1501,3 +1501,25 @@ bb0(%0 : $*foo):
%20 = tuple()
return %20 : $()
}

// Check that begin_access, end_access, strong_release, set_deallocating, and dealloc_ref don't prevent optimization.
// CHECK-LABEL: ignore_read_write
// CHECK: bb0
// CHECK-NOT: store
// CHECK-LABEL: end sil function 'ignore_read_write'
sil @ignore_read_write : $@convention(thin) () -> Int {
bb0:
%1 = alloc_ref [stack] $foo
strong_retain %1 : $foo
%2 = integer_literal $Builtin.Int64, 0
%3 = struct $Int (%2 : $Builtin.Int64)
%4 = ref_element_addr %1 : $foo, #foo.a
%5 = begin_access [modify] [dynamic] [no_nested_conflict] %4 : $*Int
store %3 to %5 : $*Int
end_access %5 : $*Int
strong_release %1 : $foo
set_deallocating %1 : $foo
dealloc_ref %1 : $foo
dealloc_ref [stack] %1 : $foo
return %3 : $Int
}
21 changes: 21 additions & 0 deletions test/SILOptimizer/devirt_release.sil
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,27 @@ bb0:
return %r : $()
}

// CHECK-LABEL: sil @devirtualize_release_multiblock
// CHECK-NOT: strong_release
// CHECK: [[A:%[0-9]+]] = alloc_ref
// CHECK-NEXT: set_deallocating [[A]]
// CHECK: [[D:%[0-9]+]] = function_ref @$s4test1BCfD
// CHECK-NEXT: apply [[D]]([[A]])
// CHECK-NEXT: br
// CHECK: bb1:
// CHECK-NEXT: dealloc_ref [stack] [[A]]
// CHECK: return
sil @devirtualize_release_multiblock : $@convention(thin) () -> () {
bb0:
%1 = alloc_ref [stack] $B
strong_release %1 : $B
br bb1

bb1:
dealloc_ref [stack] %1 : $B
%r = tuple ()
return %r : $()
}

sil @unknown_func : $@convention(thin) () -> ()

Expand Down