From bafb0e9d968049b70467e8a95c5816ebd294ca66 Mon Sep 17 00:00:00 2001 From: Roman Levenstein Date: Wed, 18 Nov 2015 10:47:00 -0800 Subject: [PATCH] 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 --- include/swift/SIL/SILInstruction.h | 7 +++++ include/swift/SIL/SILModule.h | 10 +++---- lib/SIL/SILFunction.cpp | 4 +-- lib/SIL/SILInstruction.cpp | 11 ++++++- lib/SIL/SILInstructions.cpp | 48 +++++++++++++++--------------- lib/SIL/SILModule.cpp | 11 +++++++ 6 files changed, 59 insertions(+), 32 deletions(-) diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 265e8a5b17bf4..7dd39e7e28c23 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -88,6 +88,13 @@ class SILInstruction : public ValueBase,public llvm::ilist_node{ : ValueBase(Kind, TypeList), ParentBB(0), Location(*DebugLoc) {} public: + /// Instructions should be allocated using a dedicated instruction allocation + /// function from the ContextTy. + template + void *operator new(size_t Bytes, const ContextTy &C, + size_t Alignment = alignof(ValueBase)) { + return C.allocateInst(Bytes, Alignment); + } enum class MemoryBehavior { None, diff --git a/include/swift/SIL/SILModule.h b/include/swift/SIL/SILModule.h index 7f12cddb01efd..b2beed7102ebe 100644 --- a/include/swift/SIL/SILModule.h +++ b/include/swift/SIL/SILModule.h @@ -499,12 +499,12 @@ class SILModule { bool PrintASTDecls = true) const; /// Allocate memory using the module's internal allocator. - void *allocate(unsigned Size, unsigned Align) const { - if (getASTContext().LangOpts.UseMalloc) - return AlignedAlloc(Size, Align); + void *allocate(unsigned Size, unsigned Align) const; + + /// Allocate memory for an instruction using the module's internal allocator. + void *allocateInst(unsigned Size, unsigned Align) const; + - return BPA.Allocate(Size, Align); - } /// \brief Looks up the llvm intrinsic ID and type for the builtin function. /// diff --git a/lib/SIL/SILFunction.cpp b/lib/SIL/SILFunction.cpp index 2a5ed7b3fb074..00c13c63e134d 100644 --- a/lib/SIL/SILFunction.cpp +++ b/lib/SIL/SILFunction.cpp @@ -102,14 +102,14 @@ SILFunction::SILFunction(SILModule &Module, SILLinkage Linkage, } SILFunction::~SILFunction() { -#ifndef NDEBUG // If the function is recursive, a function_ref inst inside of the function // will give the function a non-zero ref count triggering the assertion. Thus // we drop all instruction references before we erase. + // We also need to drop all references if instructions are allocated using + // an allocator that may recycle freed memory. dropAllReferences(); assert(RefCount == 0 && "Function cannot be deleted while function_ref's still exist"); -#endif } void SILFunction::setDeclContext(Decl *D) { diff --git a/lib/SIL/SILInstruction.cpp b/lib/SIL/SILInstruction.cpp index ee84b82b556bd..c812e1d69cad6 100644 --- a/lib/SIL/SILInstruction.cpp +++ b/lib/SIL/SILInstruction.cpp @@ -176,10 +176,19 @@ void SILInstruction::replaceAllUsesWithUndef() { } } +namespace swift { + void deallocate(void *Ptr) { + AlignedFree(Ptr); + } +} + namespace { class InstructionDestroyer : public SILVisitor { public: -#define VALUE(CLASS, PARENT) void visit##CLASS(CLASS *I) { I->~CLASS(); } +#define VALUE(CLASS, PARENT) void visit##CLASS(CLASS *I) { \ + I->~CLASS();\ + deallocate(I);\ + } #include "swift/SIL/SILNodes.def" }; } // end anonymous namespace diff --git a/lib/SIL/SILInstructions.cpp b/lib/SIL/SILInstructions.cpp index bcf7aa93e4bc7..70b6e3a68facd 100644 --- a/lib/SIL/SILInstructions.cpp +++ b/lib/SIL/SILInstructions.cpp @@ -126,8 +126,8 @@ AllocExistentialBoxInst *AllocExistentialBoxInst::create( SILType ConcreteLoweredType, ArrayRef Conformances, SILFunction *F) { SILModule &Mod = F->getModule(); - void *Buffer = Mod.allocate(sizeof(AllocExistentialBoxInst), - alignof(AllocExistentialBoxInst)); + void *Buffer = Mod.allocateInst(sizeof(AllocExistentialBoxInst), + alignof(AllocExistentialBoxInst)); for (ProtocolConformance *C : Conformances) declareWitnessTable(Mod, C); return ::new (Buffer) AllocExistentialBoxInst(Loc, @@ -142,7 +142,7 @@ BuiltinInst *BuiltinInst::create(SILDebugLocation *Loc, Identifier Name, ArrayRef Substitutions, ArrayRef Args, SILFunction &F) { - void *Buffer = F.getModule().allocate( + void *Buffer = F.getModule().allocateInst( sizeof(BuiltinInst) + decltype(Operands)::getExtraSize(Args.size()) + sizeof(Substitution) * Substitutions.size(), @@ -189,7 +189,7 @@ bool swift::doesApplyCalleeHaveSemantics(SILValue callee, StringRef semantics) { } void *swift::allocateApplyInst(SILFunction &F, size_t size, size_t alignment) { - return F.getModule().allocate(size, alignment); + return F.getModule().allocateInst(size, alignment); } PartialApplyInst::PartialApplyInst(SILDebugLocation *Loc, SILValue Callee, @@ -275,14 +275,14 @@ static unsigned getWordsForBitWidth(unsigned bits) { template static void *allocateLiteralInstWithTextSize(SILFunction &F, unsigned length) { - return F.getModule().allocate(sizeof(INST) + length, alignof(INST)); + return F.getModule().allocateInst(sizeof(INST) + length, alignof(INST)); } template static void *allocateLiteralInstWithBitSize(SILFunction &F, unsigned bits) { unsigned words = getWordsForBitWidth(bits); - return F.getModule().allocate(sizeof(INST) + sizeof(llvm::integerPart)*words, - alignof(INST)); + return F.getModule().allocateInst( + sizeof(INST) + sizeof(llvm::integerPart)*words, alignof(INST)); } IntegerLiteralInst::IntegerLiteralInst(SILDebugLocation *Loc, SILType Ty, @@ -408,7 +408,7 @@ AssignInst::AssignInst(SILDebugLocation *Loc, SILValue Src, SILValue Dest) MarkFunctionEscapeInst * MarkFunctionEscapeInst::create(SILDebugLocation *Loc, ArrayRef Elements, SILFunction &F) { - void *Buffer = F.getModule().allocate(sizeof(MarkFunctionEscapeInst) + + void *Buffer = F.getModule().allocateInst(sizeof(MarkFunctionEscapeInst) + decltype(Operands)::getExtraSize(Elements.size()), alignof(MarkFunctionEscapeInst)); return ::new(Buffer) MarkFunctionEscapeInst(Loc, Elements); @@ -456,7 +456,7 @@ UnconditionalCheckedCastAddrInst::UnconditionalCheckedCastAddrInst( StructInst *StructInst::create(SILDebugLocation *Loc, SILType Ty, ArrayRef Elements, SILFunction &F) { - void *Buffer = F.getModule().allocate(sizeof(StructInst) + + void *Buffer = F.getModule().allocateInst(sizeof(StructInst) + decltype(Operands)::getExtraSize(Elements.size()), alignof(StructInst)); return ::new(Buffer) StructInst(Loc, Ty, Elements); @@ -470,7 +470,7 @@ StructInst::StructInst(SILDebugLocation *Loc, SILType Ty, TupleInst *TupleInst::create(SILDebugLocation *Loc, SILType Ty, ArrayRef Elements, SILFunction &F) { - void *Buffer = F.getModule().allocate(sizeof(TupleInst) + + void *Buffer = F.getModule().allocateInst(sizeof(TupleInst) + decltype(Operands)::getExtraSize(Elements.size()), alignof(TupleInst)); return ::new(Buffer) TupleInst(Loc, Ty, Elements); @@ -673,7 +673,7 @@ BranchInst *BranchInst::create(SILDebugLocation *Loc, SILBasicBlock *DestBB, BranchInst *BranchInst::create(SILDebugLocation *Loc, SILBasicBlock *DestBB, ArrayRef Args, SILFunction &F) { - void *Buffer = F.getModule().allocate(sizeof(BranchInst) + + void *Buffer = F.getModule().allocateInst(sizeof(BranchInst) + decltype(Operands)::getExtraSize(Args.size()), alignof(BranchInst)); return ::new (Buffer) BranchInst(Loc, DestBB, Args); @@ -707,7 +707,7 @@ CondBranchInst::create(SILDebugLocation *Loc, SILValue Condition, Args.append(TrueArgs.begin(), TrueArgs.end()); Args.append(FalseArgs.begin(), FalseArgs.end()); - void *Buffer = F.getModule().allocate(sizeof(CondBranchInst) + + void *Buffer = F.getModule().allocateInst(sizeof(CondBranchInst) + decltype(Operands)::getExtraSize(Args.size()), alignof(CondBranchInst)); return ::new (Buffer) CondBranchInst(Loc, Condition, TrueBB, FalseBB, Args, @@ -857,7 +857,7 @@ SwitchValueInst *SwitchValueInst::create( size_t bufSize = sizeof(SwitchValueInst) + decltype(Operands)::getExtraSize(Cases.size()) + sizeof(SILSuccessor) * numSuccessors; - void *buf = F.getModule().allocate(bufSize, alignof(SwitchValueInst)); + void *buf = F.getModule().allocateInst(bufSize, alignof(SwitchValueInst)); return ::new (buf) SwitchValueInst(Loc, Operand, DefaultBB, Cases, BBs); } @@ -905,7 +905,7 @@ SelectValueInst::create(SILDebugLocation *Loc, SILValue Operand, SILType Type, size_t bufSize = sizeof(SelectValueInst) + decltype(Operands)::getExtraSize( CaseValuesAndResults.size()); - void *buf = F.getModule().allocate(bufSize, alignof(SelectValueInst)); + void *buf = F.getModule().allocateInst(bufSize, alignof(SelectValueInst)); return ::new (buf) SelectValueInst(Loc, Operand, Type, DefaultResult, CaseValuesAndResults); } @@ -946,7 +946,7 @@ SELECT_ENUM_INST *SelectEnumInstBase::createSelectEnum( // and `CaseBBs.size() + (DefaultBB ? 1 : 0)` values. unsigned numCases = CaseValues.size(); - void *buf = F.getModule().allocate( + void *buf = F.getModule().allocateInst( sizeof(SELECT_ENUM_INST) + sizeof(EnumElementDecl*) * numCases + TailAllocatedOperandList<1>::getExtraSize(numCases + (bool)DefaultValue), alignof(SELECT_ENUM_INST)); @@ -1067,7 +1067,7 @@ SWITCH_ENUM_INST *SwitchEnumInstBase::createSwitchEnum( unsigned numCases = CaseBBs.size(); unsigned numSuccessors = numCases + (DefaultBB ? 1 : 0); - void *buf = F.getModule().allocate(sizeof(SWITCH_ENUM_INST) + void *buf = F.getModule().allocateInst(sizeof(SWITCH_ENUM_INST) + sizeof(EnumElementDecl*) * numCases + sizeof(SILSuccessor) * numSuccessors, alignof(SWITCH_ENUM_INST)); @@ -1133,8 +1133,8 @@ DynamicMethodBranchInst * DynamicMethodBranchInst::create(SILDebugLocation *Loc, SILValue Operand, SILDeclRef Member, SILBasicBlock *HasMethodBB, SILBasicBlock *NoMethodBB, SILFunction &F) { - void *Buffer = F.getModule().allocate(sizeof(DynamicMethodBranchInst), - alignof(DynamicMethodBranchInst)); + void *Buffer = F.getModule().allocateInst(sizeof(DynamicMethodBranchInst), + alignof(DynamicMethodBranchInst)); return ::new (Buffer) DynamicMethodBranchInst(Loc, Operand, Member, HasMethodBB, NoMethodBB); } @@ -1176,7 +1176,7 @@ WitnessMethodInst::create(SILDebugLocation *Loc, CanType LookupType, SILValue OpenedExistential, bool Volatile) { SILModule &Mod = F->getModule(); void *Buffer = - Mod.allocate(sizeof(WitnessMethodInst), alignof(WitnessMethodInst)); + Mod.allocateInst(sizeof(WitnessMethodInst), alignof(WitnessMethodInst)); declareWitnessTable(Mod, Conformance); return ::new (Buffer) WitnessMethodInst(Loc, LookupType, Conformance, Member, @@ -1188,8 +1188,8 @@ InitExistentialAddrInst *InitExistentialAddrInst::create( SILType ConcreteLoweredType, ArrayRef Conformances, SILFunction *F) { SILModule &Mod = F->getModule(); - void *Buffer = Mod.allocate(sizeof(InitExistentialAddrInst), - alignof(InitExistentialAddrInst)); + void *Buffer = Mod.allocateInst(sizeof(InitExistentialAddrInst), + alignof(InitExistentialAddrInst)); for (ProtocolConformance *C : Conformances) declareWitnessTable(Mod, C); return ::new (Buffer) InitExistentialAddrInst(Loc, Existential, @@ -1204,8 +1204,8 @@ InitExistentialRefInst::create(SILDebugLocation *Loc, SILType ExistentialType, ArrayRef Conformances, SILFunction *F) { SILModule &Mod = F->getModule(); - void *Buffer = Mod.allocate(sizeof(InitExistentialRefInst), - alignof(InitExistentialRefInst)); + void *Buffer = Mod.allocateInst(sizeof(InitExistentialRefInst), + alignof(InitExistentialRefInst)); for (ProtocolConformance *C : Conformances) { if (!C) continue; @@ -1239,7 +1239,7 @@ InitExistentialMetatypeInst *InitExistentialMetatypeInst::create( unsigned size = sizeof(InitExistentialMetatypeInst); size += conformances.size() * sizeof(ProtocolConformance *); - void *buffer = M.allocate(size, alignof(InitExistentialMetatypeInst)); + void *buffer = M.allocateInst(size, alignof(InitExistentialMetatypeInst)); for (ProtocolConformance *conformance : conformances) if (!M.lookUpWitnessTable(conformance, false).first) declareWitnessTable(M, conformance); diff --git a/lib/SIL/SILModule.cpp b/lib/SIL/SILModule.cpp index ab0a535ed18ec..38092c6e57702 100644 --- a/lib/SIL/SILModule.cpp +++ b/lib/SIL/SILModule.cpp @@ -124,6 +124,17 @@ SILModule::~SILModule() { delete (SILTypeListUniquingType*)TypeListUniquing; } +void *SILModule::allocate(unsigned Size, unsigned Align) const { + if (getASTContext().LangOpts.UseMalloc) + return AlignedAlloc(Size, Align); + + return BPA.Allocate(Size, Align); +} + +void *SILModule::allocateInst(unsigned Size, unsigned Align) const { + return AlignedAlloc(Size, Align); +} + SILWitnessTable * SILModule::createWitnessTableDeclaration(ProtocolConformance *C, SILLinkage linkage) {