https://github.com/ClaytonKnittel created https://github.com/llvm/llvm-project/pull/218165
The constructor homing optimization limits the amount of redundant debug info generated for classes by only emitting forward declarations to debug info in translation units that can't instantiate the class on their own (i.e. they don't see the definitions of any constructors). Currently, classes that have _any_ constexpr constructors, or _any_ user-defined constructors are excluded from the optimization. This is being changed to only exclude _defined_ constexpr constructors, as declared constexpr constructors are not callable in a TU that doesn't see their definition. Additionally, we ignore delegating constructors, as we can rely on the delegated constructor to instantiate debug info. >From e2bd9040c622d4180668075e42cd67934110916c Mon Sep 17 00:00:00 2001 From: Clayton Knittel <[email protected]> Date: Sat, 22 Aug 2026 21:25:17 +0000 Subject: [PATCH] Apply constructor homing more aggressively. The constructor homing optimization limits the amount of redundant debug info generated for classes by only emitting forward declarations to debug info in translation units that can't instantiate the class on their own (i.e. they don't see the definitions of any constructors). Currently, classes that have _any_ constexpr constructors, or _any_ user-defined constructors are excluded from the optimization. This is being changed to only exclude _defined_ constexpr constructors, as declared constexpr constructors are not callable in a TU that doesn't see their definition. Additionally, we ignore delegating constructors, as we can rely on the delegated constructor to instantiate debug info. Signed-off-by: Clayton Knittel <[email protected]> --- clang/lib/CodeGen/CGDebugInfo.cpp | 34 ++++++++++++++++---- clang/test/DebugInfo/CXX/limited-ctor.cpp | 39 +++++++++++++++++++++++ 2 files changed, 66 insertions(+), 7 deletions(-) diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index 37ee3765fd7c4..4175e2943e924 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -2539,10 +2539,12 @@ llvm::DISubprogram *CGDebugInfo::CreateCXXMemberFunction( SPFlags |= llvm::DISubprogram::SPFlagOptimized; // In this debug mode, emit type info for a class when its constructor type - // info is emitted. + // info is emitted. Delegating constructors are ignored because the target + // constructor's definition will emit the type info. if (DebugKind == llvm::codegenoptions::DebugInfoConstructor) if (const CXXConstructorDecl *CD = dyn_cast<CXXConstructorDecl>(Method)) - completeUnusedClass(*CD->getParent()); + if (!CD->isDelegatingConstructor()) + completeUnusedClass(*CD->getParent()); llvm::DINodeArray TParamsArray = CollectFunctionTemplateParams(Method, Unit); llvm::DISubprogram *SP = DBuilder.createMethod( @@ -3236,18 +3238,36 @@ static bool canUseCtorHoming(const CXXRecordDecl *RD) { if (isClassOrMethodDLLImport(RD)) return false; - if (RD->isLambda() || RD->isAggregate() || - RD->hasTrivialDefaultConstructor() || - RD->hasConstexprNonCopyMoveConstructor()) + if (RD->isLambda() || RD->isAggregate() || RD->hasTrivialDefaultConstructor()) return false; + // Skip this optimization if the class has a constexpr default constructor, + // since those constructors can be invoked without emitting type information + // for the constructor. + if (RD->needsImplicitDefaultConstructor() && + RD->defaultedDefaultConstructorIsConstexpr()) + return false; + + bool HasNonDeletedCtor = false; for (const CXXConstructorDecl *Ctor : RD->ctors()) { if (Ctor->isCopyOrMoveConstructor()) continue; + const FunctionDecl *Def = nullptr; + if (Ctor->isDefined(Def)) { + const auto *CtorDef = cast<CXXConstructorDecl>(Def); + // Ignore delegating constructors, the target constructor's definition + // will emit the type info. + if (CtorDef->isDelegatingConstructor()) + continue; + // Skip this optimization if we see a defined constexpr constructor, which + // can be invoked without emitting type info. + if (Ctor->isConstexpr() && !Ctor->isDeleted()) + return false; + } if (!Ctor->isDeleted()) - return true; + HasNonDeletedCtor = true; } - return false; + return HasNonDeletedCtor; } static bool shouldOmitDefinition(llvm::codegenoptions::DebugInfoKind DebugKind, diff --git a/clang/test/DebugInfo/CXX/limited-ctor.cpp b/clang/test/DebugInfo/CXX/limited-ctor.cpp index 18adfdeed0480..df41bb7104c34 100644 --- a/clang/test/DebugInfo/CXX/limited-ctor.cpp +++ b/clang/test/DebugInfo/CXX/limited-ctor.cpp @@ -27,6 +27,45 @@ struct E { constexpr E(){}; } TestE; +// Declared but not defined constexpr constructor should not trigger homing. +// CHECK-DAG: !DICompositeType(tag: DW_TAG_structure_type, name: "DeclaredConstexpr"{{.*}}flags: DIFlagFwdDecl +struct DeclaredConstexpr { + constexpr DeclaredConstexpr(); +}; +void f(DeclaredConstexpr d) {} + +// Defined out-of-line constexpr constructor should trigger homing. +// CHECK-DAG: !DICompositeType(tag: DW_TAG_structure_type, name: "OutOfLineConstexpr"{{.*}}DIFlagTypePassByValue +struct OutOfLineConstexpr { + constexpr OutOfLineConstexpr(); +}; +constexpr OutOfLineConstexpr::OutOfLineConstexpr() {} +OutOfLineConstexpr TestOutOfLineConstexpr; + +// Defined delegating constructor where delegated constructor is not defined +// should not trigger homing. +// CHECK-DAG: !DICompositeType(tag: DW_TAG_structure_type, name: "Delegating"{{.*}}flags: DIFlagFwdDecl +struct Delegating { + Delegating() : Delegating(42) {} + Delegating(int); +} TestDelegating; + +// Defined delegating constructor where delegated constructor is from a base +// class should trigger homing. +// CHECK-DAG: !DICompositeType(tag: DW_TAG_structure_type, name: "DelegatingToBaseClass"{{.*}}DIFlagTypePassByValue +struct DelegatingToBaseClass : public Delegating { + DelegatingToBaseClass() : Delegating(42) {} +} TestDelegatingToBaseClass; + +// Defined delegating constexpr constructor where delegated constructor is also +// defined should trigger homing. +// CHECK-DAG: !DICompositeType(tag: DW_TAG_structure_type, name: "DelegatingConstexprBothDefined"{{.*}}DIFlagTypePassByValue +struct DelegatingConstexprBothDefined { + constexpr DelegatingConstexprBothDefined() + : DelegatingConstexprBothDefined(42) {} + constexpr DelegatingConstexprBothDefined(int) {} +} TestDelegatingConstexprBothDefined; + // Test for trivial constructor. // CHECK-DAG: !DICompositeType(tag: DW_TAG_structure_type, name: "F"{{.*}}DIFlagTypePassByValue struct F { _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
