Skip to content

Commit b861853

Browse files
authored
Merge pull request #6912 from eeckstein/dead-conf-elim
Remove dead witness tables, including dead witness functions.
2 parents 2515ead + 44f35b9 commit b861853

16 files changed

+784
-98
lines changed

include/swift/SIL/InstructionUtils.h

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,13 @@
1414
#define SWIFT_SIL_INSTRUCTIONUTILS_H
1515

1616
#include "swift/SIL/SILInstruction.h"
17+
#include "llvm/ADT/SmallVector.h"
18+
#include "llvm/ADT/SmallPtrSet.h"
1719

1820
namespace swift {
1921

22+
class SILWitnessTable;
23+
2024
/// Strip off casts/indexing insts/address projections from V until there is
2125
/// nothing left to strip.
2226
SILValue getUnderlyingObject(SILValue V);
@@ -73,6 +77,92 @@ SILValue stripIndexingInsts(SILValue V);
7377
/// intrinsic call.
7478
SILValue stripExpectIntrinsic(SILValue V);
7579

80+
/// Collects conformances which are used by instructions or inside witness
81+
/// tables.
82+
///
83+
/// It also collects "escaping" metatypes. If a metatype can escape to a not
84+
/// visible function (outside the compilation unit), all it's conformances have
85+
/// to be alive, because an opaque type can be tested at runtime if it conforms
86+
/// to a protocol.
87+
class ConformanceCollector {
88+
/// The used conformances.
89+
llvm::SmallVector<const ProtocolConformance *, 8> Conformances;
90+
91+
/// Types of which metatypes are escaping.
92+
llvm::SmallVector<const NominalTypeDecl *, 8> EscapingMetaTypes;
93+
94+
/// Conformances and types which have been handled.
95+
llvm::SmallPtrSet<const void *, 8> Visited;
96+
97+
SILModule &M;
98+
99+
void scanType(Type type);
100+
void scanSubsts(ArrayRef<Substitution> substs);
101+
102+
template <typename T> void scanFuncParams(ArrayRef<T> OrigParams,
103+
ArrayRef<T> SubstParams) {
104+
size_t NumParams = OrigParams.size();
105+
assert(NumParams == SubstParams.size());
106+
for (size_t Idx = 0; Idx < NumParams; ++Idx) {
107+
if (OrigParams[Idx].getType()->hasTypeParameter()) {
108+
scanType(SubstParams[Idx].getType());
109+
}
110+
}
111+
}
112+
113+
void scanConformance(ProtocolConformance *C);
114+
115+
void scanConformance(ProtocolConformanceRef CRef) {
116+
if (CRef.isConcrete())
117+
scanConformance(CRef.getConcrete());
118+
}
119+
120+
void scanConformances(ArrayRef<ProtocolConformanceRef> CRefs);
121+
122+
public:
123+
124+
ConformanceCollector(SILModule &M) : M(M) { }
125+
126+
/// Collect all used conformances of an instruction.
127+
///
128+
/// If the instruction can escape a metatype, also record this metatype.
129+
void collect(SILInstruction *I);
130+
131+
/// Collect all used conformances and metatypes of a witness table.
132+
void collect(SILWitnessTable *WT);
133+
134+
/// Clears all information about used conformances and metatypes.
135+
void clear() {
136+
Conformances.clear();
137+
EscapingMetaTypes.clear();
138+
Visited.clear();
139+
}
140+
141+
/// For debug purposes.
142+
void dump();
143+
144+
/// Returns the list of collected used conformances.
145+
ArrayRef<const ProtocolConformance *> getUsedConformances() {
146+
return Conformances;
147+
}
148+
149+
/// Returns the list of collected escaping metatypes.
150+
ArrayRef<const NominalTypeDecl *> getEscapingMetaTypes() {
151+
return EscapingMetaTypes;
152+
}
153+
154+
/// Returns true if \p Conf is in the list of used conformances.
155+
bool isUsed(const ProtocolConformance *Conf) {
156+
return Visited.count(Conf) != 0;
157+
}
158+
159+
/// Returns true if the metatype of \p NT is in the list of escaping
160+
/// metatypes.
161+
bool isMetaTypeEscaping(const NominalTypeDecl *NT) {
162+
return Visited.count(NT) != 0;
163+
}
164+
};
165+
76166
/// A utility class for evaluating whether a newly parsed or deserialized
77167
/// function has qualified or unqualified ownership.
78168
///

include/swift/SIL/SILModule.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,9 @@ class SILModule {
535535
createDefaultWitnessTableDeclaration(const ProtocolDecl *Protocol,
536536
SILLinkage Linkage);
537537

538+
/// Deletes a dead witness table.
539+
void deleteWitnessTable(SILWitnessTable *Wt);
540+
538541
/// \brief Return the stage of processing this module is at.
539542
SILStage getStage() const { return Stage; }
540543

include/swift/SIL/SILWitnessTable.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ class SILWitnessTable : public llvm::ilist_node<SILWitnessTable>,
247247
for (Entry &entry : Entries) {
248248
if (entry.getKind() == WitnessKind::Method) {
249249
const MethodWitness &MW = entry.getMethodWitness();
250-
if (predicate(MW)) {
250+
if (MW.Witness && predicate(MW)) {
251251
entry.removeWitnessMethod();
252252
}
253253
}

lib/IRGen/GenDecl.cpp

Lines changed: 110 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -824,7 +824,17 @@ void IRGenerator::emitGlobalTopLevel() {
824824
// Emit witness tables.
825825
for (SILWitnessTable &wt : PrimaryIGM->getSILModule().getWitnessTableList()) {
826826
CurrentIGMPtr IGM = getGenModule(wt.getConformance()->getDeclContext());
827+
#ifndef NDEBUG
828+
IGM->EligibleConfs.collect(&wt);
829+
IGM->CurrentWitnessTable = &wt;
830+
#endif
831+
827832
IGM->emitSILWitnessTable(&wt);
833+
834+
#ifndef NDEBUG
835+
IGM->EligibleConfs.clear();
836+
IGM->CurrentWitnessTable = nullptr;
837+
#endif
828838
}
829839

830840
for (auto Iter : *this) {
@@ -1049,7 +1059,8 @@ static SILLinkage getNonUniqueSILLinkage(FormalLinkage linkage,
10491059

10501060
static SILLinkage getConformanceLinkage(IRGenModule &IGM,
10511061
const ProtocolConformance *conf) {
1052-
if (auto wt = IGM.getSILModule().lookUpWitnessTable(conf)) {
1062+
if (auto wt = IGM.getSILModule().lookUpWitnessTable(conf,
1063+
/*deserializeLazily*/ false)) {
10531064
return wt->getLinkage();
10541065
} else {
10551066
return SILLinkage::PublicExternal;
@@ -1232,18 +1243,25 @@ bool LinkEntity::isFragile(IRGenModule &IGM) const {
12321243
case Kind::SILGlobalVariable:
12331244
return getSILGlobalVariable()->isFragile();
12341245

1235-
case Kind::DirectProtocolWitnessTable: {
1236-
if (auto wt = IGM.getSILModule().lookUpWitnessTable(
1237-
getProtocolConformance())) {
1238-
return wt->isFragile();
1239-
} else {
1240-
return false;
1241-
}
1242-
}
1243-
1246+
case Kind::ReflectionAssociatedTypeDescriptor:
1247+
case Kind::ReflectionSuperclassDescriptor:
1248+
return false;
1249+
12441250
default:
12451251
break;
12461252
}
1253+
if (isProtocolConformanceKind(getKind())) {
1254+
if (SILWitnessTable *wt = IGM.getSILModule().lookUpWitnessTable(
1255+
getProtocolConformance(), false)) {
1256+
SILLinkage L = wt->getLinkage();
1257+
// We don't deserialize the fragile attribute correctly. But we know that
1258+
// if the witness table was deserialized (= available externally) and it's
1259+
// not public, it must be fragile.
1260+
if (swift::isAvailableExternally(L) && !hasPublicVisibility(L))
1261+
return true;
1262+
return wt->isFragile();
1263+
}
1264+
}
12471265
return false;
12481266
}
12491267

@@ -1835,6 +1853,75 @@ IRGenModule::getAddrOfLLVMVariable(LinkEntity entity, Alignment alignment,
18351853
llvm_unreachable("bad reference kind");
18361854
}
18371855

1856+
void IRGenModule::checkEligibleConf(const ProtocolConformance *Conf) {
1857+
#ifndef NDEBUG
1858+
if (EligibleConfs.isUsed(Conf))
1859+
return;
1860+
1861+
if (CurrentInst) {
1862+
llvm::errs() << "## Conformance: ";
1863+
Conf->dump();
1864+
llvm::errs() << "## Inst: ";
1865+
CurrentInst->dump();
1866+
llvm_unreachable(
1867+
"ConformanceCollector is missing a conformance in instruction");
1868+
}
1869+
if (CurrentWitnessTable) {
1870+
llvm_unreachable(
1871+
"ConformanceCollector is missing a conformance in witness table");
1872+
}
1873+
#endif
1874+
}
1875+
1876+
void IRGenModule::checkEligibleMetaType(NominalTypeDecl *NT) {
1877+
#ifndef NDEBUG
1878+
if (!NT || EligibleConfs.isMetaTypeEscaping(NT))
1879+
return;
1880+
1881+
if (CurrentInst) {
1882+
// Ignore instructions which may use a metatype but do not let escape it.
1883+
switch (CurrentInst->getKind()) {
1884+
case ValueKind::DestroyAddrInst:
1885+
case ValueKind::StructElementAddrInst:
1886+
case ValueKind::TupleElementAddrInst:
1887+
case ValueKind::InjectEnumAddrInst:
1888+
case ValueKind::SwitchEnumAddrInst:
1889+
case ValueKind::SelectEnumAddrInst:
1890+
case ValueKind::IndexAddrInst:
1891+
case ValueKind::RefElementAddrInst:
1892+
case ValueKind::AllocValueBufferInst:
1893+
case ValueKind::ProjectValueBufferInst:
1894+
case ValueKind::ProjectBoxInst:
1895+
case ValueKind::CopyAddrInst:
1896+
case ValueKind::UncheckedRefCastAddrInst:
1897+
case ValueKind::AllocStackInst:
1898+
case ValueKind::SuperMethodInst:
1899+
case ValueKind::WitnessMethodInst:
1900+
case ValueKind::DeallocRefInst:
1901+
case ValueKind::AllocGlobalInst:
1902+
return;
1903+
case ValueKind::ApplyInst:
1904+
case ValueKind::TryApplyInst:
1905+
case ValueKind::PartialApplyInst:
1906+
// It's not trivial to find the non-escaping vs. escaping meta-
1907+
// types of an apply. Therefore we just trust the ConformanceCollector
1908+
// that it does the right job.
1909+
return;
1910+
default:
1911+
break;
1912+
}
1913+
llvm::errs() << "## NominalType: " << NT->getName() << "\n## Inst: ";
1914+
CurrentInst->dump();
1915+
llvm_unreachable(
1916+
"ConformanceCollector is missing a metatype in instruction");
1917+
}
1918+
if (CurrentWitnessTable) {
1919+
llvm_unreachable(
1920+
"ConformanceCollector is missing a metatype in witness table");
1921+
}
1922+
#endif
1923+
}
1924+
18381925
/// A convenient wrapper around getAddrOfLLVMVariable which uses the
18391926
/// default type as the definition type.
18401927
llvm::Constant *
@@ -2368,6 +2455,7 @@ llvm::Function *
23682455
IRGenModule::getAddrOfTypeMetadataAccessFunction(CanType type,
23692456
ForDefinition_t forDefinition) {
23702457
assert(!type->hasArchetype() && !type->hasTypeParameter());
2458+
checkEligibleMetaType(type->getNominalOrBoundGenericNominal());
23712459
LinkEntity entity = LinkEntity::forTypeMetadataAccessFunction(type);
23722460
llvm::Function *&entry = GlobalFuncs[entity];
23732461
if (entry) {
@@ -2389,6 +2477,7 @@ IRGenModule::getAddrOfGenericTypeMetadataAccessFunction(
23892477
ForDefinition_t forDefinition) {
23902478
assert(!genericArgs.empty());
23912479
assert(nominal->isGenericContext());
2480+
checkEligibleMetaType(nominal);
23922481

23932482
auto type = nominal->getDeclaredType()->getCanonicalType();
23942483
assert(type->hasUnboundGenericType());
@@ -2411,6 +2500,7 @@ llvm::Constant *
24112500
IRGenModule::getAddrOfTypeMetadataLazyCacheVariable(CanType type,
24122501
ForDefinition_t forDefinition) {
24132502
assert(!type->hasArchetype() && !type->hasTypeParameter());
2503+
checkEligibleMetaType(type->getNominalOrBoundGenericNominal());
24142504
LinkEntity entity = LinkEntity::forTypeMetadataLazyCacheVariable(type);
24152505
return getAddrOfLLVMVariable(entity, getPointerAlignment(), forDefinition,
24162506
TypeMetadataPtrTy, DebugTypeInfo());
@@ -2570,6 +2660,7 @@ ConstantReference IRGenModule::getAddrOfTypeMetadata(CanType concreteType,
25702660
SymbolReferenceKind refKind) {
25712661
assert(isPattern || !isa<UnboundGenericType>(concreteType));
25722662

2663+
checkEligibleMetaType(concreteType->getNominalOrBoundGenericNominal());
25732664
llvm::Type *defaultVarTy;
25742665
unsigned adjustmentIndex;
25752666
Alignment alignment = getPointerAlignment();
@@ -2664,6 +2755,7 @@ ConstantReference IRGenModule::getAddrOfTypeMetadata(CanType concreteType,
26642755
llvm::Constant *IRGenModule::getAddrOfNominalTypeDescriptor(NominalTypeDecl *D,
26652756
llvm::Type *definitionType) {
26662757
assert(definitionType && "not defining nominal type descriptor?");
2758+
checkEligibleMetaType(D);
26672759
auto entity = LinkEntity::forNominalTypeDescriptor(D);
26682760
return getAddrOfLLVMVariable(entity, getPointerAlignment(),
26692761
definitionType, definitionType,
@@ -3040,6 +3132,7 @@ IRGenModule::getResilienceExpansionForLayout(SILGlobalVariable *global) {
30403132
llvm::Constant *IRGenModule::
30413133
getAddrOfGenericWitnessTableCache(const NormalProtocolConformance *conf,
30423134
ForDefinition_t forDefinition) {
3135+
checkEligibleConf(conf);
30433136
auto entity = LinkEntity::forGenericProtocolWitnessTableCache(conf);
30443137
auto expectedTy = getGenericWitnessTableCacheTy();
30453138
auto storageTy = (forDefinition ? expectedTy : nullptr);
@@ -3050,6 +3143,7 @@ getAddrOfGenericWitnessTableCache(const NormalProtocolConformance *conf,
30503143
llvm::Function *
30513144
IRGenModule::getAddrOfGenericWitnessTableInstantiationFunction(
30523145
const NormalProtocolConformance *conf) {
3146+
checkEligibleConf(conf);
30533147
auto forDefinition = ForDefinition;
30543148

30553149
LinkEntity entity =
@@ -3096,6 +3190,7 @@ llvm::Function *
30963190
IRGenModule::getAddrOfWitnessTableAccessFunction(
30973191
const NormalProtocolConformance *conf,
30983192
ForDefinition_t forDefinition) {
3193+
checkEligibleConf(conf);
30993194
LinkEntity entity = LinkEntity::forProtocolWitnessTableAccessFunction(conf);
31003195
llvm::Function *&entry = GlobalFuncs[entity];
31013196
if (entry) {
@@ -3122,6 +3217,7 @@ IRGenModule::getAddrOfWitnessTableLazyAccessFunction(
31223217
const NormalProtocolConformance *conf,
31233218
CanType conformingType,
31243219
ForDefinition_t forDefinition) {
3220+
checkEligibleConf(conf);
31253221
LinkEntity entity =
31263222
LinkEntity::forProtocolWitnessTableLazyAccessFunction(conf, conformingType);
31273223
llvm::Function *&entry = GlobalFuncs[entity];
@@ -3146,6 +3242,7 @@ IRGenModule::getAddrOfWitnessTableLazyCacheVariable(
31463242
CanType conformingType,
31473243
ForDefinition_t forDefinition) {
31483244
assert(!conformingType->hasArchetype());
3245+
checkEligibleConf(conf);
31493246
LinkEntity entity =
31503247
LinkEntity::forProtocolWitnessTableLazyCacheVariable(conf, conformingType);
31513248
return getAddrOfLLVMVariable(entity, getPointerAlignment(),
@@ -3162,6 +3259,7 @@ IRGenModule::getAddrOfWitnessTableLazyCacheVariable(
31623259
llvm::Constant*
31633260
IRGenModule::getAddrOfWitnessTable(const NormalProtocolConformance *conf,
31643261
llvm::Type *storageTy) {
3262+
checkEligibleConf(conf);
31653263
auto entity = LinkEntity::forDirectProtocolWitnessTable(conf);
31663264
return getAddrOfLLVMVariable(entity, getPointerAlignment(), storageTy,
31673265
WitnessTableTy, DebugTypeInfo());
@@ -3171,6 +3269,7 @@ llvm::Function *
31713269
IRGenModule::getAddrOfAssociatedTypeMetadataAccessFunction(
31723270
const NormalProtocolConformance *conformance,
31733271
AssociatedTypeDecl *associate) {
3272+
checkEligibleConf(conformance);
31743273
auto forDefinition = ForDefinition;
31753274

31763275
LinkEntity entity =
@@ -3192,6 +3291,7 @@ IRGenModule::getAddrOfAssociatedTypeWitnessTableAccessFunction(
31923291
const NormalProtocolConformance *conformance,
31933292
AssociatedTypeDecl *associate,
31943293
ProtocolDecl *associateProtocol) {
3294+
checkEligibleConf(conformance);
31953295
auto forDefinition = ForDefinition;
31963296

31973297
assert(conformance->getProtocol() == associate->getProtocol());

lib/IRGen/IRGenModule.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,9 @@ IRGenModule::IRGenModule(IRGenerator &irgen,
127127
Module(*ClangCodeGen->GetModule()), LLVMContext(Module.getContext()),
128128
DataLayout(target->createDataLayout()), Triple(Context.LangOpts.Target),
129129
TargetMachine(std::move(target)), OutputFilename(OutputFilename),
130+
#ifndef NDEBUG
131+
EligibleConfs(getSILModule()),
132+
#endif
130133
TargetInfo(SwiftTargetInfo::get(*this)), DebugInfo(nullptr),
131134
ModuleHash(nullptr), ObjCInterop(Context.LangOpts.EnableObjCInterop),
132135
Types(*new TypeConverter(*this)) {

0 commit comments

Comments
 (0)