Skip to content

Commit 9f2b6a4

Browse files
committed
Reuse _ContiguousArrayStorage<AnyObject> metadata for any class or objc generic type
Reduces the number of _ContiguousArrayStorage metadata. In order to support constant time bridging we do need to set the correct metadata when we bridge to Objective-C. This is so that the type check succeeds when bridging back from Objective-C to reuse the storage instance rather than bridging the elements. To support dynamically setting the `_ContiguousArrayStorage` element type i needed to add support for optimizing `alloc_ref_dynamic` throughout the optimizer. Possible future improvements: * Use different metadata such that we can disambiguate native Swift classes during destruction -- allowing native release rather then unknown release usage. * Optimize the newly added semantic function getContiguousArrayStorageType rdar://86171143
1 parent 8166ad4 commit 9f2b6a4

32 files changed

+224
-51
lines changed

SwiftCompilerSources/Sources/SIL/Instruction.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ final public class DeallocStackInst : Instruction, UnaryInstruction {
220220
}
221221

222222
final public class DeallocStackRefInst : Instruction, UnaryInstruction {
223-
public var allocRef: AllocRefInst { operand as! AllocRefInst }
223+
public var allocRef: AllocRefInstBase { operand as! AllocRefInstBase }
224224
}
225225

226226
final public class CondFailInst : Instruction, UnaryInstruction {

include/swift/AST/KnownStdlibTypes.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ KNOWN_STDLIB_TYPE_DECL(String, NominalTypeDecl, 0)
4949
KNOWN_STDLIB_TYPE_DECL(StaticString, NominalTypeDecl, 0)
5050
KNOWN_STDLIB_TYPE_DECL(Substring, NominalTypeDecl, 0)
5151
KNOWN_STDLIB_TYPE_DECL(Array, NominalTypeDecl, 1)
52+
KNOWN_STDLIB_TYPE_DECL(_ContiguousArrayStorage, NominalTypeDecl, 1)
5253
KNOWN_STDLIB_TYPE_DECL(Set, NominalTypeDecl, 1)
5354
KNOWN_STDLIB_TYPE_DECL(Sequence, NominalTypeDecl, 1)
5455
KNOWN_STDLIB_TYPE_DECL(Dictionary, NominalTypeDecl, 2)

include/swift/SIL/SILBuilder.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -408,14 +408,15 @@ class SILBuilder {
408408

409409
AllocRefDynamicInst *createAllocRefDynamic(SILLocation Loc, SILValue operand,
410410
SILType type, bool objc,
411+
bool canAllocOnStack,
411412
ArrayRef<SILType> ElementTypes,
412413
ArrayRef<SILValue> ElementCountOperands) {
413414
// AllocRefDynamicInsts expand to function calls and can therefore
414415
// not be counted towards the function prologue.
415416
assert(!Loc.isInPrologue());
416417
return insert(AllocRefDynamicInst::create(
417-
getSILDebugLocation(Loc), *F, operand, type, objc, ElementTypes,
418-
ElementCountOperands));
418+
getSILDebugLocation(Loc), *F, operand, type, objc, canAllocOnStack,
419+
ElementTypes, ElementCountOperands));
419420
}
420421

421422
AllocBoxInst *createAllocBox(SILLocation Loc, CanSILBoxType BoxType,

include/swift/SIL/SILCloner.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -839,6 +839,7 @@ SILCloner<ImplClass>::visitAllocRefDynamicInst(AllocRefDynamicInst *Inst) {
839839
getOpValue(Inst->getMetatypeOperand()),
840840
getOpType(Inst->getType()),
841841
Inst->isObjC(),
842+
Inst->canAllocOnStack(),
842843
ElemTypes, CountArgs);
843844
recordClonedInstruction(Inst, NewInst);
844845
}

include/swift/SIL/SILInstruction.h

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2102,6 +2102,26 @@ class AllocRefInstBase : public AllocationInst {
21022102
bool isObjC() const {
21032103
return SILNode::Bits.AllocRefInstBase.ObjC;
21042104
}
2105+
2106+
static bool classof(SILNodePointer node) {
2107+
if (auto *i = dyn_cast<SILInstruction>(node.get()))
2108+
return classof(i);
2109+
return false;
2110+
}
2111+
2112+
static bool classof(const SILInstruction *inst) {
2113+
return classof(inst->getKind());
2114+
}
2115+
2116+
static bool classof(SILInstructionKind kind) {
2117+
switch (kind) {
2118+
case SILInstructionKind::AllocRefInst:
2119+
case SILInstructionKind::AllocRefDynamicInst:
2120+
return true;
2121+
default:
2122+
return false;
2123+
}
2124+
}
21052125
};
21062126

