mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-07 11:26:02 +03:00
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:
parent
1da088330d
commit
6367d98d3a
8 changed files with 143 additions and 2 deletions
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
81
tests/codegen/gh2515.d
Normal 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
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue