From 27b4f730aab923f3acd569791a4e159ef97d9f47 Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Sun, 27 Oct 2013 15:36:14 +0100 Subject: [PATCH 1/5] gc2stack: Remove unused safeToDelete mechanism. --- gen/passes/GarbageCollect2Stack.cpp | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/gen/passes/GarbageCollect2Stack.cpp b/gen/passes/GarbageCollect2Stack.cpp index 2e006287..55fcbd7c 100644 --- a/gen/passes/GarbageCollect2Stack.cpp +++ b/gen/passes/GarbageCollect2Stack.cpp @@ -109,7 +109,6 @@ namespace { public: unsigned TypeInfoArgNr; - bool SafeToDelete; /// Whether the allocated memory is returned as a D array instead of /// just a plain pointer. @@ -134,10 +133,8 @@ namespace { return new AllocaInst(Ty, ".nongc_mem", Begin); // FIXME: align? } - FunctionInfo(unsigned typeInfoArgNr, bool safeToDelete, bool returnsArray) - : TypeInfoArgNr(typeInfoArgNr), - SafeToDelete(safeToDelete), - ReturnsArray(returnsArray) {} + FunctionInfo(unsigned typeInfoArgNr, bool returnsArray) + : TypeInfoArgNr(typeInfoArgNr), ReturnsArray(returnsArray) {} virtual ~FunctionInfo() {} }; @@ -147,9 +144,9 @@ namespace { bool Initialized; public: - ArrayFI(unsigned tiArgNr, bool safeToDelete, bool returnsArray, + ArrayFI(unsigned tiArgNr, bool returnsArray, bool initialized, unsigned arrSizeArgNr) - : FunctionInfo(tiArgNr, safeToDelete, returnsArray), + : FunctionInfo(tiArgNr, returnsArray), ArrSizeArgNr(arrSizeArgNr), Initialized(initialized) {} @@ -288,7 +285,7 @@ namespace { // The default promote() should be fine. - AllocClassFI() : FunctionInfo(~0u, true, false) {} + AllocClassFI() : FunctionInfo(~0u, false) {} }; } @@ -420,7 +417,7 @@ bool GarbageCollect2Stack::runOnFunction(Function &F) { FunctionInfo* info = OMI->getValue(); - if (Inst->use_empty() && info->SafeToDelete) { + if (Inst->use_empty()) { Changed = true; NumDeleted++; RemoveCall(CS, A); From e1a6d8144b085c8b53354e53e43f5beb8d0d5822 Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Sun, 27 Oct 2013 15:43:12 +0100 Subject: [PATCH 2/5] gc2stack: Replace return type bool with proper enum flag. This makes the code much easier to read. --- gen/passes/GarbageCollect2Stack.cpp | 32 ++++++++++++++++------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/gen/passes/GarbageCollect2Stack.cpp b/gen/passes/GarbageCollect2Stack.cpp index 55fcbd7c..73352d2f 100644 --- a/gen/passes/GarbageCollect2Stack.cpp +++ b/gen/passes/GarbageCollect2Stack.cpp @@ -103,16 +103,20 @@ static void EmitMemZero(IRBuilder<>& B, Value* Dst, Value* Len, //===----------------------------------------------------------------------===// namespace { + namespace ReturnType { + enum Type { + Pointer, /// Function returns a pointer to the allocated memory. + Array /// Function returns the allocated memory as an array slice. + }; + } + class FunctionInfo { protected: Type* Ty; public: unsigned TypeInfoArgNr; - - /// Whether the allocated memory is returned as a D array instead of - /// just a plain pointer. - bool ReturnsArray; + ReturnType::Type ReturnType; // Analyze the current call, filling in some fields. Returns true if // this is an allocation we can stack-allocate. @@ -133,8 +137,8 @@ namespace { return new AllocaInst(Ty, ".nongc_mem", Begin); // FIXME: align? } - FunctionInfo(unsigned typeInfoArgNr, bool returnsArray) - : TypeInfoArgNr(typeInfoArgNr), ReturnsArray(returnsArray) {} + FunctionInfo(unsigned typeInfoArgNr, ReturnType::Type returnType) + : TypeInfoArgNr(typeInfoArgNr), ReturnType(returnType) {} virtual ~FunctionInfo() {} }; @@ -144,9 +148,9 @@ namespace { bool Initialized; public: - ArrayFI(unsigned tiArgNr, bool returnsArray, + ArrayFI(unsigned tiArgNr, ReturnType::Type returnType, bool initialized, unsigned arrSizeArgNr) - : FunctionInfo(tiArgNr, returnsArray), + : FunctionInfo(tiArgNr, returnType), ArrSizeArgNr(arrSizeArgNr), Initialized(initialized) {} @@ -228,7 +232,7 @@ namespace { EmitMemZero(B, alloca, Size, A); } - if (ReturnsArray) { + if (ReturnType == ReturnType::Array) { Value* arrStruct = llvm::UndefValue::get(CS.getType()); arrStruct = Builder.CreateInsertValue(arrStruct, arrSize, 0); Value* memPtr = Builder.CreateBitCast(alloca, @@ -285,7 +289,7 @@ namespace { // The default promote() should be fine. - AllocClassFI() : FunctionInfo(~0u, false) {} + AllocClassFI() : FunctionInfo(~0u, ReturnType::Pointer) {} }; } @@ -341,9 +345,9 @@ FunctionPass *createGarbageCollect2Stack() { GarbageCollect2Stack::GarbageCollect2Stack() : FunctionPass(ID), - AllocMemoryT(0, true, false), - NewArrayVT(0, true, true, false, 1), - NewArrayT(0, true, true, true, 1) + AllocMemoryT(0, ReturnType::Pointer), + NewArrayVT(0, ReturnType::Array, false, 1), + NewArrayT(0, ReturnType::Array, true, 1) { KnownFunctions["_d_allocmemoryT"] = &AllocMemoryT; KnownFunctions["_d_newarrayvT"] = &NewArrayVT; @@ -430,7 +434,7 @@ bool GarbageCollect2Stack::runOnFunction(Function &F) { continue; SmallVector RemoveTailCallInsts; - if (info->ReturnsArray) { + if (info->ReturnType == ReturnType::Array) { if (!isSafeToStackAllocateArray(Inst, DT, RemoveTailCallInsts)) continue; } else { if (!isSafeToStackAllocate(Inst, Inst, DT, RemoveTailCallInsts)) continue; From 5b9208ef85a063f8aac97108ff669a240a97f9ab Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Sun, 27 Oct 2013 16:02:33 +0100 Subject: [PATCH 3/5] gc2stack: Extract static size checking code into helper function. --- gen/passes/GarbageCollect2Stack.cpp | 48 ++++++++++++++++------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/gen/passes/GarbageCollect2Stack.cpp b/gen/passes/GarbageCollect2Stack.cpp index 73352d2f..fd6ac7ee 100644 --- a/gen/passes/GarbageCollect2Stack.cpp +++ b/gen/passes/GarbageCollect2Stack.cpp @@ -142,6 +142,31 @@ namespace { virtual ~FunctionInfo() {} }; + static bool isKnownLessThan(Value* Val, uint64_t Limit, const Analysis& A) { + unsigned BitsLimit = Log2_64(Limit); + + // LLVM's alloca ueses an i32 for the number of elements. + BitsLimit = std::min(BitsLimit, 32U); + + const IntegerType* SizeType = + dyn_cast(Val->getType()); + if (!SizeType) + return false; + unsigned Bits = SizeType->getBitWidth(); + + if (Bits > BitsLimit) { + APInt Mask = APInt::getLowBitsSet(Bits, BitsLimit); + Mask.flipAllBits(); + APInt KnownZero(Bits, 0), KnownOne(Bits, 0); + ComputeMaskedBits(Val, KnownZero, KnownOne, &A.TD); + + if ((KnownZero & Mask) != Mask) + return false; + } + + return true; + } + class ArrayFI : public FunctionInfo { Value* arrSize; int ArrSizeArgNr; @@ -174,29 +199,10 @@ namespace { // miscompilations for humongous arrays, but as the value "range" // (set bits) inference algorithm is rather limited, this is // useful for experimenting. - if (SizeLimit > 0) - { + if (SizeLimit > 0) { uint64_t ElemSize = A.TD.getTypeAllocSize(Ty); - unsigned BitsLimit = Log2_64(SizeLimit / ElemSize); - - // LLVM's alloca ueses an i32 for the number of elements. - BitsLimit = std::min(BitsLimit, 32U); - - const IntegerType* SizeType = - dyn_cast(arrSize->getType()); - if (!SizeType) + if (!isKnownLessThan(arrSize, SizeLimit / ElemSize, A)) return false; - unsigned Bits = SizeType->getBitWidth(); - - if (Bits > BitsLimit) { - APInt Mask = APInt::getLowBitsSet(Bits, BitsLimit); - Mask.flipAllBits(); - APInt KnownZero(Bits, 0), KnownOne(Bits, 0); - ComputeMaskedBits(arrSize, KnownZero, KnownOne, &A.TD); - - if ((KnownZero & Mask) != Mask) - return false; - } } return true; From 3539e201f81c7409f83ce8f562d2c0844d1e33da Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Sun, 27 Oct 2013 16:17:21 +0100 Subject: [PATCH 4/5] gc2stack: Move TypeInfo handling code into FunctionInfo subclass. This is to properly support calls that don't involve TypeInfo at all, like it is already the case for _d_newclass, and will be for _d_allocmemory. --- gen/passes/GarbageCollect2Stack.cpp | 57 ++++++++++++++++------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/gen/passes/GarbageCollect2Stack.cpp b/gen/passes/GarbageCollect2Stack.cpp index fd6ac7ee..45b5dd29 100644 --- a/gen/passes/GarbageCollect2Stack.cpp +++ b/gen/passes/GarbageCollect2Stack.cpp @@ -115,18 +115,11 @@ namespace { Type* Ty; public: - unsigned TypeInfoArgNr; ReturnType::Type ReturnType; // Analyze the current call, filling in some fields. Returns true if // this is an allocation we can stack-allocate. - virtual bool analyze(CallSite CS, const Analysis& A) { - Value* TypeInfo = CS.getArgument(TypeInfoArgNr); - Ty = A.getTypeFor(TypeInfo); - if (!Ty) return false; - - return A.TD.getTypeAllocSize(Ty) < SizeLimit; - } + virtual bool analyze(CallSite CS, const Analysis& A) = 0; // Returns the alloca to replace this call. // It will always be inserted before the call. @@ -137,8 +130,8 @@ namespace { return new AllocaInst(Ty, ".nongc_mem", Begin); // FIXME: align? } - FunctionInfo(unsigned typeInfoArgNr, ReturnType::Type returnType) - : TypeInfoArgNr(typeInfoArgNr), ReturnType(returnType) {} + FunctionInfo(ReturnType::Type returnType) + : ReturnType(returnType) {} virtual ~FunctionInfo() {} }; @@ -167,22 +160,36 @@ namespace { return true; } - class ArrayFI : public FunctionInfo { - Value* arrSize; - int ArrSizeArgNr; - bool Initialized; + class TypeInfoFI : public FunctionInfo { + unsigned TypeInfoArgNr; public: - ArrayFI(unsigned tiArgNr, ReturnType::Type returnType, - bool initialized, unsigned arrSizeArgNr) - : FunctionInfo(tiArgNr, returnType), + TypeInfoFI(ReturnType::Type returnType, unsigned tiArgNr) + : FunctionInfo(returnType), TypeInfoArgNr(tiArgNr) {} + + virtual bool analyze(CallSite CS, const Analysis& A) { + Value* TypeInfo = CS.getArgument(TypeInfoArgNr); + Ty = A.getTypeFor(TypeInfo); + if (!Ty) return false; + return A.TD.getTypeAllocSize(Ty) < SizeLimit; + } + }; + + class ArrayFI : public TypeInfoFI { + int ArrSizeArgNr; + bool Initialized; + Value* arrSize; + + public: + ArrayFI(ReturnType::Type returnType, unsigned tiArgNr, + unsigned arrSizeArgNr, bool initialized) + : TypeInfoFI(returnType, tiArgNr), ArrSizeArgNr(arrSizeArgNr), Initialized(initialized) {} virtual bool analyze(CallSite CS, const Analysis& A) { - if (!FunctionInfo::analyze(CS, A)) - return false; + if (!TypeInfoFI::analyze(CS, A)) return false; arrSize = CS.getArgument(ArrSizeArgNr); @@ -255,8 +262,6 @@ namespace { class AllocClassFI : public FunctionInfo { public: virtual bool analyze(CallSite CS, const Analysis& A) { - // This call contains no TypeInfo parameter, so don't call the - // base class implementation here... if (CS.arg_size() != 1) return false; Value* arg = CS.getArgument(0)->stripPointerCasts(); @@ -295,7 +300,7 @@ namespace { // The default promote() should be fine. - AllocClassFI() : FunctionInfo(~0u, ReturnType::Pointer) {} + AllocClassFI() : FunctionInfo(ReturnType::Pointer) {} }; } @@ -311,7 +316,7 @@ namespace { StringMap KnownFunctions; Module* M; - FunctionInfo AllocMemoryT; + TypeInfoFI AllocMemoryT; ArrayFI NewArrayVT; ArrayFI NewArrayT; AllocClassFI AllocClass; @@ -351,9 +356,9 @@ FunctionPass *createGarbageCollect2Stack() { GarbageCollect2Stack::GarbageCollect2Stack() : FunctionPass(ID), - AllocMemoryT(0, ReturnType::Pointer), - NewArrayVT(0, ReturnType::Array, false, 1), - NewArrayT(0, ReturnType::Array, true, 1) + AllocMemoryT(ReturnType::Pointer, 0), + NewArrayVT(ReturnType::Array, 0, 1, false), + NewArrayT(ReturnType::Array, 0, 1, true) { KnownFunctions["_d_allocmemoryT"] = &AllocMemoryT; KnownFunctions["_d_newarrayvT"] = &NewArrayVT; From 9f1c26b52a310266bdaa6bc916eb7806ea1a1b33 Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Sun, 27 Oct 2013 16:42:04 +0100 Subject: [PATCH 5/5] gc2stack: Handle _d_allocmemory. This allows us to clean up after inlining closures. --- gen/passes/GarbageCollect2Stack.cpp | 61 ++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/gen/passes/GarbageCollect2Stack.cpp b/gen/passes/GarbageCollect2Stack.cpp index 45b5dd29..901f1dc2 100644 --- a/gen/passes/GarbageCollect2Stack.cpp +++ b/gen/passes/GarbageCollect2Stack.cpp @@ -302,6 +302,62 @@ namespace { AllocClassFI() : FunctionInfo(ReturnType::Pointer) {} }; + + /// Describes runtime functions that allocate a chunk of memory with a + /// given size. + class UntypedMemoryFI : public FunctionInfo { + unsigned SizeArgNr; + Value* SizeArg; + public: + virtual bool analyze(CallSite CS, const Analysis& A) { + if (CS.arg_size() < SizeArgNr + 1) + return false; + + SizeArg = CS.getArgument(SizeArgNr); + + // If the user explicitly disabled the limits, don't even check + // whether the allocated size fits in 32 bits. This could cause + // miscompilations for humongous allocations, but as the value + // "range" (set bits) inference algorithm is rather limited, this + // is useful for experimenting. + if (SizeLimit > 0) { + if (!isKnownLessThan(SizeArg, SizeLimit, A)) + return false; + } + + // Should be i8. + Ty = CS.getType()->getContainedType(0); + return true; + } + + virtual Value* promote(CallSite CS, IRBuilder<>& B, const Analysis& A) { + IRBuilder<> Builder = B; + // If the allocation is of constant size it's best to put it in the + // entry block, so do so if we're not already there. + // For dynamically-sized allocations it's best to avoid the overhead + // of allocating them if possible, so leave those where they are. + // While we're at it, update statistics too. + if (isa(SizeArg)) { + BasicBlock& Entry = CS.getCaller()->getEntryBlock(); + if (Builder.GetInsertBlock() != &Entry) + Builder.SetInsertPoint(&Entry, Entry.begin()); + NumGcToStack++; + } else { + NumToDynSize++; + } + + // Convert array size to 32 bits if necessary + Value* count = Builder.CreateIntCast(SizeArg, Builder.getInt32Ty(), false); + AllocaInst* alloca = Builder.CreateAlloca(Ty, count, ".nongc_mem"); // FIXME: align? + + return Builder.CreateBitCast(alloca, CS.getType()); + } + + UntypedMemoryFI(unsigned sizeArgNr) + : FunctionInfo(ReturnType::Pointer), + SizeArgNr(sizeArgNr) + {} + }; } @@ -320,6 +376,7 @@ namespace { ArrayFI NewArrayVT; ArrayFI NewArrayT; AllocClassFI AllocClass; + UntypedMemoryFI AllocMemory; public: static char ID; // Pass identification @@ -358,12 +415,14 @@ GarbageCollect2Stack::GarbageCollect2Stack() : FunctionPass(ID), AllocMemoryT(ReturnType::Pointer, 0), NewArrayVT(ReturnType::Array, 0, 1, false), - NewArrayT(ReturnType::Array, 0, 1, true) + NewArrayT(ReturnType::Array, 0, 1, true), + AllocMemory(0) { KnownFunctions["_d_allocmemoryT"] = &AllocMemoryT; KnownFunctions["_d_newarrayvT"] = &NewArrayVT; KnownFunctions["_d_newarrayT"] = &NewArrayT; KnownFunctions["_d_newclass"] = &AllocClass; + KnownFunctions["_d_allocmemory"] = &AllocMemory; } static void RemoveCall(CallSite CS, const Analysis& A) {