Skip to content

Commit 46b58ac

Browse files
committed
Re-apply "Reduce memory footprint of the Swift compiler"
Use malloc/free for allocating/freeing SIL instructions instead of using the BumpPtrAllocator. This allows for memory reuse and significantly reduces the memory footprint of the compiler. For example, a peak memory usage during a compilation of the standard library and StdlibUnitTest is reduced by 25%-30%. The performance of the compiler seems to be not affected by this change, i.e. no slowdown is measured. The use-after-free issues reported by build bots are fixed now. rdar://23303031
1 parent 13b679d commit 46b58ac

File tree

7 files changed

+85
-37
lines changed

7 files changed

+85
-37
lines changed

Diff for: include/swift/SIL/SILBasicBlock.h

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class SILArgument;
3030
class SILBasicBlock :
3131
public llvm::ilist_node<SILBasicBlock>, public SILAllocated<SILBasicBlock> {
3232
friend class SILSuccessor;
33+
friend class SILFunction;
3334
public:
3435
using InstListType = llvm::iplist<SILInstruction>;
3536
private:

Diff for: include/swift/SIL/SILInstruction.h

+7
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,13 @@ class SILInstruction : public ValueBase,public llvm::ilist_node<SILInstruction>{
8888
: ValueBase(Kind, TypeList), ParentBB(0), Location(*DebugLoc) {}
8989

9090
public:
91+
/// Instructions should be allocated using a dedicated instruction allocation
92+
/// function from the ContextTy.
93+
template <typename ContextTy>
94+
void *operator new(size_t Bytes, const ContextTy &C,
95+
size_t Alignment = alignof(ValueBase)) {
96+
return C.allocateInst(Bytes, Alignment);
97+
}
9198

9299
enum class MemoryBehavior {
93100
None,

Diff for: include/swift/SIL/SILModule.h

+6-5
Original file line numberDiff line numberDiff line change
@@ -531,12 +531,13 @@ class SILModule {
531531
bool PrintASTDecls = true) const;
532532

533533
/// Allocate memory using the module's internal allocator.
534-
void *allocate(unsigned Size, unsigned Align) const {
535-
if (getASTContext().LangOpts.UseMalloc)
536-
return AlignedAlloc(Size, Align);
534+
void *allocate(unsigned Size, unsigned Align) const;
537535

538-
return BPA.Allocate(Size, Align);
539-
}
536+
/// Allocate memory for an instruction using the module's internal allocator.
537+
void *allocateInst(unsigned Size, unsigned Align) const;
538+
539+
/// Deallocate memory of an instruction.
540+
void deallocateInst(SILInstruction *I);
540541

541542
/// \brief Looks up the llvm intrinsic ID and type for the builtin function.
542543
///

Diff for: lib/SIL/SILBasicBlock.cpp

+15-6
Original file line numberDiff line numberDiff line change
@@ -37,18 +37,25 @@ SILBasicBlock::SILBasicBlock(SILFunction *parent, SILBasicBlock *afterBB)
3737
}
3838
}
3939
SILBasicBlock::~SILBasicBlock() {
40-
// Notify the delete handlers that the instructions in this block are
41-
// being deleted.
42-
for (auto I = begin(), E = end(); I != E; ++I) {
43-
getModule().notifyDeleteHandlers(&*I);
44-
}
45-
4640
// Invalidate all of the basic block arguments.
4741
for (auto *Arg : BBArgList) {
4842
getModule().notifyDeleteHandlers(Arg);
4943
}
5044

45+
dropAllReferences();
46+
47+
// Notify the delete handlers that the instructions in this block are
48+
// being deleted.
49+
auto &M = getModule();
50+
for (auto I = begin(), E = end(); I != E;) {
51+
auto Inst = &*I;
52+
++I;
53+
M.notifyDeleteHandlers(Inst);
54+
erase(Inst);
55+
}
56+
5157
// iplist's destructor is going to destroy the InstList.
58+
InstList.clearAndLeakNodesUnsafely();
5259
}
5360

5461
int SILBasicBlock::getDebugID() {
@@ -86,7 +93,9 @@ void SILBasicBlock::remove(SILInstruction *I) {
8693
void SILBasicBlock::erase(SILInstruction *I) {
8794
// Notify the delete handlers that this instruction is going away.
8895
getModule().notifyDeleteHandlers(&*I);
96+
auto *F = getParent();
8997
InstList.erase(I);
98+
F->getModule().deallocateInst(I);
9099
}
91100

92101
/// This method unlinks 'self' from the containing SILFunction and deletes it.

Diff for: lib/SIL/SILFunction.cpp

+17-2
Original file line numberDiff line numberDiff line change
@@ -102,14 +102,29 @@ SILFunction::SILFunction(SILModule &Module, SILLinkage Linkage,
102102
}
103103

104104
SILFunction::~SILFunction() {
105-
#ifndef NDEBUG
106105
// If the function is recursive, a function_ref inst inside of the function
107106
// will give the function a non-zero ref count triggering the assertion. Thus
108107
// we drop all instruction references before we erase.
108+
// We also need to drop all references if instructions are allocated using
109+
// an allocator that may recycle freed memory.
109110
dropAllReferences();
111+
112+
auto &M = getModule();
113+
for (auto &BB : *this) {
114+
for (auto I = BB.begin(), E = BB.end(); I != E;) {
115+
auto Inst = &*I;
116+
++I;
117+
SILInstruction::destroy(Inst);
118+
// TODO: It is only safe to directly deallocate an
119+
// instruction if this BB is being removed in scope
120+
// of destructing a SILFunction.
121+
M.deallocateInst(Inst);
122+
}
123+
BB.InstList.clearAndLeakNodesUnsafely();
124+
}
125+
110126
assert(RefCount == 0 &&
111127
"Function cannot be deleted while function_ref's still exist");
112-
#endif
113128
}
114129

115130
void SILFunction::setDeclContext(Decl *D) {

Diff for: lib/SIL/SILInstructions.cpp

+24-24
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,8 @@ AllocExistentialBoxInst *AllocExistentialBoxInst::create(
127127
SILType ConcreteLoweredType, ArrayRef<ProtocolConformance *> Conformances,
128128
SILFunction *F) {
129129
SILModule &Mod = F->getModule();
130-
void *Buffer = Mod.allocate(sizeof(AllocExistentialBoxInst),
131-
alignof(AllocExistentialBoxInst));
130+
void *Buffer = Mod.allocateInst(sizeof(AllocExistentialBoxInst),
131+
alignof(AllocExistentialBoxInst));
132132
for (ProtocolConformance *C : Conformances)
133133
declareWitnessTable(Mod, C);
134134
return ::new (Buffer) AllocExistentialBoxInst(Loc,
@@ -143,7 +143,7 @@ BuiltinInst *BuiltinInst::create(SILDebugLocation *Loc, Identifier Name,
143143
ArrayRef<Substitution> Substitutions,
144144
ArrayRef<SILValue> Args,
145145
SILFunction &F) {
146-
void *Buffer = F.getModule().allocate(
146+
void *Buffer = F.getModule().allocateInst(
147147
sizeof(BuiltinInst)
148148
+ decltype(Operands)::getExtraSize(Args.size())
149149
+ sizeof(Substitution) * Substitutions.size(),
@@ -190,7 +190,7 @@ bool swift::doesApplyCalleeHaveSemantics(SILValue callee, StringRef semantics) {
190190
}
191191

192192
void *swift::allocateApplyInst(SILFunction &F, size_t size, size_t alignment) {
193-
return F.getModule().allocate(size, alignment);
193+
return F.getModule().allocateInst(size, alignment);
194194
}
195195

196196
PartialApplyInst::PartialApplyInst(SILDebugLocation *Loc, SILValue Callee,
@@ -276,14 +276,14 @@ static unsigned getWordsForBitWidth(unsigned bits) {
276276

277277
template<typename INST>
278278
static void *allocateLiteralInstWithTextSize(SILFunction &F, unsigned length) {
279-
return F.getModule().allocate(sizeof(INST) + length, alignof(INST));
279+
return F.getModule().allocateInst(sizeof(INST) + length, alignof(INST));
280280
}
281281

282282
template<typename INST>
283283
static void *allocateLiteralInstWithBitSize(SILFunction &F, unsigned bits) {
284284
unsigned words = getWordsForBitWidth(bits);
285-
return F.getModule().allocate(sizeof(INST) + sizeof(llvm::integerPart)*words,
286-
alignof(INST));
285+
return F.getModule().allocateInst(
286+
sizeof(INST) + sizeof(llvm::integerPart)*words, alignof(INST));
287287
}
288288

289289
IntegerLiteralInst::IntegerLiteralInst(SILDebugLocation *Loc, SILType Ty,
@@ -409,7 +409,7 @@ AssignInst::AssignInst(SILDebugLocation *Loc, SILValue Src, SILValue Dest)
409409
MarkFunctionEscapeInst *
410410
MarkFunctionEscapeInst::create(SILDebugLocation *Loc,
411411
ArrayRef<SILValue> Elements, SILFunction &F) {
412-
void *Buffer = F.getModule().allocate(sizeof(MarkFunctionEscapeInst) +
412+
void *Buffer = F.getModule().allocateInst(sizeof(MarkFunctionEscapeInst) +
413413
decltype(Operands)::getExtraSize(Elements.size()),
414414
alignof(MarkFunctionEscapeInst));
415415
return ::new(Buffer) MarkFunctionEscapeInst(Loc, Elements);
@@ -452,7 +452,7 @@ UnconditionalCheckedCastAddrInst::UnconditionalCheckedCastAddrInst(
452452

453453
StructInst *StructInst::create(SILDebugLocation *Loc, SILType Ty,
454454
ArrayRef<SILValue> Elements, SILFunction &F) {
455-
void *Buffer = F.getModule().allocate(sizeof(StructInst) +
455+
void *Buffer = F.getModule().allocateInst(sizeof(StructInst) +
456456
decltype(Operands)::getExtraSize(Elements.size()),
457457
alignof(StructInst));
458458
return ::new(Buffer) StructInst(Loc, Ty, Elements);
@@ -466,7 +466,7 @@ StructInst::StructInst(SILDebugLocation *Loc, SILType Ty,
466466

467467
TupleInst *TupleInst::create(SILDebugLocation *Loc, SILType Ty,
468468
ArrayRef<SILValue> Elements, SILFunction &F) {
469-
void *Buffer = F.getModule().allocate(sizeof(TupleInst) +
469+
void *Buffer = F.getModule().allocateInst(sizeof(TupleInst) +
470470
decltype(Operands)::getExtraSize(Elements.size()),
471471
alignof(TupleInst));
472472
return ::new(Buffer) TupleInst(Loc, Ty, Elements);
@@ -669,7 +669,7 @@ BranchInst *BranchInst::create(SILDebugLocation *Loc, SILBasicBlock *DestBB,
669669
BranchInst *BranchInst::create(SILDebugLocation *Loc,
670670
SILBasicBlock *DestBB, ArrayRef<SILValue> Args,
671671
SILFunction &F) {
672-
void *Buffer = F.getModule().allocate(sizeof(BranchInst) +
672+
void *Buffer = F.getModule().allocateInst(sizeof(BranchInst) +
673673
decltype(Operands)::getExtraSize(Args.size()),
674674
alignof(BranchInst));
675675
return ::new (Buffer) BranchInst(Loc, DestBB, Args);
@@ -703,7 +703,7 @@ CondBranchInst::create(SILDebugLocation *Loc, SILValue Condition,
703703
Args.append(TrueArgs.begin(), TrueArgs.end());
704704
Args.append(FalseArgs.begin(), FalseArgs.end());
705705

706-
void *Buffer = F.getModule().allocate(sizeof(CondBranchInst) +
706+
void *Buffer = F.getModule().allocateInst(sizeof(CondBranchInst) +
707707
decltype(Operands)::getExtraSize(Args.size()),
708708
alignof(CondBranchInst));
709709
return ::new (Buffer) CondBranchInst(Loc, Condition, TrueBB, FalseBB, Args,
@@ -853,7 +853,7 @@ SwitchValueInst *SwitchValueInst::create(
853853
size_t bufSize = sizeof(SwitchValueInst) +
854854
decltype(Operands)::getExtraSize(Cases.size()) +
855855
sizeof(SILSuccessor) * numSuccessors;
856-
void *buf = F.getModule().allocate(bufSize, alignof(SwitchValueInst));
856+
void *buf = F.getModule().allocateInst(bufSize, alignof(SwitchValueInst));
857857
return ::new (buf) SwitchValueInst(Loc, Operand, DefaultBB, Cases, BBs);
858858
}
859859

@@ -901,7 +901,7 @@ SelectValueInst::create(SILDebugLocation *Loc, SILValue Operand, SILType Type,
901901

902902
size_t bufSize = sizeof(SelectValueInst) + decltype(Operands)::getExtraSize(
903903
CaseValuesAndResults.size());
904-
void *buf = F.getModule().allocate(bufSize, alignof(SelectValueInst));
904+
void *buf = F.getModule().allocateInst(bufSize, alignof(SelectValueInst));
905905
return ::new (buf)
906906
SelectValueInst(Loc, Operand, Type, DefaultResult, CaseValuesAndResults);
907907
}
@@ -942,7 +942,7 @@ SELECT_ENUM_INST *SelectEnumInstBase::createSelectEnum(
942942
// and `CaseBBs.size() + (DefaultBB ? 1 : 0)` values.
943943
unsigned numCases = CaseValues.size();
944944

945-
void *buf = F.getModule().allocate(
945+
void *buf = F.getModule().allocateInst(
946946
sizeof(SELECT_ENUM_INST) + sizeof(EnumElementDecl*) * numCases
947947
+ TailAllocatedOperandList<1>::getExtraSize(numCases + (bool)DefaultValue),
948948
alignof(SELECT_ENUM_INST));
@@ -1063,7 +1063,7 @@ SWITCH_ENUM_INST *SwitchEnumInstBase::createSwitchEnum(
10631063
unsigned numCases = CaseBBs.size();
10641064
unsigned numSuccessors = numCases + (DefaultBB ? 1 : 0);
10651065

1066-
void *buf = F.getModule().allocate(sizeof(SWITCH_ENUM_INST)
1066+
void *buf = F.getModule().allocateInst(sizeof(SWITCH_ENUM_INST)
10671067
+ sizeof(EnumElementDecl*) * numCases
10681068
+ sizeof(SILSuccessor) * numSuccessors,
10691069
alignof(SWITCH_ENUM_INST));
@@ -1129,8 +1129,8 @@ DynamicMethodBranchInst *
11291129
DynamicMethodBranchInst::create(SILDebugLocation *Loc, SILValue Operand,
11301130
SILDeclRef Member, SILBasicBlock *HasMethodBB,
11311131
SILBasicBlock *NoMethodBB, SILFunction &F) {
1132-
void *Buffer = F.getModule().allocate(sizeof(DynamicMethodBranchInst),
1133-
alignof(DynamicMethodBranchInst));
1132+
void *Buffer = F.getModule().allocateInst(sizeof(DynamicMethodBranchInst),
1133+
alignof(DynamicMethodBranchInst));
11341134
return ::new (Buffer)
11351135
DynamicMethodBranchInst(Loc, Operand, Member, HasMethodBB, NoMethodBB);
11361136
}
@@ -1172,7 +1172,7 @@ WitnessMethodInst::create(SILDebugLocation *Loc, CanType LookupType,
11721172
SILValue OpenedExistential, bool Volatile) {
11731173
SILModule &Mod = F->getModule();
11741174
void *Buffer =
1175-
Mod.allocate(sizeof(WitnessMethodInst), alignof(WitnessMethodInst));
1175+
Mod.allocateInst(sizeof(WitnessMethodInst), alignof(WitnessMethodInst));
11761176

11771177
declareWitnessTable(Mod, Conformance);
11781178
return ::new (Buffer) WitnessMethodInst(Loc, LookupType, Conformance, Member,
@@ -1184,8 +1184,8 @@ InitExistentialAddrInst *InitExistentialAddrInst::create(
11841184
SILType ConcreteLoweredType, ArrayRef<ProtocolConformance *> Conformances,
11851185
SILFunction *F) {
11861186
SILModule &Mod = F->getModule();
1187-
void *Buffer = Mod.allocate(sizeof(InitExistentialAddrInst),
1188-
alignof(InitExistentialAddrInst));
1187+
void *Buffer = Mod.allocateInst(sizeof(InitExistentialAddrInst),
1188+
alignof(InitExistentialAddrInst));
11891189
for (ProtocolConformance *C : Conformances)
11901190
declareWitnessTable(Mod, C);
11911191
return ::new (Buffer) InitExistentialAddrInst(Loc, Existential,
@@ -1200,8 +1200,8 @@ InitExistentialRefInst::create(SILDebugLocation *Loc, SILType ExistentialType,
12001200
ArrayRef<ProtocolConformance *> Conformances,
12011201
SILFunction *F) {
12021202
SILModule &Mod = F->getModule();
1203-
void *Buffer = Mod.allocate(sizeof(InitExistentialRefInst),
1204-
alignof(InitExistentialRefInst));
1203+
void *Buffer = Mod.allocateInst(sizeof(InitExistentialRefInst),
1204+
alignof(InitExistentialRefInst));
12051205
for (ProtocolConformance *C : Conformances) {
12061206
if (!C)
12071207
continue;
@@ -1235,7 +1235,7 @@ InitExistentialMetatypeInst *InitExistentialMetatypeInst::create(
12351235
unsigned size = sizeof(InitExistentialMetatypeInst);
12361236
size += conformances.size() * sizeof(ProtocolConformance *);
12371237

1238-
void *buffer = M.allocate(size, alignof(InitExistentialMetatypeInst));
1238+
void *buffer = M.allocateInst(size, alignof(InitExistentialMetatypeInst));
12391239
for (ProtocolConformance *conformance : conformances)
12401240
if (!M.lookUpWitnessTable(conformance, false).first)
12411241
declareWitnessTable(M, conformance);

Diff for: lib/SIL/SILModule.cpp

+15
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,21 @@ SILModule::~SILModule() {
124124
delete (SILTypeListUniquingType*)TypeListUniquing;
125125
}
126126

127+
void *SILModule::allocate(unsigned Size, unsigned Align) const {
128+
if (getASTContext().LangOpts.UseMalloc)
129+
return AlignedAlloc(Size, Align);
130+
131+
return BPA.Allocate(Size, Align);
132+
}
133+
134+
void *SILModule::allocateInst(unsigned Size, unsigned Align) const {
135+
return AlignedAlloc(Size, Align);
136+
}
137+
138+
void SILModule::deallocateInst(SILInstruction *I) {
139+
AlignedFree(I);
140+
}
141+
127142
SILWitnessTable *
128143
SILModule::createWitnessTableDeclaration(ProtocolConformance *C,
129144
SILLinkage linkage) {

0 commit comments

Comments
 (0)