Skip to content

Commit 6c09803

Browse files
committed
Lift the decision of whether a method needs a vtable slot up to AST.
This lets us serialize that decision, which means we can conceivably /change/ the decision in later versions of the compiler without breaking existing code. More immediately, it's groundwork that will eventually allow us to drop decls from the AST without affecting vtable layout. This isn't actually a great answer; what we really want is for SIL vtables to be serialized consistently and treated as the point of truth. But that would be more change than we're comfortable taking in the Swift 4 timeframe. First part of rdar://problem/31878396.
1 parent 2e61205 commit 6c09803

File tree

10 files changed

+148
-115
lines changed

10 files changed

+148
-115
lines changed

include/swift/AST/Decl.h

Lines changed: 50 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -365,8 +365,14 @@ class alignas(1 << DeclAlignInBits) Decl {
365365

366366
/// Whether the function body throws.
367367
unsigned Throws : 1;
368+
369+
/// Whether this function requires a new vtable entry.
370+
unsigned NeedsNewVTableEntry : 1;
371+
372+
/// Whether NeedsNewVTableEntry is valid.
373+
unsigned HasComputedNeedsNewVTableEntry : 1;
368374
};
369-
enum { NumAbstractFunctionDeclBits = NumValueDeclBits + 11 };
375+
enum { NumAbstractFunctionDeclBits = NumValueDeclBits + 13 };
370376
static_assert(NumAbstractFunctionDeclBits <= 32, "fits in an unsigned");
371377

