@@ -263,6 +263,55 @@ static Flags getMethodDescriptorFlags(ValueDecl *fn) {
263
263
return Flags (kind).withIsInstance (!fn->isStatic ());
264
264
}
265
265
266
+ static void buildMethodDescriptorFields (IRGenModule &IGM,
267
+ const SILVTable *VTable,
268
+ SILDeclRef fn,
269
+ ConstantStructBuilder &descriptor) {
270
+ auto *func = cast<AbstractFunctionDecl>(fn.getDecl ());
271
+ // Classify the method.
272
+ using Flags = MethodDescriptorFlags;
273
+ auto flags = getMethodDescriptorFlags<Flags>(func);
274
+
275
+ // Remember if the declaration was dynamic.
276
+ if (func->shouldUseObjCDispatch ())
277
+ flags = flags.withIsDynamic (true );
278
+
279
+ // Include the pointer-auth discriminator.
280
+ if (auto &schema = IGM.getOptions ().PointerAuth .SwiftClassMethods ) {
281
+ auto discriminator =
282
+ PointerAuthInfo::getOtherDiscriminator (IGM, schema, fn);
283
+ flags = flags.withExtraDiscriminator (discriminator->getZExtValue ());
284
+ }
285
+
286
+ // TODO: final? open?
287
+ descriptor.addInt (IGM.Int32Ty , flags.getIntValue ());
288
+
289
+ if (auto entry = VTable->getEntry (IGM.getSILModule (), fn)) {
290
+ assert (entry->getKind () == SILVTable::Entry::Kind::Normal);
291
+ auto *implFn = IGM.getAddrOfSILFunction (entry->getImplementation (),
292
+ NotForDefinition);
293
+ descriptor.addRelativeAddress (implFn);
294
+ } else {
295
+ // The method is removed by dead method elimination.
296
+ // It should be never called. We add a pointer to an error function.
297
+ descriptor.addRelativeAddressOrNull (nullptr );
298
+ }
299
+ }
300
+
301
+ void IRGenModule::emitNonoverriddenMethodDescriptor (const SILVTable *VTable,
302
+ SILDeclRef declRef) {
303
+
304
+ ConstantInitBuilder ib (*this );
305
+ ConstantStructBuilder sb (ib.beginStruct (MethodDescriptorStructTy));
306
+
307
+ buildMethodDescriptorFields (*this , VTable, declRef, sb);
308
+
309
+ auto init = sb.finishAndCreateFuture ();
310
+
311
+ auto entity = LinkEntity::forMethodDescriptor (declRef);
312
+ getAddrOfLLVMVariable (entity, init, DebugTypeInfo ());
313
+ }
314
+
266
315
namespace {
267
316
template <class Impl >
268
317
class ContextDescriptorBuilderBase {
@@ -1416,6 +1465,7 @@ namespace {
1416
1465
1417
1466
SILVTable *VTable;
1418
1467
bool Resilient;
1468
+ bool HasNonoverriddenMethods = false ;
1419
1469
1420
1470
SmallVector<SILDeclRef, 8 > VTableEntries;
1421
1471
SmallVector<std::pair<SILDeclRef, SILDeclRef>, 8 > OverrideTableEntries;
@@ -1442,7 +1492,7 @@ namespace {
1442
1492
void addMethod (SILDeclRef fn) {
1443
1493
if (!VTable || methodRequiresReifiedVTableEntry (IGM, VTable, fn)) {
1444
1494
VTableEntries.push_back (fn);
1445
- } else if (getType ()-> getEffectiveAccess () >= AccessLevel::Public ) {
1495
+ } else if (hasPublicVisibility (fn. getLinkage (NotForDefinition)) ) {
1446
1496
// Emit a stub method descriptor and lookup function for nonoverridden
1447
1497
// methods so that resilient code sequences can still use them.
1448
1498
emitNonoverriddenMethod (fn);
@@ -1539,14 +1589,15 @@ namespace {
1539
1589
}
1540
1590
);
1541
1591
1542
- if (VTableEntries.empty ())
1543
- return ;
1544
-
1545
1592
// Only emit a method lookup function if the class is resilient
1546
- // and has a non-empty vtable.
1547
- if (IGM.hasResilientMetadata (getType (), ResilienceExpansion::Minimal))
1593
+ // and has a non-empty vtable, as well as no elided methods.
1594
+ if (IGM.hasResilientMetadata (getType (), ResilienceExpansion::Minimal)
1595
+ && (HasNonoverriddenMethods || !VTableEntries.empty ()))
1548
1596
IGM.emitMethodLookupFunction (getType ());
1549
1597
1598
+ if (VTableEntries.empty ())
1599
+ return ;
1600
+
1550
1601
auto offset = MetadataLayout->hasResilientSuperclass ()
1551
1602
? MetadataLayout->getRelativeVTableOffset ()
1552
1603
: MetadataLayout->getStaticVTableOffset ();
@@ -1564,51 +1615,31 @@ namespace {
1564
1615
B.getAddrOfCurrentPosition (IGM.MethodDescriptorStructTy ));
1565
1616
1566
1617
// Actually build the descriptor.
1567
- auto *func = cast<AbstractFunctionDecl>(fn.getDecl ());
1568
1618
auto descriptor = B.beginStruct (IGM.MethodDescriptorStructTy );
1569
-
1570
- // Classify the method.
1571
- using Flags = MethodDescriptorFlags;
1572
- auto flags = getMethodDescriptorFlags<Flags>(func);
1573
-
1574
- // Remember if the declaration was dynamic.
1575
- if (func->shouldUseObjCDispatch ())
1576
- flags = flags.withIsDynamic (true );
1577
-
1578
- // Include the pointer-auth discriminator.
1579
- if (auto &schema = IGM.getOptions ().PointerAuth .SwiftClassMethods ) {
1580
- auto discriminator =
1581
- PointerAuthInfo::getOtherDiscriminator (IGM, schema, fn);
1582
- flags = flags.withExtraDiscriminator (discriminator->getZExtValue ());
1583
- }
1584
-
1585
- // TODO: final? open?
1586
- descriptor.addInt (IGM.Int32Ty , flags.getIntValue ());
1587
-
1588
- if (auto entry = VTable->getEntry (IGM.getSILModule (), fn)) {
1589
- assert (entry->getKind () == SILVTable::Entry::Kind::Normal);
1590
- auto *implFn = IGM.getAddrOfSILFunction (entry->getImplementation (),
1591
- NotForDefinition);
1592
- descriptor.addRelativeAddress (implFn);
1593
- } else {
1594
- // The method is removed by dead method elimination.
1595
- // It should be never called. We add a pointer to an error function.
1596
- descriptor.addRelativeAddressOrNull (nullptr );
1597
- }
1598
-
1619
+ buildMethodDescriptorFields (IGM, VTable, fn, descriptor);
1599
1620
descriptor.finishAndAddTo (B);
1600
1621
1601
1622
// Emit method dispatch thunk if the class is resilient.
1623
+ auto *func = cast<AbstractFunctionDecl>(fn.getDecl ());
1602
1624
if (Resilient &&
1603
1625
func->getEffectiveAccess () >= AccessLevel::Public) {
1604
1626
IGM.emitDispatchThunk (fn);
1605
1627
}
1606
1628
}
1607
1629
1608
1630
void emitNonoverriddenMethod (SILDeclRef fn) {
1609
- // TODO: Emit a freestanding method descriptor structure, and a method
1610
- // lookup function, to present the ABI of an overridable method even
1611
- // though the method has no real overrides currently.
1631
+ HasNonoverriddenMethods = true ;
1632
+ // Although this method is non-overridden and therefore left out of the
1633
+ // vtable, we still need to maintain the ABI of a potentially-overridden
1634
+ // method for external clients.
1635
+
1636
+ // Emit method dispatch thunk.
1637
+ IGM.emitDispatchThunk (fn);
1638
+ // Emit a freestanding method descriptor structure. This doesn't have to
1639
+ // exist in the table in the class's context descriptor since it isn't
1640
+ // in the vtable, but external clients need to be able to link against the
1641
+ // symbol.
1642
+ IGM.emitNonoverriddenMethodDescriptor (VTable, fn);
1612
1643
}
1613
1644
1614
1645
void addOverrideTable () {
@@ -5107,28 +5138,61 @@ void IRGenModule::emitOpaqueTypeDecl(OpaqueTypeDecl *D) {
5107
5138
bool irgen::methodRequiresReifiedVTableEntry (IRGenModule &IGM,
5108
5139
const SILVTable *vtable,
5109
5140
SILDeclRef method) {
5110
- auto entry = vtable->getEntry (IGM.getSILModule (), method);
5141
+ Optional<SILVTable::Entry> entry
5142
+ = vtable->getEntry (IGM.getSILModule (), method);
5143
+ LLVM_DEBUG (llvm::dbgs () << " looking at vtable:\n " ;
5144
+ vtable->print (llvm::dbgs ()));
5111
5145
if (!entry) {
5146
+ LLVM_DEBUG (llvm::dbgs () << " vtable entry in "
5147
+ << vtable->getClass ()->getName ()
5148
+ << " for " ;
5149
+ method.print (llvm::dbgs ());
5150
+ llvm::dbgs () << " is not available\n " );
5112
5151
return true ;
5113
5152
}
5153
+ LLVM_DEBUG (llvm::dbgs () << " entry: " ;
5154
+ entry->print (llvm::dbgs ());
5155
+ llvm::dbgs () << " \n " );
5114
5156
5115
5157
// We may be able to elide the vtable entry, ABI permitting, if it's not
5116
5158
// overridden.
5117
5159
if (!entry->isNonOverridden ()) {
5160
+ LLVM_DEBUG (llvm::dbgs () << " vtable entry in "
5161
+ << vtable->getClass ()->getName ()
5162
+ << " for " ;
5163
+ method.print (llvm::dbgs ());
5164
+ llvm::dbgs () << " is overridden\n " );
5118
5165
return true ;
5119
5166
}
5120
5167
5121
- // Does the ABI require a vtable entry to exist? If the class is public,
5168
+ // Does the ABI require a vtable entry to exist? If the class the vtable
5169
+ // entry originates from is public,
5122
5170
// and it's either marked fragile or part of a non-resilient module, then
5123
5171
// other modules will directly address vtable offsets and we can't remove
5124
5172
// vtable entries.
5125
- if (vtable->getClass ()->getEffectiveAccess () >= AccessLevel::Public) {
5126
- // TODO: Check whether we use a resilient ABI to access this
5127
- // class's methods. We can drop unnecessary vtable entries if we do;
5128
- // otherwise fixed vtable offsets are part of the ABI.
5129
- return true ;
5173
+ auto originatingClass =
5174
+ cast<ClassDecl>(method.getOverriddenVTableEntry ().getDecl ()->getDeclContext ());
5175
+
5176
+ if (originatingClass->getEffectiveAccess () >= AccessLevel::Public) {
5177
+ // If the class is public,
5178
+ // and it's either marked fragile or part of a non-resilient module, then
5179
+ // other modules will directly address vtable offsets and we can't remove
5180
+ // vtable entries.
5181
+ if (!originatingClass->isResilient ()) {
5182
+ LLVM_DEBUG (llvm::dbgs () << " vtable entry in "
5183
+ << vtable->getClass ()->getName ()
5184
+ << " for " ;
5185
+ method.print (llvm::dbgs ());
5186
+ llvm::dbgs () << " originates from a public fragile class\n " );
5187
+ return true ;
5188
+ }
5130
5189
}
5131
5190
5132
5191
// Otherwise, we can leave this method out of the runtime vtable.
5192
+ LLVM_DEBUG (llvm::dbgs () << " vtable entry in "
5193
+ << vtable->getClass ()->getName ()
5194
+ << " for " ;
5195
+ method.print (llvm::dbgs ());
5196
+ llvm::dbgs () << " can be elided\n " );
5133
5197
return false ;
5134
5198
}
0 commit comments