diff --git a/ddmd/declaration.d b/ddmd/declaration.d index c18574641b..2de5b04f70 100644 --- a/ddmd/declaration.d +++ b/ddmd/declaration.d @@ -904,6 +904,11 @@ extern (C++) class VarDeclaration : Declaration Expression edtor; // if !=null, does the destruction of the variable IntRange* range; // if !=null, the variable is known to be within the range + version (IN_LLVM) + { + TypeClass scopeClassType; // real (dynamic) type if onstack == true (stack-allocated class) + } + final extern (D) this(Loc loc, Type type, Identifier id, Initializer _init, StorageClass storage_class = STCundefined) { super(id); diff --git a/ddmd/declaration.h b/ddmd/declaration.h index 0957ef7ea8..dcef228958 100644 --- a/ddmd/declaration.h +++ b/ddmd/declaration.h @@ -268,6 +268,10 @@ public: Expression *edtor; // if !=NULL, does the destruction of the variable IntRange *range; // if !NULL, the variable is known to be within the range +#if IN_LLVM + TypeClass *scopeClassType; // real (dynamic) type if onstack == true (stack-allocated class) +#endif + Dsymbol *syntaxCopy(Dsymbol *); void setFieldOffset(AggregateDeclaration *ad, unsigned *poffset, bool isunion); const char *kind() const; diff --git a/ddmd/dsymbolsem.d b/ddmd/dsymbolsem.d index 8eaf14cdd7..e04ac22570 100644 --- a/ddmd/dsymbolsem.d +++ b/ddmd/dsymbolsem.d @@ -2546,6 +2546,10 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor { ne.onstack = 1; dsym.onstack = true; + version (IN_LLVM) + { + dsym.scopeClassType = cast(TypeClass) ne.newtype; + } } } } diff --git a/gen/classes.cpp b/gen/classes.cpp index 298b4a9a66..5aaa663192 100644 --- a/gen/classes.cpp +++ b/gen/classes.cpp @@ -21,6 +21,7 @@ #include "gen/llvmhelpers.h" #include "gen/logger.h" #include "gen/nested.h" +#include "gen/optimizer.h" #include "gen/rttibuilder.h" #include "gen/runtime.h" #include "gen/structs.h" @@ -193,6 +194,50 @@ void DtoFinalizeClass(Loc &loc, LLValue *inst) { //////////////////////////////////////////////////////////////////////////////// +void DtoFinalizeScopeClass(Loc &loc, LLValue *inst, ClassDeclaration *cd) { + if (!isOptimizationEnabled()) { + DtoFinalizeClass(loc, inst); + return; + } + + assert(cd); + // As of 2.077, the front-end doesn't emit the implicit delete() for C++ + // classes, so this code assumes D classes. + assert(!cd->isCPPclass()); + + bool hasDtor = false; + for (; cd; cd = cd->baseClass) { + if (cd->dtor) { + hasDtor = true; + break; + } + } + + if (hasDtor) { + DtoFinalizeClass(loc, inst); + return; + } + + // no dtors => only finalize (via druntime call) if monitor is set, + // see https://github.com/ldc-developers/ldc/issues/2515 + llvm::BasicBlock *ifbb = gIR->insertBB("if"); + llvm::BasicBlock *endbb = gIR->insertBBAfter(ifbb, "endif"); + + const auto monitor = DtoLoad(DtoGEPi(inst, 0, 1), ".monitor"); + const auto hasMonitor = + gIR->ir->CreateICmp(llvm::CmpInst::ICMP_NE, monitor, + getNullValue(monitor->getType()), ".hasMonitor"); + llvm::BranchInst::Create(ifbb, endbb, hasMonitor, gIR->scopebb()); + + gIR->scope() = IRScope(ifbb); + DtoFinalizeClass(loc, inst); + gIR->ir->CreateBr(endbb); + + gIR->scope() = IRScope(endbb); +} + +//////////////////////////////////////////////////////////////////////////////// + DValue *DtoCastClass(Loc &loc, DValue *val, Type *_to) { IF_LOG Logger::println("DtoCastClass(%s, %s)", val->type->toChars(), _to->toChars()); diff --git a/gen/classes.h b/gen/classes.h index 2583732d05..ca747114de 100644 --- a/gen/classes.h +++ b/gen/classes.h @@ -33,6 +33,7 @@ llvm::Constant *DtoDefineClassInfo(ClassDeclaration *cd); DValue *DtoNewClass(Loc &loc, TypeClass *type, NewExp *newexp); void DtoInitClass(TypeClass *tc, llvm::Value *dst); void DtoFinalizeClass(Loc &loc, llvm::Value *inst); +void DtoFinalizeScopeClass(Loc &loc, llvm::Value *inst, ClassDeclaration *cd); DValue *DtoCastClass(Loc &loc, DValue *val, Type *to); DValue *DtoDynamicCastObject(Loc &loc, DValue *val, Type *to); diff --git a/gen/toir.cpp b/gen/toir.cpp index 859dddcd7d..63990712aa 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -1613,7 +1613,9 @@ public: } else if (e->e1->op == TOKvar) { if (auto vd = static_cast(e->e1)->var->isVarDeclaration()) { if (vd->onstack) { - DtoFinalizeClass(e->loc, DtoRVal(dval)); + assert(vd->scopeClassType); + const auto cd = vd->scopeClassType->sym->isClassDeclaration(); + DtoFinalizeScopeClass(e->loc, DtoRVal(dval), cd); onstack = true; } } diff --git a/gen/tollvm.cpp b/gen/tollvm.cpp index a79d2caf7f..bf979b6358 100644 --- a/gen/tollvm.cpp +++ b/gen/tollvm.cpp @@ -275,7 +275,6 @@ llvm::GetElementPtrInst *DtoGEP(LLValue *ptr, llvm::ArrayRef indices, bool inBounds, const char *name, llvm::BasicBlock *bb) { LLPointerType *p = isaPointer(ptr); - (void)p; assert(p && "GEP expects a pointer type"); auto gep = llvm::GetElementPtrInst::Create( p->getElementType(), ptr, indices, name, bb ? bb : gIR->scopebb()); diff --git a/tests/codegen/gh2515.d b/tests/codegen/gh2515.d new file mode 100644 index 0000000000..65a5d2f34f --- /dev/null +++ b/tests/codegen/gh2515.d @@ -0,0 +1,81 @@ +// For scope-allocated class objects, make sure the _d_callfinalizer() +// druntime call is elided if the object has no dtors and no monitor. + +// RUN: %ldc -O3 -output-ll -of=%t.ll %s && FileCheck %s < %t.ll + +import core.stdc.stdio : printf; + +class Base +{ + int val = 123; + void foo() { val *= 3; } + void bar() { synchronized(this) val *= 2; } +} + +class WithDtor : Base +{ + ~this() {} +} + +class WithImplicitDtor : Base +{ + static struct S { int val; ~this() {} } + S s; +} + +// CHECK: define{{.*}} void @{{.*}}_D6gh251516noDtor_noMonitorFZv +void noDtor_noMonitor() +{ + scope b = new Base(); + b.foo(); + printf("%d\n", b.val); + // CHECK-NOT: _d_callfinalizer + // CHECK: ret void +} + +// CHECK: define{{.*}} void @{{.*}}_D6gh251518noDtor_withMonitorFZv +void noDtor_withMonitor() +{ + scope b = new Base(); + b.bar(); + printf("%d\n", b.val); + // CHECK: _d_callfinalizer + // CHECK: ret void +} + +// CHECK: define{{.*}} void @{{.*}}_D6gh25158withDtorFZv +void withDtor() +{ + scope Base b = new WithDtor(); + b.foo(); + printf("%d\n", b.val); + // CHECK: _d_callfinalizer + // CHECK: ret void +} + +// CHECK: define{{.*}} void @{{.*}}_D6gh251516withImplicitDtorFZv +void withImplicitDtor() +{ + scope Base b = new WithImplicitDtor(); + b.foo(); + printf("%d\n", b.val); + // CHECK: _d_callfinalizer + // CHECK: ret void +} + + +/* Test a C++ class as well, which as of 2.077 isn't implicitly delete()d. */ + +extern(C++) class CppClass +{ + int val = 666; +} + +// CHECK: define{{.*}} void @{{.*}}_D6gh25158cppClassFZv +void cppClass() +{ + scope c = new CppClass(); + printf("%d\n", c.val); + // CHECK-NOT: _d_callfinalizer + // CHECK: ret void +}