21072127
/// AllocRefInst - This represents the primitive allocation of an instance
@@ -2162,10 +2182,11 @@ class AllocRefDynamicInst final
21622182
AllocRefDynamicInst(SILDebugLocation DebugLoc,
21632183
SILType ty,
21642184
bool objc,
2185+
bool canBeOnStack,
21652186
ArrayRef<SILType> ElementTypes,
21662187
ArrayRef<SILValue> AllOperands)
21672188
: InstructionBaseWithTrailingOperands(AllOperands, DebugLoc, ty, objc,
2168-
false, ElementTypes) {
2189+
canBeOnStack, ElementTypes) {
21692190
assert(AllOperands.size() >= ElementTypes.size() + 1);
21702191
std::uninitialized_copy(ElementTypes.begin(), ElementTypes.end(),
21712192
getTrailingObjects<SILType>());
@@ -2174,6 +2195,7 @@ class AllocRefDynamicInst final
21742195
static AllocRefDynamicInst *
21752196
create(SILDebugLocation DebugLoc, SILFunction &F,
21762197
SILValue metatypeOperand, SILType ty, bool objc,
2198+
bool canBeOnStack,
21772199
ArrayRef<SILType> ElementTypes,
21782200
ArrayRef<SILValue> ElementCountOperands);
21792201

@@ -2189,6 +2211,9 @@ class AllocRefDynamicInst final
21892211
MutableArrayRef<Operand> getTypeDependentOperands() {
21902212
return getAllOperands().slice(getNumTailTypes() + 1);
21912213
}
2214+
// Is the deinit and the size of the dynamic type known to be equivalent to
2215+
// the the base type (i.e `this->getType()`).
2216+
bool isDynamicTypeDeinitAndSizeKnownEquivalentToBaseType() const;
21922217
};
21932218

21942219
/// This represents the allocation of a heap box for a Swift value of some type.
@@ -7649,7 +7674,9 @@ class DeallocStackRefInst
76497674
DeallocStackRefInst(SILDebugLocation DebugLoc, SILValue Operand)
76507675
: UnaryInstructionBase(DebugLoc, Operand) {}
76517676
public:
7652-
AllocRefInst *getAllocRef() { return cast<AllocRefInst>(getOperand()); }
7677+
AllocRefInstBase *getAllocRef() {
7678+
return cast<AllocRefInstBase>(getOperand());
7679+
}
76537680
};
76547681

76557682
/// Deallocate memory for a reference type instance from a destructor or

lib/IRGen/GenClass.cpp

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -828,12 +828,32 @@ llvm::Value *irgen::emitClassAllocationDynamic(IRGenFunction &IGF,
828828
llvm::Value *metadata,
829829
SILType selfType,
830830
bool objc,
831+
int &StackAllocSize,
831832
TailArraysRef TailArrays) {
832833
// If we need to use Objective-C allocation, do so.
833834
if (objc) {
835+
StackAllocSize = -1;
834836
return emitObjCAllocObjectCall(IGF, metadata, selfType);
835837
}
836838

839+
llvm::Value *Promoted;
840+
auto &classTI = IGF.getTypeInfo(selfType).as<ClassTypeInfo>();
841+
auto &classLayout = classTI.getClassLayout(IGF.IGM, selfType,
842+
/*forBackwardDeployment=*/false);
843+
844+
// If we are allowed to allocate on the stack we are allowed to use
845+
// `selfType`'s size assumptions.
846+
if (StackAllocSize >= 0 &&
847+
(Promoted = stackPromote(IGF, classLayout, StackAllocSize,
848+
TailArrays))) {
849+
llvm::Value *val = IGF.Builder.CreateBitCast(Promoted,
850+
IGF.IGM.RefCountedPtrTy);
851+
val = IGF.emitInitStackObjectCall(metadata, val, "reference.new");
852+
853+
llvm::Type *destType = classLayout.getType()->getPointerTo();
854+
return IGF.Builder.CreateBitCast(val, destType);
855+
}
856+
837857
// Otherwise, allocate using Swift's routines.
838858
llvm::Value *size, *alignMask;
839859
std::tie(size, alignMask)
@@ -845,10 +865,8 @@ llvm::Value *irgen::emitClassAllocationDynamic(IRGenFunction &IGF,
845865

846866
llvm::Value *val = IGF.emitAllocObjectCall(metadata, size, alignMask,
847867
"reference.new");
848-
auto &classTI = IGF.getTypeInfo(selfType).as<ClassTypeInfo>();
849-
auto &layout = classTI.getClassLayout(IGF.IGM, selfType,
850-
/*forBackwardDeployment=*/false);
851-
llvm::Type *destType = layout.getType()->getPointerTo();
868+
StackAllocSize = -1;
869+
llvm::Type *destType = classLayout.getType()->getPointerTo();
852870
return IGF.Builder.CreateBitCast(val, destType);
853871
}
854872

lib/IRGen/GenClass.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,10 +167,12 @@ namespace irgen {
167167
bool objc, int &StackAllocSize, TailArraysRef TailArrays);
168168

169169
/// Emit an allocation of a class using a metadata value.
170-
llvm::Value *emitClassAllocationDynamic(IRGenFunction &IGF,
170+
llvm::Value *emitClassAllocationDynamic(IRGenFunction &IGF,
171171
llvm::Value *metadata,
172172
SILType selfType,
173-
bool objc, TailArraysRef TailArrays);
173+
bool objc,
174+
int &StackAllocSize,
175+
TailArraysRef TailArrays);
174176

175177
/// Emit class deallocation.
176178
void emitClassDeallocation(IRGenFunction &IGF, SILType selfType,

lib/IRGen/IRGenSIL.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5425,14 +5425,30 @@ void IRGenSILFunction::visitAllocRefInst(swift::AllocRefInst *i) {
54255425
}
54265426

54275427
void IRGenSILFunction::visitAllocRefDynamicInst(swift::AllocRefDynamicInst *i) {
5428+
int StackAllocSize = -1;
5429+
if (i->canAllocOnStack()) {
5430+
assert(i->isDynamicTypeDeinitAndSizeKnownEquivalentToBaseType());
5431+
estimateStackSize();
5432+
// Is there enough space for stack allocation?
5433+
StackAllocSize = IGM.IRGen.Opts.StackPromotionSizeLimit - EstimatedStackSize;
5434+
}
5435+
54285436
SmallVector<std::pair<SILType, llvm::Value *>, 4> TailArrays;
54295437
buildTailArrays(*this, TailArrays, i);
54305438

54315439
Explosion metadata = getLoweredExplosion(i->getMetatypeOperand());
54325440
auto metadataValue = metadata.claimNext();
54335441
llvm::Value *alloced = emitClassAllocationDynamic(*this, metadataValue,
54345442
i->getType(), i->isObjC(),
5443+
StackAllocSize,
54355444
TailArrays);
5445+
5446+
if (StackAllocSize >= 0) {
5447+
// Remember that this alloc_ref_dynamic allocates the object on the stack.
5448+
StackAllocs.insert(i);
5449+
EstimatedStackSize += StackAllocSize;
5450+
}
5451+
54365452
Explosion e;
54375453
e.add(alloced);
54385454
setLoweredExplosion(i, e);
@@ -5476,7 +5492,7 @@ void IRGenSILFunction::visitDeallocRefInst(swift::DeallocRefInst *i) {
54765492
// Lower the operand.
54775493
Explosion self = getLoweredExplosion(i->getOperand());
54785494
auto selfValue = self.claimNext();
5479-
auto *ARI = dyn_cast<AllocRefInst>(i->getOperand());
5495+
auto *ARI = dyn_cast<AllocRefInstBase>(i->getOperand());
54805496
if (ARI && StackAllocs.count(ARI)) {
54815497
// We can ignore dealloc_refs for stack allocated objects.
54825498
//

lib/SIL/IR/Linker.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,20 @@ void SILLinkerVisitor::visitInitExistentialRefInst(
327327
visitProtocolConformance(C, Optional<SILDeclRef>());
328328
}
329329
}
330+
void SILLinkerVisitor::visitAllocRefDynamicInst(AllocRefDynamicInst *ARI) {
331+
if (!isLinkAll())
332+
return;
333+
334+
if (!ARI->isDynamicTypeDeinitAndSizeKnownEquivalentToBaseType())
335+
return;
336+
337+
// Grab the class decl from the alloc ref inst.
338+
ClassDecl *D = ARI->getType().getClassOrBoundGenericClass();
339+
if (!D)
340+
return;
341+
342+
linkInVTable(D);
343+
}
330344

331345
void SILLinkerVisitor::visitAllocRefInst(AllocRefInst *ARI) {
332346
if (!isLinkAll())

lib/SIL/IR/Linker.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ class SILLinkerVisitor : public SILInstructionVisitor<SILLinkerVisitor, void> {
7272
void visitInitExistentialAddrInst(InitExistentialAddrInst *IEI);
7373
void visitInitExistentialRefInst(InitExistentialRefInst *IERI);
7474
void visitAllocRefInst(AllocRefInst *ARI);
75+
void visitAllocRefDynamicInst(AllocRefDynamicInst *ARI);
7576
void visitMetatypeInst(MetatypeInst *MI);
7677

7778
private:

0 commit comments

Comments
 (0)