mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-08 20:06:03 +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
|
Expression edtor; // if !=null, does the destruction of the variable
|
||||||
IntRange* range; // if !=null, the variable is known to be within the range
|
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)
|
final extern (D) this(Loc loc, Type type, Identifier id, Initializer _init, StorageClass storage_class = STCundefined)
|
||||||
{
|
{
|
||||||
super(id);
|
super(id);
|
||||||
|
|
|
@ -268,6 +268,10 @@ public:
|
||||||
Expression *edtor; // if !=NULL, does the destruction of the variable
|
Expression *edtor; // if !=NULL, does the destruction of the variable
|
||||||
IntRange *range; // if !NULL, the variable is known to be within the range
|
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 *);
|
Dsymbol *syntaxCopy(Dsymbol *);
|
||||||
void setFieldOffset(AggregateDeclaration *ad, unsigned *poffset, bool isunion);
|
void setFieldOffset(AggregateDeclaration *ad, unsigned *poffset, bool isunion);
|
||||||
const char *kind() const;
|
const char *kind() const;
|
||||||
|
|
|
@ -2546,6 +2546,10 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
|
||||||
{
|
{
|
||||||
ne.onstack = 1;
|
ne.onstack = 1;
|
||||||
dsym.onstack = true;
|
dsym.onstack = true;
|
||||||
|
version (IN_LLVM)
|
||||||
|
{
|
||||||
|
dsym.scopeClassType = cast(TypeClass) ne.newtype;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include "gen/llvmhelpers.h"
|
#include "gen/llvmhelpers.h"
|
||||||
#include "gen/logger.h"
|
#include "gen/logger.h"
|
||||||
#include "gen/nested.h"
|
#include "gen/nested.h"
|
||||||
|
#include "gen/optimizer.h"
|
||||||
#include "gen/rttibuilder.h"
|
#include "gen/rttibuilder.h"
|
||||||
#include "gen/runtime.h"
|
#include "gen/runtime.h"
|
||||||
#include "gen/structs.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) {
|
DValue *DtoCastClass(Loc &loc, DValue *val, Type *_to) {
|
||||||
IF_LOG Logger::println("DtoCastClass(%s, %s)", val->type->toChars(),
|
IF_LOG Logger::println("DtoCastClass(%s, %s)", val->type->toChars(),
|
||||||
_to->toChars());
|
_to->toChars());
|
||||||
|
|
|
@ -33,6 +33,7 @@ llvm::Constant *DtoDefineClassInfo(ClassDeclaration *cd);
|
||||||
DValue *DtoNewClass(Loc &loc, TypeClass *type, NewExp *newexp);
|
DValue *DtoNewClass(Loc &loc, TypeClass *type, NewExp *newexp);
|
||||||
void DtoInitClass(TypeClass *tc, llvm::Value *dst);
|
void DtoInitClass(TypeClass *tc, llvm::Value *dst);
|
||||||
void DtoFinalizeClass(Loc &loc, llvm::Value *inst);
|
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 *DtoCastClass(Loc &loc, DValue *val, Type *to);
|
||||||
DValue *DtoDynamicCastObject(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) {
|
} else if (e->e1->op == TOKvar) {
|
||||||
if (auto vd = static_cast<VarExp *>(e->e1)->var->isVarDeclaration()) {
|
if (auto vd = static_cast<VarExp *>(e->e1)->var->isVarDeclaration()) {
|
||||||
if (vd->onstack) {
|
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;
|
onstack = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -275,7 +275,6 @@ llvm::GetElementPtrInst *DtoGEP(LLValue *ptr, llvm::ArrayRef<LLValue *> indices,
|
||||||
bool inBounds, const char *name,
|
bool inBounds, const char *name,
|
||||||
llvm::BasicBlock *bb) {
|
llvm::BasicBlock *bb) {
|
||||||
LLPointerType *p = isaPointer(ptr);
|
LLPointerType *p = isaPointer(ptr);
|
||||||
(void)p;
|
|
||||||
assert(p && "GEP expects a pointer type");
|
assert(p && "GEP expects a pointer type");
|
||||||
auto gep = llvm::GetElementPtrInst::Create(
|
auto gep = llvm::GetElementPtrInst::Create(
|
||||||
p->getElementType(), ptr, indices, name, bb ? bb : gIR->scopebb());
|
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