Skip to content

Commit

Permalink
Merge pull request ldc-developers#532 from klickverbot/gc2stack-fixes
Browse files Browse the repository at this point in the history
gc2stack: Cleanup, handle _d_allocmemory.
  • Loading branch information
redstar committed Oct 28, 2013
2 parents 5bf42f8 + 9f1c26b commit c03ed8e
Showing 1 changed file with 126 additions and 55 deletions.
181 changes: 126 additions & 55 deletions gen/passes/GarbageCollect2Stack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,27 +103,23 @@ 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;
bool SafeToDelete;

/// 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.
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.
Expand All @@ -134,29 +130,66 @@ namespace {
return new AllocaInst(Ty, ".nongc_mem", Begin); // FIXME: align?
}

FunctionInfo(unsigned typeInfoArgNr, bool safeToDelete, bool returnsArray)
: TypeInfoArgNr(typeInfoArgNr),
SafeToDelete(safeToDelete),
ReturnsArray(returnsArray) {}
FunctionInfo(ReturnType::Type returnType)
: ReturnType(returnType) {}
virtual ~FunctionInfo() {}
};

class ArrayFI : public FunctionInfo {
Value* arrSize;
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<IntegerType>(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 TypeInfoFI : public FunctionInfo {
unsigned TypeInfoArgNr;

public:
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(unsigned tiArgNr, bool safeToDelete, bool returnsArray,
bool initialized, unsigned arrSizeArgNr)
: FunctionInfo(tiArgNr, safeToDelete, returnsArray),
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);

Expand All @@ -173,29 +206,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<IntegerType>(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;
Expand Down Expand Up @@ -231,7 +245,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,
Expand All @@ -248,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();
Expand Down Expand Up @@ -288,7 +300,63 @@ namespace {

// The default promote() should be fine.

AllocClassFI() : FunctionInfo(~0u, true, false) {}
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<Constant>(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)
{}
};
}

Expand All @@ -304,10 +372,11 @@ namespace {
StringMap<FunctionInfo*> KnownFunctions;
Module* M;

FunctionInfo AllocMemoryT;
TypeInfoFI AllocMemoryT;
ArrayFI NewArrayVT;
ArrayFI NewArrayT;
AllocClassFI AllocClass;
UntypedMemoryFI AllocMemory;

public:
static char ID; // Pass identification
Expand Down Expand Up @@ -344,14 +413,16 @@ FunctionPass *createGarbageCollect2Stack() {

GarbageCollect2Stack::GarbageCollect2Stack()
: FunctionPass(ID),
AllocMemoryT(0, true, false),
NewArrayVT(0, true, true, false, 1),
NewArrayT(0, true, true, true, 1)
AllocMemoryT(ReturnType::Pointer, 0),
NewArrayVT(ReturnType::Array, 0, 1, false),
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) {
Expand Down Expand Up @@ -420,7 +491,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);
Expand All @@ -433,7 +504,7 @@ bool GarbageCollect2Stack::runOnFunction(Function &F) {
continue;

SmallVector<CallInst*, 4> RemoveTailCallInsts;
if (info->ReturnsArray) {
if (info->ReturnType == ReturnType::Array) {
if (!isSafeToStackAllocateArray(Inst, DT, RemoveTailCallInsts)) continue;
} else {
if (!isSafeToStackAllocate(Inst, Inst, DT, RemoveTailCallInsts)) continue;
Expand Down

0 comments on commit c03ed8e

Please sign in to comment.