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:
Martin Kinkelin 2023-01-21 17:31:49 +01:00
parent 3541eaa03b
commit 153f8ccdce
7 changed files with 82 additions and 28 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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