372378
class FuncDeclBitfields {
@@ -378,15 +384,8 @@ class alignas(1 << DeclAlignInBits) Decl {
378384

379385
/// \brief Whether 'static' or 'class' was used.
380386
unsigned StaticSpelling : 2;
381-
382-
/// Whether this function is a 'mutating' method.
383-
unsigned Mutating : 1;
384-
385-
/// Whether this function has a dynamic Self return type.
386-
unsigned HasDynamicSelf : 1;
387-
388387
};
389-
enum { NumFuncDeclBits = NumAbstractFunctionDeclBits + 5 };
388+
enum { NumFuncDeclBits = NumAbstractFunctionDeclBits + 3 };
390389
static_assert(NumFuncDeclBits <= 32, "fits in an unsigned");
391390

392391
class ConstructorDeclBitfields {
@@ -399,17 +398,8 @@ class alignas(1 << DeclAlignInBits) Decl {
399398
/// of the definition of the constructor that is useful only to semantic
400399
/// analysis and SIL generation.
401400
unsigned ComputedBodyInitKind : 3;
402-
403-
/// Whether this initializer is a stub placed into a subclass to
404-
/// catch invalid delegations to a designated initializer not
405-
/// overridden by the subclass. A stub will always trap at runtime.
406-
///
407-
/// Initializer stubs can be invoked from Objective-C or through
408-
/// the Objective-C runtime; there is no way to directly express
409-
/// an object construction that will invoke a stub.
410-
unsigned HasStubImplementation : 1;
411401
};
412-
enum { NumConstructorDeclBits = NumAbstractFunctionDeclBits + 4 };
402+
enum { NumConstructorDeclBits = NumAbstractFunctionDeclBits + 3 };
413403
static_assert(NumConstructorDeclBits <= 32, "fits in an unsigned");
414404

415405
class TypeDeclBitfields {
@@ -4790,6 +4780,8 @@ class AbstractFunctionDecl : public ValueDecl, public GenericContext {
47904780
AbstractFunctionDeclBits.NumParameterLists = NumParameterLists;
47914781
AbstractFunctionDeclBits.Overridden = false;
47924782
AbstractFunctionDeclBits.Throws = Throws;
4783+
AbstractFunctionDeclBits.NeedsNewVTableEntry = false;
4784+
AbstractFunctionDeclBits.HasComputedNeedsNewVTableEntry = false;
47934785

47944786
// Verify no bitfield truncation.
47954787
assert(AbstractFunctionDeclBits.NumParameterLists == NumParameterLists);
@@ -4903,6 +4895,21 @@ class AbstractFunctionDecl : public ValueDecl, public GenericContext {
49034895
return getBodyKind() == BodyKind::MemberwiseInitializer;
49044896
}
49054897

4898+
void setNeedsNewVTableEntry(bool value) {
4899+
AbstractFunctionDeclBits.HasComputedNeedsNewVTableEntry = true;
4900+
AbstractFunctionDeclBits.NeedsNewVTableEntry = value;
4901+
}
4902+
4903+
bool needsNewVTableEntry() const {
4904+
if (!AbstractFunctionDeclBits.HasComputedNeedsNewVTableEntry)
4905+
const_cast<AbstractFunctionDecl *>(this)->computeNeedsNewVTableEntry();
4906+
return AbstractFunctionDeclBits.NeedsNewVTableEntry;
4907+
}
4908+
4909+
private:
4910+
void computeNeedsNewVTableEntry();
4911+
4912+
public:
49064913
/// Retrieve the source range of the function body.
49074914
SourceRange getBodySourceRange() const;
49084915

@@ -5059,6 +5066,12 @@ class FuncDecl final : public AbstractFunctionDecl,
50595066
/// Whether we are statically dispatched even if overridable
50605067
unsigned ForcedStaticDispatch : 1;
50615068

5069+
/// Whether this function has a dynamic Self return type.
5070+
unsigned HasDynamicSelf : 1;
5071+
5072+
/// Whether this function is a 'mutating' method.
5073+
unsigned Mutating : 1;
5074+
50625075
/// \brief If this FuncDecl is an accessor for a property, this indicates
50635076
/// which property and what kind of accessor.
50645077
llvm::PointerIntPair<AbstractStorageDecl*, 3, AccessorKind> AccessorDecl;
@@ -5086,9 +5099,9 @@ class FuncDecl final : public AbstractFunctionDecl,
50865099
StaticLoc.isValid() || StaticSpelling != StaticSpellingKind::None;
50875100
FuncDeclBits.StaticSpelling = static_cast<unsigned>(StaticSpelling);
50885101
assert(NumParameterLists > 0 && "Must have at least an empty tuple arg");
5089-
FuncDeclBits.Mutating = false;
5090-
FuncDeclBits.HasDynamicSelf = false;
50915102

5103+
Mutating = false;
5104+
HasDynamicSelf = false;
50925105
ForcedStaticDispatch = false;
50935106
HaveSearchedForCommonOverloadReturnType = false;
50945107
HaveFoundCommonOverloadReturnType = false;
@@ -5141,10 +5154,10 @@ class FuncDecl final : public AbstractFunctionDecl,
51415154
FuncDeclBits.IsStatic = IsStatic;
51425155
}
51435156
bool isMutating() const {
5144-
return FuncDeclBits.Mutating;
5157+
return Mutating;
51455158
}
5146-
void setMutating(bool Mutating = true) {
5147-
FuncDeclBits.Mutating = Mutating;
5159+
void setMutating(bool mutating = true) {
5160+
Mutating = mutating;
51485161
}
51495162

51505163
/// \brief Returns the parameter lists(s) for the function definition.
@@ -5266,11 +5279,11 @@ class FuncDecl final : public AbstractFunctionDecl,
52665279

52675280
/// Determine whether this function has a dynamic \c Self return
52685281
/// type.
5269-
bool hasDynamicSelf() const { return FuncDeclBits.HasDynamicSelf; }
5282+
bool hasDynamicSelf() const { return HasDynamicSelf; }
52705283

52715284
/// Set whether this function has a dynamic \c Self return or not.
52725285
void setDynamicSelf(bool hasDynamicSelf) {
5273-
FuncDeclBits.HasDynamicSelf = hasDynamicSelf;
5286+
HasDynamicSelf = hasDynamicSelf;
52745287
}
52755288

52765289
void getLocalCaptures(SmallVectorImpl<CapturedValue> &Result) const {
@@ -5549,6 +5562,15 @@ class ConstructorDecl : public AbstractFunctionDecl {
55495562
/// The failability of this initializer, which is an OptionalTypeKind.
55505563
unsigned Failability : 2;
55515564

5565+
/// Whether this initializer is a stub placed into a subclass to
5566+
/// catch invalid delegations to a designated initializer not
5567+
/// overridden by the subclass. A stub will always trap at runtime.
5568+
///
5569+
/// Initializer stubs can be invoked from Objective-C or through
5570+
/// the Objective-C runtime; there is no way to directly express
5571+
/// an object construction that will invoke a stub.
5572+
unsigned HasStubImplementation : 1;
5573+
55525574
/// The location of the '!' or '?' for a failable initializer.
55535575
SourceLoc FailabilityLoc;
55545576

@@ -5708,13 +5730,13 @@ class ConstructorDecl : public AbstractFunctionDecl {
57085730

57095731
/// Whether the implementation of this method is a stub that traps at runtime.
57105732
bool hasStubImplementation() const {
5711-
return ConstructorDeclBits.HasStubImplementation;
5733+
return HasStubImplementation;
57125734
}
57135735

57145736
/// Set whether the implementation of this method is a stub that
57155737
/// traps at runtime.
57165738
void setStubImplementation(bool stub) {
5717-
ConstructorDeclBits.HasStubImplementation = stub;
5739+
HasStubImplementation = stub;
57185740
}
57195741

57205742
ConstructorDecl *getOverriddenDecl() const { return OverriddenDecl; }

include/swift/SIL/SILVTableVisitor.h

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -43,38 +43,33 @@ template <class T> class SILVTableVisitor {
4343
void maybeAddMethod(FuncDecl *fd) {
4444
assert(!fd->hasClangNode());
4545

46-
// Observing accessors and addressors don't get vtable entries.
47-
if (fd->isObservingAccessor() ||
48-
fd->getAddressorKind() != AddressorKind::NotAddressor)
49-
return;
50-
51-
maybeAddEntry(SILDeclRef(fd, SILDeclRef::Kind::Func));
46+
maybeAddEntry(SILDeclRef(fd, SILDeclRef::Kind::Func),
47+
fd->needsNewVTableEntry());
5248
}
5349

5450
void maybeAddConstructor(ConstructorDecl *cd) {
5551
assert(!cd->hasClangNode());
5652

57-
SILDeclRef initRef(cd, SILDeclRef::Kind::Initializer);
58-
59-
// Stub constructors don't get a vtable entry unless they were synthesized
60-
// to override a base class initializer.
61-
if (cd->hasStubImplementation() &&
62-
!initRef.getNextOverriddenVTableEntry())
63-
return;
64-
6553
// Required constructors (or overrides thereof) have their allocating entry
6654
// point in the vtable.
67-
if (cd->isRequired())
68-
maybeAddEntry(SILDeclRef(cd, SILDeclRef::Kind::Allocator));
55+
if (cd->isRequired()) {
56+
bool needsAllocatingEntry = cd->needsNewVTableEntry();
57+
if (!needsAllocatingEntry)
58+
if (auto *baseCD = cd->getOverriddenDecl())
59+
needsAllocatingEntry = !baseCD->isRequired();
60+
maybeAddEntry(SILDeclRef(cd, SILDeclRef::Kind::Allocator),
61+
needsAllocatingEntry);
62+
}
6963

7064
// All constructors have their initializing constructor in the
7165
// vtable, which can be used by a convenience initializer.
72-
maybeAddEntry(SILDeclRef(cd, SILDeclRef::Kind::Initializer));
66+
maybeAddEntry(SILDeclRef(cd, SILDeclRef::Kind::Initializer),
67+
cd->needsNewVTableEntry());
7368
}
7469

75-
void maybeAddEntry(SILDeclRef declRef) {
70+
void maybeAddEntry(SILDeclRef declRef, bool needsNewEntry) {
7671
// Introduce a new entry if required.
77-
if (Types.requiresNewVTableEntry(declRef))
72+
if (needsNewEntry)
7873
asDerived().addMethod(declRef);
7974

8075
// Update any existing entries that it overrides.

include/swift/SIL/TypeLowering.h

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -511,8 +511,6 @@ class TypeConverter {
511511

512512
llvm::DenseMap<OverrideKey, SILConstantInfo> ConstantOverrideTypes;
513513

514-
llvm::DenseMap<SILDeclRef, bool> RequiresVTableEntry;
515-
516514
llvm::DenseMap<AnyFunctionRef, CaptureInfo> LoweredCaptures;
517515

518516
CanAnyFunctionType makeConstantInterfaceType(SILDeclRef constant);
@@ -665,11 +663,6 @@ class TypeConverter {
665663
/// `constant` must refer to a method.
666664
SILParameterInfo getConstantSelfParameter(SILDeclRef constant);
667665

668-
/// Return if this method introduces a new vtable entry. This will be true
669-
/// if the method does not override any method of its base class, or if it
670-
/// overrides a method but has a more general AST type.
671-
bool requiresNewVTableEntry(SILDeclRef method);
672-
673666
/// Return the most derived override which requires a new vtable entry.
674667
/// If the method does not override anything or no override is vtable
675668
/// dispatched, will return the least derived method.
@@ -852,8 +845,6 @@ class TypeConverter {
852845
CanAnyFunctionType getBridgedFunctionType(AbstractionPattern fnPattern,
853846
CanAnyFunctionType fnType,
854847
AnyFunctionType::ExtInfo extInfo);
855-
856-
bool requiresNewVTableEntryUncached(SILDeclRef method);
857848
};
858849

859850
inline const TypeLowering &

include/swift/Serialization/ModuleFormat.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ const uint16_t VERSION_MAJOR = 0;
5454
/// in source control, you should also update the comment to briefly
5555
/// describe what change you made. The content of this comment isn't important;
5656
/// it just ensures a conflict if two people change the module format.
57-
const uint16_t VERSION_MINOR = 342; // Last change: keypath instruction
57+
const uint16_t VERSION_MINOR = 343; // Last change: new vtable entry flag
5858

5959
using DeclID = PointerEmbeddedInt<unsigned, 31>;
6060
using DeclIDField = BCFixed<31>;
@@ -858,6 +858,7 @@ namespace decls_block {
858858
TypeIDField, // canonical interface type
859859
DeclIDField, // overridden decl
860860
AccessibilityKindField, // accessibility
861+
BCFixed<1>, // requires a new vtable slot
861862
BCArray<IdentifierIDField> // argument names
862863
// Trailed by its generic parameters, if any, followed by the parameter
863864
// patterns.
@@ -916,6 +917,7 @@ namespace decls_block {
916917
BCFixed<1>, // name is compound?
917918
AddressorKindField, // addressor kind
918919
AccessibilityKindField, // accessibility
920+
BCFixed<1>, // requires a new vtable slot
919921
BCArray<IdentifierIDField> // name components
920922
// The record is trailed by:
921923
// - its _silgen_name, if any

lib/AST/Decl.cpp

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4608,6 +4608,63 @@ AbstractFunctionDecl *AbstractFunctionDecl::getOverriddenDecl() const {
46084608
return nullptr;
46094609
}
46104610

4611+
static bool requiresNewVTableEntry(const AbstractFunctionDecl *decl) {
4612+
assert(isa<FuncDecl>(decl) || isa<ConstructorDecl>(decl));
4613+
4614+
// Final members are always be called directly.
4615+
// Dynamic methods are always accessed by objc_msgSend().
4616+
if (decl->isFinal() || decl->isDynamic())
4617+
return false;
4618+
4619+
if (auto *fd = dyn_cast<FuncDecl>(decl)) {
4620+
switch (fd->getAccessorKind()) {
4621+
case AccessorKind::NotAccessor:
4622+
case AccessorKind::IsGetter:
4623+
case AccessorKind::IsSetter:
4624+
break;
4625+
case AccessorKind::IsAddressor:
4626+
case AccessorKind::IsMutableAddressor:
4627+
return false;
4628+
case AccessorKind::IsMaterializeForSet:
4629+
// Special case -- materializeForSet on dynamic storage is not
4630+
// itself dynamic, but should be treated as such for the
4631+
// purpose of constructing a vtable.
4632+
// FIXME: It should probably just be 'final'.
4633+
if (fd->getAccessorStorageDecl()->isDynamic())
4634+
return false;
4635+
break;
4636+
case AccessorKind::IsWillSet:
4637+
case AccessorKind::IsDidSet:
4638+
return false;
4639+
}
4640+
}
4641+
4642+
auto base = decl->getOverriddenDecl();
4643+
if (!base || base->hasClangNode())
4644+
return true;
4645+
4646+
// If the method overrides something, we only need a new entry if the
4647+
// override has a more general AST type. However an abstraction
4648+
// change is OK; we don't want to add a whole new vtable entry just
4649+
// because an @in parameter because @owned, or whatever.
4650+
auto baseInterfaceTy = base->getInterfaceType();
4651+
auto derivedInterfaceTy = decl->getInterfaceType();
4652+
4653+
auto selfInterfaceTy = decl->getDeclContext()->getDeclaredInterfaceType();
4654+
4655+
auto overrideInterfaceTy = selfInterfaceTy->adjustSuperclassMemberDeclType(
4656+
base, decl, baseInterfaceTy);
4657+
4658+
return !derivedInterfaceTy->matches(overrideInterfaceTy,
4659+
TypeMatchFlags::AllowABICompatible,
4660+
/*resolver*/nullptr);
4661+
}
4662+
4663+
void AbstractFunctionDecl::computeNeedsNewVTableEntry() {
4664+
setNeedsNewVTableEntry(requiresNewVTableEntry(this));
4665+
}
4666+
4667+
46114668
FuncDecl *FuncDecl::createImpl(ASTContext &Context,
46124669
SourceLoc StaticLoc,
46134670
StaticSpellingKind StaticSpelling,
@@ -4762,7 +4819,7 @@ ConstructorDecl::ConstructorDecl(DeclName Name, SourceLoc ConstructorLoc,
47624819
setParameterLists(SelfDecl, BodyParams);
47634820

47644821
ConstructorDeclBits.ComputedBodyInitKind = 0;
4765-
ConstructorDeclBits.HasStubImplementation = 0;
4822+
this->HasStubImplementation = 0;
47664823
this->InitKind = static_cast<unsigned>(CtorInitializerKind::Designated);
47674824
this->Failability = static_cast<unsigned>(Failability);
47684825
}

lib/ClangImporter/ImporterImpl.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1128,6 +1128,8 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
11281128
ASD->setSetterAccessibility(access);
11291129
// All imported decls are constructed fully validated.
11301130
D->setValidationStarted();
1131+
if (auto AFD = dyn_cast<AbstractFunctionDecl>(static_cast<Decl *>(D)))
1132+
AFD->setNeedsNewVTableEntry(false);
11311133
return D;
11321134
}
11331135

0 commit comments

Comments
 (0)