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

View file

@ -267,8 +267,8 @@ public:
bool onstack() const; // it is a class that was allocated on the stack
bool onstack(bool v);
#if IN_LLVM
bool onstackWithDtor() const; // it is a class that was allocated on the stack and needs destruction
bool onstackWithDtor(bool v);
bool onstackWithMatchingDynType() const; // and dynamic type is equivalent to static type
bool onstackWithMatchingDynType(bool v);
#endif
bool overlapped() const; // if it is a field and has overlapping
bool overlapped(bool v);

View file

@ -1062,17 +1062,17 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
dsym.onstack = true;
version (IN_LLVM)
{
bool hasDtor = false;
auto cd = (cast(TypeClass) ne.newtype).sym;
for (; cd; cd = cd.baseClass)
auto tcStatic = dsym.type.toBasetype().isTypeClass();
auto tcDynamic = ne.newtype.toBasetype().isTypeClass();
if (!tcDynamic)
{
if (cd.dtor)
{
hasDtor = true;
break;
//printf(".: resolving %s\n", ne.newtype.toPrettyChars());
ne.newtype = ne.newtype.typeSemantic(dsym.loc, sc);
tcDynamic = ne.newtype.toBasetype().isTypeClass();
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,
bool dynTypeMatchesStaticType) {
llvm::Value *inst = DtoRVal(dval);
if (!isOptimizationEnabled() || hasDtor) {
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);
return;
}
@ -199,9 +215,10 @@ void DtoFinalizeScopeClass(const Loc &loc, DValue* dval, bool hasDtor) {
llvm::BasicBlock *ifbb = gIR->insertBB("if");
llvm::BasicBlock *endbb = gIR->insertBBAfter(ifbb, "endif");
llvm::StructType *st = getIrAggr(static_cast<TypeClass *>(dval->type)->sym)
->getLLStructType();
const auto monitor = DtoLoad(st->getElementType(1), DtoGEP(st, inst, 0, 1), ".monitor");
llvm::StructType *st =
getIrAggr(static_cast<TypeClass *>(dval->type)->sym)->getLLStructType();
const auto monitor =
DtoLoad(st->getElementType(1), DtoGEP(st, inst, 0, 1), ".monitor");
const auto hasMonitor =
gIR->ir->CreateICmp(llvm::CmpInst::ICMP_NE, monitor,
getNullValue(monitor->getType()), ".hasMonitor");

View file

@ -29,7 +29,8 @@ void DtoResolveClass(ClassDeclaration *cd);
DValue *DtoNewClass(const Loc &loc, TypeClass *type, NewExp *newexp);
void DtoInitClass(TypeClass *tc, llvm::Value *dst);
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 *DtoDynamicCastObject(const Loc &loc, DValue *val, Type *to);

View file

@ -1644,7 +1644,8 @@ public:
} else if (auto ve = e->e1->isVarExp()) {
if (auto vd = ve->var->isVarDeclaration()) {
if (vd->onstack()) {
DtoFinalizeScopeClass(e->loc, dval, vd->onstackWithDtor());
DtoFinalizeScopeClass(e->loc, dval,
vd->onstackWithMatchingDynType());
onstack = true;
}
}

View file

@ -17,12 +17,6 @@ class WithDtor : Base
~this() {}
}
class WithImplicitDtor : Base
{
static struct S { int val; ~this() {} }
S s;
}
// CHECK: define{{.*}} void @{{.*}}_D6gh251516noDtor_noMonitorFZv
void noDtor_noMonitor()
{
@ -46,7 +40,18 @@ void noDtor_withMonitor()
// CHECK: define{{.*}} void @{{.*}}_D6gh25158withDtorFZv
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();
printf("%d\n", b.val);
// CHECK: _d_callfinalizer
@ -56,7 +61,13 @@ void withDtor()
// CHECK: define{{.*}} void @{{.*}}_D6gh251516withImplicitDtorFZv
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();
printf("%d\n", b.val);
// 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. */
extern(C++) class CppClass