mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-02 16:11:08 +03:00
Fix regression for LDC-specific elide-scope-class-finalization optimization
`NewExp.type` can apparently newly be a yet-unresolved `TypeIdentifier`. Resolve it in that case to a `TypeClass` now. Also defer the has-dtor check for the whole class hierarchy of the *dynamic* type from that LDC-specific frontend addition to the glue layer, as I'm not sure we can rely on `ClassDeclaration.dtor` having been set at that time already. I've chosen to keep storing a single extra LDC-specific *bit* (`onstackWithMatchingDynType`) instead of storing the full dynamic class type. While this slightly weakens the optimization (no optimization for non-equivalent static/dynamic class types anymore), it doesn't increase the AST node size. And I think almost all cases use an equivalent type anyway (e.g., the DMD frontend itself only uses `scope` class allocations with equivalent types).
This commit is contained in:
parent
3541eaa03b
commit
153f8ccdce
7 changed files with 82 additions and 28 deletions
|
@ -1143,7 +1143,7 @@ extern (C++) class VarDeclaration : Declaration
|
||||||
bool onstack;
|
bool onstack;
|
||||||
version (IN_LLVM)
|
version (IN_LLVM)
|
||||||
{
|
{
|
||||||
bool onstackWithDtor; /// it is a class that was allocated on the stack and needs destruction
|
bool onstackWithMatchingDynType; /// and dynamic type is equivalent to static type
|
||||||
}
|
}
|
||||||
|
|
||||||
bool overlapped; /// if it is a field and has overlapping
|
bool overlapped; /// if it is a field and has overlapping
|
||||||
|
|
|
@ -267,8 +267,8 @@ public:
|
||||||
bool onstack() const; // it is a class that was allocated on the stack
|
bool onstack() const; // it is a class that was allocated on the stack
|
||||||
bool onstack(bool v);
|
bool onstack(bool v);
|
||||||
#if IN_LLVM
|
#if IN_LLVM
|
||||||
bool onstackWithDtor() const; // it is a class that was allocated on the stack and needs destruction
|
bool onstackWithMatchingDynType() const; // and dynamic type is equivalent to static type
|
||||||
bool onstackWithDtor(bool v);
|
bool onstackWithMatchingDynType(bool v);
|
||||||
#endif
|
#endif
|
||||||
bool overlapped() const; // if it is a field and has overlapping
|
bool overlapped() const; // if it is a field and has overlapping
|
||||||
bool overlapped(bool v);
|
bool overlapped(bool v);
|
||||||
|
|
|
@ -1062,17 +1062,17 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
|
||||||
dsym.onstack = true;
|
dsym.onstack = true;
|
||||||
version (IN_LLVM)
|
version (IN_LLVM)
|
||||||
{
|
{
|
||||||
bool hasDtor = false;
|
auto tcStatic = dsym.type.toBasetype().isTypeClass();
|
||||||
auto cd = (cast(TypeClass) ne.newtype).sym;
|
auto tcDynamic = ne.newtype.toBasetype().isTypeClass();
|
||||||
for (; cd; cd = cd.baseClass)
|
if (!tcDynamic)
|
||||||
{
|
{
|
||||||
if (cd.dtor)
|
//printf(".: resolving %s\n", ne.newtype.toPrettyChars());
|
||||||
{
|
ne.newtype = ne.newtype.typeSemantic(dsym.loc, sc);
|
||||||
hasDtor = true;
|
tcDynamic = ne.newtype.toBasetype().isTypeClass();
|
||||||
break;
|
assert(tcDynamic);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
dsym.onstackWithDtor = hasDtor;
|
|
||||||
|
dsym.onstackWithMatchingDynType = tcStatic.sym is tcDynamic.sym;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -187,9 +187,25 @@ void DtoFinalizeClass(const Loc &loc, LLValue *inst) {
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
void DtoFinalizeScopeClass(const Loc &loc, DValue* dval, bool hasDtor) {
|
void DtoFinalizeScopeClass(const Loc &loc, DValue *dval,
|
||||||
llvm::Value* inst = DtoRVal(dval);
|
bool dynTypeMatchesStaticType) {
|
||||||
if (!isOptimizationEnabled() || hasDtor) {
|
llvm::Value *inst = DtoRVal(dval);
|
||||||
|
|
||||||
|
if (!isOptimizationEnabled() || !dynTypeMatchesStaticType) {
|
||||||
|
DtoFinalizeClass(loc, inst);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasDtor = false;
|
||||||
|
auto cd = dval->type->toBasetype()->isTypeClass()->sym;
|
||||||
|
for (; cd; cd = cd->baseClass) {
|
||||||
|
if (cd->dtor) {
|
||||||
|
hasDtor = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasDtor) {
|
||||||
DtoFinalizeClass(loc, inst);
|
DtoFinalizeClass(loc, inst);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -199,9 +215,10 @@ void DtoFinalizeScopeClass(const Loc &loc, DValue* dval, bool hasDtor) {
|
||||||
llvm::BasicBlock *ifbb = gIR->insertBB("if");
|
llvm::BasicBlock *ifbb = gIR->insertBB("if");
|
||||||
llvm::BasicBlock *endbb = gIR->insertBBAfter(ifbb, "endif");
|
llvm::BasicBlock *endbb = gIR->insertBBAfter(ifbb, "endif");
|
||||||
|
|
||||||
llvm::StructType *st = getIrAggr(static_cast<TypeClass *>(dval->type)->sym)
|
llvm::StructType *st =
|
||||||
->getLLStructType();
|
getIrAggr(static_cast<TypeClass *>(dval->type)->sym)->getLLStructType();
|
||||||
const auto monitor = DtoLoad(st->getElementType(1), DtoGEP(st, inst, 0, 1), ".monitor");
|
const auto monitor =
|
||||||
|
DtoLoad(st->getElementType(1), DtoGEP(st, inst, 0, 1), ".monitor");
|
||||||
const auto hasMonitor =
|
const auto hasMonitor =
|
||||||
gIR->ir->CreateICmp(llvm::CmpInst::ICMP_NE, monitor,
|
gIR->ir->CreateICmp(llvm::CmpInst::ICMP_NE, monitor,
|
||||||
getNullValue(monitor->getType()), ".hasMonitor");
|
getNullValue(monitor->getType()), ".hasMonitor");
|
||||||
|
|
|
@ -29,7 +29,8 @@ void DtoResolveClass(ClassDeclaration *cd);
|
||||||
DValue *DtoNewClass(const Loc &loc, TypeClass *type, NewExp *newexp);
|
DValue *DtoNewClass(const Loc &loc, TypeClass *type, NewExp *newexp);
|
||||||
void DtoInitClass(TypeClass *tc, llvm::Value *dst);
|
void DtoInitClass(TypeClass *tc, llvm::Value *dst);
|
||||||
void DtoFinalizeClass(const Loc &loc, llvm::Value *inst);
|
void DtoFinalizeClass(const Loc &loc, llvm::Value *inst);
|
||||||
void DtoFinalizeScopeClass(const Loc &loc, DValue* dval, bool hasDtor);
|
void DtoFinalizeScopeClass(const Loc &loc, DValue *dval,
|
||||||
|
bool dynTypeMatchesStaticType);
|
||||||
|
|
||||||
DValue *DtoCastClass(const Loc &loc, DValue *val, Type *to);
|
DValue *DtoCastClass(const Loc &loc, DValue *val, Type *to);
|
||||||
DValue *DtoDynamicCastObject(const Loc &loc, DValue *val, Type *to);
|
DValue *DtoDynamicCastObject(const Loc &loc, DValue *val, Type *to);
|
||||||
|
|
|
@ -1644,7 +1644,8 @@ public:
|
||||||
} else if (auto ve = e->e1->isVarExp()) {
|
} else if (auto ve = e->e1->isVarExp()) {
|
||||||
if (auto vd = ve->var->isVarDeclaration()) {
|
if (auto vd = ve->var->isVarDeclaration()) {
|
||||||
if (vd->onstack()) {
|
if (vd->onstack()) {
|
||||||
DtoFinalizeScopeClass(e->loc, dval, vd->onstackWithDtor());
|
DtoFinalizeScopeClass(e->loc, dval,
|
||||||
|
vd->onstackWithMatchingDynType());
|
||||||
onstack = true;
|
onstack = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,12 +17,6 @@ class WithDtor : Base
|
||||||
~this() {}
|
~this() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
class WithImplicitDtor : Base
|
|
||||||
{
|
|
||||||
static struct S { int val; ~this() {} }
|
|
||||||
S s;
|
|
||||||
}
|
|
||||||
|
|
||||||
// CHECK: define{{.*}} void @{{.*}}_D6gh251516noDtor_noMonitorFZv
|
// CHECK: define{{.*}} void @{{.*}}_D6gh251516noDtor_noMonitorFZv
|
||||||
void noDtor_noMonitor()
|
void noDtor_noMonitor()
|
||||||
{
|
{
|
||||||
|
@ -46,7 +40,18 @@ void noDtor_withMonitor()
|
||||||
// CHECK: define{{.*}} void @{{.*}}_D6gh25158withDtorFZv
|
// CHECK: define{{.*}} void @{{.*}}_D6gh25158withDtorFZv
|
||||||
void withDtor()
|
void withDtor()
|
||||||
{
|
{
|
||||||
scope Base b = new WithDtor();
|
scope b = new WithDtor();
|
||||||
|
b.foo();
|
||||||
|
printf("%d\n", b.val);
|
||||||
|
// CHECK: _d_callfinalizer
|
||||||
|
// CHECK: ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK: define{{.*}} void @{{.*}}_D6gh251517withInheritedDtorFZv
|
||||||
|
void withInheritedDtor()
|
||||||
|
{
|
||||||
|
static class WithInheritedDtor : WithDtor {}
|
||||||
|
scope b = new WithInheritedDtor();
|
||||||
b.foo();
|
b.foo();
|
||||||
printf("%d\n", b.val);
|
printf("%d\n", b.val);
|
||||||
// CHECK: _d_callfinalizer
|
// CHECK: _d_callfinalizer
|
||||||
|
@ -56,7 +61,13 @@ void withDtor()
|
||||||
// CHECK: define{{.*}} void @{{.*}}_D6gh251516withImplicitDtorFZv
|
// CHECK: define{{.*}} void @{{.*}}_D6gh251516withImplicitDtorFZv
|
||||||
void withImplicitDtor()
|
void withImplicitDtor()
|
||||||
{
|
{
|
||||||
scope Base b = new WithImplicitDtor();
|
static class WithImplicitDtor : Base
|
||||||
|
{
|
||||||
|
static struct S { int val; ~this() {} }
|
||||||
|
S s;
|
||||||
|
}
|
||||||
|
|
||||||
|
scope b = new WithImplicitDtor();
|
||||||
b.foo();
|
b.foo();
|
||||||
printf("%d\n", b.val);
|
printf("%d\n", b.val);
|
||||||
// CHECK: _d_callfinalizer
|
// CHECK: _d_callfinalizer
|
||||||
|
@ -64,6 +75,30 @@ void withImplicitDtor()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Test static vs. dynamic type mismatches. */
|
||||||
|
|
||||||
|
// CHECK: define{{.*}} void @{{.*}}_D6gh251529staticAndDynamicTypesMismatchFZv
|
||||||
|
void staticAndDynamicTypesMismatch() // not optimized
|
||||||
|
{
|
||||||
|
static class NoDtor : Base {}
|
||||||
|
|
||||||
|
scope Base b = new NoDtor();
|
||||||
|
b.foo();
|
||||||
|
printf("%d\n", b.val);
|
||||||
|
// CHECK: _d_callfinalizer
|
||||||
|
// CHECK: ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK: define{{.*}} void @{{.*}}_D6gh251526typesMatchModuloQualifiersFZv
|
||||||
|
void typesMatchModuloQualifiers() // different qualifiers don't prevent optimization
|
||||||
|
{
|
||||||
|
scope shared Base b = new Base();
|
||||||
|
printf("%d\n", b.val);
|
||||||
|
// CHECK-NOT: _d_callfinalizer
|
||||||
|
// CHECK: ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Test a C++ class as well, which as of 2.077 isn't implicitly delete()d. */
|
/* Test a C++ class as well, which as of 2.077 isn't implicitly delete()d. */
|
||||||
|
|
||||||
extern(C++) class CppClass
|
extern(C++) class CppClass
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue