Don't finalize scope objects without dtors and monitor (#2516)

The dtors can be checked at compile-time; insert a runtime check for the
monitor before finalizing the stack-allocated class object via druntime
call.

See issue #2515.
This commit is contained in:
Martin Kinkelin 2018-01-26 18:38:16 +01:00 committed by GitHub
parent 1da088330d
commit 6367d98d3a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 143 additions and 2 deletions

View file

@ -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);

View file

@ -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;

View file

@ -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;
}
}
}
}

View file

@ -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());

View file

@ -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);

View file

@ -1613,7 +1613,9 @@ public:
} else if (e->e1->op == TOKvar) {
if (auto vd = static_cast<VarExp *>(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;
}
}

View file

@ -275,7 +275,6 @@ llvm::GetElementPtrInst *DtoGEP(LLValue *ptr, llvm::ArrayRef<LLValue *> 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());

81
tests/codegen/gh2515.d Normal file
View file

@ -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
}