Skip to content

Commit 8d946d8

Browse files
committed
[LVA] Update ignore instructions RLE and DSE.
RLE can ignore end_access, set_deallocating, dealloc_ref, begin_access, and strong_release instructions. DSE can ignore end_access, set_deallocating, begin_access, and sometimes strong_Release.
1 parent dc122a3 commit 8d946d8

File tree

3 files changed

+61
-0
lines changed

3 files changed

+61
-0
lines changed

lib/SILOptimizer/Transforms/DeadStoreElimination.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,8 @@ static bool isDeadStoreInertInstruction(SILInstruction *Inst) {
161161
case SILInstructionKind::DeallocRefInst:
162162
case SILInstructionKind::CondFailInst:
163163
case SILInstructionKind::FixLifetimeInst:
164+
case SILInstructionKind::EndAccessInst:
165+
case SILInstructionKind::SetDeallocatingInst:
164166
return true;
165167
default:
166168
return false;
@@ -1140,6 +1142,28 @@ void DSEContext::processInstruction(SILInstruction *I, DSEKind Kind) {
11401142
processStoreInst(I, Kind);
11411143
} else if (isa<DebugValueAddrInst>(I)) {
11421144
processDebugValueAddrInst(I, Kind);
1145+
} else if (isa<BeginAccessInst>(I)) {
1146+
// Do the same thing here as in RLE. Begin access writes/reads memory only
1147+
// if one of its users writes/reads memory. We will look at all of its users
1148+
// and we can project to the source memory location so, if there are any
1149+
// actual writes/reads of memory we will catch them there so, we can ignore
1150+
// begin_access here.
1151+
return;
1152+
} else if (auto *release = dyn_cast<StrongReleaseInst>(I)) {
1153+
// If the strong release operand type can't have a custom destructor it's
1154+
// OK.
1155+
if (release->getOperand()->getType().getClassOrBoundGenericClass() ==
1156+
nullptr)
1157+
return;
1158+
// For strong releases, we have to prove that the strong release won't
1159+
// envoke the destructor. For now, we just try to find a set_deallocating
1160+
// instruction that indicates the life-ending strong_release has been
1161+
// devirtualized. TODO: there should be a better way to do this.
1162+
for (auto *use : release->getOperand()->getUses()) {
1163+
if (isa<SetDeallocatingInst>(use->getUser()))
1164+
return;
1165+
}
1166+
processUnknownReadInst(I, Kind);
11431167
} else if (I->mayReadFromMemory()) {
11441168
processUnknownReadInst(I, Kind);
11451169
}

lib/SILOptimizer/Transforms/RedundantLoadElimination.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,9 @@ static bool isRLEInertInstruction(SILInstruction *Inst) {
160160
case SILInstructionKind::IsEscapingClosureInst:
161161
case SILInstructionKind::IsUniqueInst:
162162
case SILInstructionKind::FixLifetimeInst:
163+
case SILInstructionKind::EndAccessInst:
164+
case SILInstructionKind::SetDeallocatingInst:
165+
case SILInstructionKind::DeallocRefInst:
163166
return true;
164167
default:
165168
return false;
@@ -1089,6 +1092,18 @@ void BlockState::processInstructionWithKind(RLEContext &Ctx,
10891092
return;
10901093
}
10911094

1095+
// Begin access writes to memory only if one of its users writes to memory.
1096+
// We will look at all of its users and we can project to the source memory
1097+
// location so, if there are any actual writes to memory we will catch them
1098+
// there so, we can ignore begin_access here.
1099+
if (isa<BeginAccessInst>(Inst))
1100+
return;
1101+
1102+
// If the load is valid, then this strong release won't invoke the destructor.
1103+
// So we can ignore it.
1104+
if (isa<StrongReleaseInst>(Inst))
1105+
return;
1106+
10921107
// If this instruction has side effects, but is inert from a load store
10931108
// perspective, skip it.
10941109
if (isRLEInertInstruction(Inst))

test/SILOptimizer/dead_store_elim.sil

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1501,3 +1501,25 @@ bb0(%0 : $*foo):
15011501
%20 = tuple()
15021502
return %20 : $()
15031503
}
1504+
1505+
// Check that begin_access, end_access, strong_release, set_deallocating, and dealloc_ref don't prevent optimization.
1506+
// CHECK-LABEL: ignore_read_write
1507+
// CHECK: bb0
1508+
// CHECK-NOT: store
1509+
// CHECK-LABEL: end sil function 'ignore_read_write'
1510+
sil @ignore_read_write : $@convention(thin) () -> Int {
1511+
bb0:
1512+
%1 = alloc_ref [stack] $foo
1513+
strong_retain %1 : $foo
1514+
%2 = integer_literal $Builtin.Int64, 0
1515+
%3 = struct $Int (%2 : $Builtin.Int64)
1516+
%4 = ref_element_addr %1 : $foo, #foo.a
1517+
%5 = begin_access [modify] [dynamic] [no_nested_conflict] %4 : $*Int
1518+
store %3 to %5 : $*Int
1519+
end_access %5 : $*Int
1520+
strong_release %1 : $foo
1521+
set_deallocating %1 : $foo
1522+
dealloc_ref %1 : $foo
1523+
dealloc_ref [stack] %1 : $foo
1524+
return %3 : $Int
1525+
}

0 commit comments

Comments
 (0)