diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h --- a/clang/include/clang/Basic/CodeGenOptions.h +++ b/clang/include/clang/Basic/CodeGenOptions.h @@ -357,6 +357,11 @@ /// Check if CSIR profile use is on. bool hasProfileCSIRUse() const { return getProfileUse() == ProfileCSIRInstr; } + + /// Check if full debug info should be emitted. + bool isFullDebug() const { + return getDebugInfo() >= codegenoptions::DebugInfoConstructor; + } }; } // end namespace clang diff --git a/clang/include/clang/Basic/DebugInfoOptions.h b/clang/include/clang/Basic/DebugInfoOptions.h --- a/clang/include/clang/Basic/DebugInfoOptions.h +++ b/clang/include/clang/Basic/DebugInfoOptions.h @@ -34,6 +34,11 @@ /// (-gline-tables-only). DebugLineTablesOnly, + /// Limit generated debug info for classes to reduce size. This emits class + /// type info only where the constructor is emitted, if it is a class that + /// has a constructor (-flimit-debug-info-constructor). + DebugInfoConstructor, + /// Limit generated debug info to reduce size (-fno-standalone-debug). This /// emits forward decls for types that could be replaced with forward decls in /// the source code. For dynamic C++ classes type info is only emitted into diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -1781,6 +1781,11 @@ HelpText<"Limit debug information produced to reduce size of debug binary">; def flimit_debug_info : Flag<["-"], "flimit-debug-info">, Flags<[CoreOption]>, Alias; def fno_limit_debug_info : Flag<["-"], "fno-limit-debug-info">, Flags<[CoreOption]>, Alias; +def flimit_debug_info_constructor : Flag<["-"], "flimit-debug-info-constructor">, Group, Flags<[CoreOption]>, + HelpText<"Limit debug information by only emitting class debug info when its constructor " + "is emitted">; +def fno_limit_debug_info_constructor : Flag<["-"], "fno-limit-debug-info-constructor">, Group, Flags<[CoreOption]>, + Alias; def fdebug_macro : Flag<["-"], "fdebug-macro">, Group, Flags<[CoreOption]>, HelpText<"Emit macro debug information">; def fno_debug_macro : Flag<["-"], "fno-debug-macro">, Group, Flags<[CoreOption]>, diff --git a/clang/lib/CodeGen/CGBlocks.cpp b/clang/lib/CodeGen/CGBlocks.cpp --- a/clang/lib/CodeGen/CGBlocks.cpp +++ b/clang/lib/CodeGen/CGBlocks.cpp @@ -1483,8 +1483,7 @@ Address alloc = CreateMemTemp(D->getType(), D->getName() + ".addr"); Builder.CreateStore(arg, alloc); if (CGDebugInfo *DI = getDebugInfo()) { - if (CGM.getCodeGenOpts().getDebugInfo() >= - codegenoptions::LimitedDebugInfo) { + if (CGM.getCodeGenOpts().isFullDebug()) { DI->setLocation(D->getLocation()); DI->EmitDeclareOfBlockLiteralArgVariable( *BlockInfo, D->getName(), argNum, @@ -1656,8 +1655,7 @@ const VarDecl *variable = CI.getVariable(); DI->EmitLocation(Builder, variable->getLocation()); - if (CGM.getCodeGenOpts().getDebugInfo() >= - codegenoptions::LimitedDebugInfo) { + if (CGM.getCodeGenOpts().isFullDebug()) { const CGBlockInfo::Capture &capture = blockInfo.getCapture(variable); if (capture.isConstant()) { auto addr = LocalDeclMap.find(variable)->second; diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -591,6 +591,7 @@ case codegenoptions::DebugDirectivesOnly: EmissionKind = llvm::DICompileUnit::DebugDirectivesOnly; break; + case codegenoptions::DebugInfoConstructor: case codegenoptions::LimitedDebugInfo: case codegenoptions::FullDebugInfo: EmissionKind = llvm::DICompileUnit::FullDebug; @@ -1647,6 +1648,12 @@ if (CGM.getLangOpts().Optimize) SPFlags |= llvm::DISubprogram::SPFlagOptimized; + // In this debug mode, emit type info for a class when its constructor type + // info is emitted. + if (DebugKind == codegenoptions::DebugInfoConstructor) + if (const CXXConstructorDecl *CD = dyn_cast(Method)) + completeClass(CD->getParent()); + llvm::DINodeArray TParamsArray = CollectFunctionTemplateParams(Method, Unit); llvm::DISubprogram *SP = DBuilder.createMethod( RecordTy, MethodName, MethodLinkageName, MethodDefUnit, MethodLine, @@ -2043,7 +2050,7 @@ llvm::DIType *CGDebugInfo::getOrCreateRecordType(QualType RTy, SourceLocation Loc) { - assert(DebugKind >= codegenoptions::LimitedDebugInfo); + assert(CGM.getCodeGenOpts().isFullDebug()); llvm::DIType *T = getOrCreateType(RTy, getOrCreateFile(Loc)); return T; } @@ -2055,7 +2062,7 @@ llvm::DIType *CGDebugInfo::getOrCreateStandaloneType(QualType D, SourceLocation Loc) { - assert(DebugKind >= codegenoptions::LimitedDebugInfo); + assert(CGM.getCodeGenOpts().isFullDebug()); assert(!D.isNull() && "null type"); llvm::DIType *T = getOrCreateType(D, getOrCreateFile(Loc)); assert(T && "could not create debug info for type"); @@ -2210,6 +2217,17 @@ !isClassOrMethodDLLImport(CXXDecl)) return true; + // In constructor debug mode, only emit debug info for a class when its + // constructor is emitted. Skip this optimization if the class or any of + // its methods are marked dllimport. + if (DebugKind == codegenoptions::DebugInfoConstructor && + !CXXDecl->isLambda() && !isClassOrMethodDLLImport(CXXDecl)) { + for (auto ctor : CXXDecl->ctors()) { + if (ctor->isUserProvided()) + return true; + } + } + TemplateSpecializationKind Spec = TSK_Undeclared; if (const auto *SD = dyn_cast(RD)) Spec = SD->getSpecializationKind(); @@ -3270,7 +3288,7 @@ DebugKind <= codegenoptions::DebugLineTablesOnly)) LinkageName = StringRef(); - if (DebugKind >= codegenoptions::LimitedDebugInfo) { + if (CGM.getCodeGenOpts().isFullDebug()) { if (const NamespaceDecl *NSDecl = dyn_cast_or_null(FD->getDeclContext())) FDContext = getOrCreateNamespace(NSDecl); @@ -3957,7 +3975,7 @@ llvm::Optional ArgNo, CGBuilderTy &Builder, const bool UsePointerValue) { - assert(DebugKind >= codegenoptions::LimitedDebugInfo); + assert(CGM.getCodeGenOpts().isFullDebug()); assert(!LexicalBlockStack.empty() && "Region stack mismatch, stack empty!"); if (VD->hasAttr()) return nullptr; @@ -4091,12 +4109,12 @@ CGDebugInfo::EmitDeclareOfAutoVariable(const VarDecl *VD, llvm::Value *Storage, CGBuilderTy &Builder, const bool UsePointerValue) { - assert(DebugKind >= codegenoptions::LimitedDebugInfo); + assert(CGM.getCodeGenOpts().isFullDebug()); return EmitDeclare(VD, Storage, llvm::None, Builder, UsePointerValue); } void CGDebugInfo::EmitLabel(const LabelDecl *D, CGBuilderTy &Builder) { - assert(DebugKind >= codegenoptions::LimitedDebugInfo); + assert(CGM.getCodeGenOpts().isFullDebug()); assert(!LexicalBlockStack.empty() && "Region stack mismatch, stack empty!"); if (D->hasAttr()) @@ -4132,7 +4150,7 @@ void CGDebugInfo::EmitDeclareOfBlockDeclRefVariable( const VarDecl *VD, llvm::Value *Storage, CGBuilderTy &Builder, const CGBlockInfo &blockInfo, llvm::Instruction *InsertPoint) { - assert(DebugKind >= codegenoptions::LimitedDebugInfo); + assert(CGM.getCodeGenOpts().isFullDebug()); assert(!LexicalBlockStack.empty() && "Region stack mismatch, stack empty!"); if (Builder.GetInsertBlock() == nullptr) @@ -4203,7 +4221,7 @@ void CGDebugInfo::EmitDeclareOfArgVariable(const VarDecl *VD, llvm::Value *AI, unsigned ArgNo, CGBuilderTy &Builder) { - assert(DebugKind >= codegenoptions::LimitedDebugInfo); + assert(CGM.getCodeGenOpts().isFullDebug()); EmitDeclare(VD, AI, ArgNo, Builder); } @@ -4260,7 +4278,7 @@ unsigned ArgNo, llvm::AllocaInst *Alloca, CGBuilderTy &Builder) { - assert(DebugKind >= codegenoptions::LimitedDebugInfo); + assert(CGM.getCodeGenOpts().isFullDebug()); ASTContext &C = CGM.getContext(); const BlockDecl *blockDecl = block.getBlockDecl(); @@ -4426,7 +4444,7 @@ void CGDebugInfo::EmitGlobalVariable(llvm::GlobalVariable *Var, const VarDecl *D) { - assert(DebugKind >= codegenoptions::LimitedDebugInfo); + assert(CGM.getCodeGenOpts().isFullDebug()); if (D->hasAttr()) return; @@ -4495,7 +4513,7 @@ } void CGDebugInfo::EmitGlobalVariable(const ValueDecl *VD, const APValue &Init) { - assert(DebugKind >= codegenoptions::LimitedDebugInfo); + assert(CGM.getCodeGenOpts().isFullDebug()); if (VD->hasAttr()) return; llvm::TimeTraceScope TimeScope("DebugConstGlobalVariable", [&]() { @@ -4594,7 +4612,7 @@ void CGDebugInfo::EmitExternalVariable(llvm::GlobalVariable *Var, const VarDecl *D) { - assert(DebugKind >= codegenoptions::LimitedDebugInfo); + assert(CGM.getCodeGenOpts().isFullDebug()); if (D->hasAttr()) return; @@ -4619,7 +4637,7 @@ } void CGDebugInfo::EmitUsingDirective(const UsingDirectiveDecl &UD) { - if (CGM.getCodeGenOpts().getDebugInfo() < codegenoptions::LimitedDebugInfo) + if (!CGM.getCodeGenOpts().isFullDebug()) return; const NamespaceDecl *NSDecl = UD.getNominatedNamespace(); if (!NSDecl->isAnonymousNamespace() || @@ -4632,7 +4650,7 @@ } void CGDebugInfo::EmitUsingDecl(const UsingDecl &UD) { - if (CGM.getCodeGenOpts().getDebugInfo() < codegenoptions::LimitedDebugInfo) + if (!CGM.getCodeGenOpts().isFullDebug()) return; assert(UD.shadow_size() && "We shouldn't be codegening an invalid UsingDecl containing no decls"); @@ -4673,7 +4691,7 @@ llvm::DIImportedEntity * CGDebugInfo::EmitNamespaceAlias(const NamespaceAliasDecl &NA) { - if (CGM.getCodeGenOpts().getDebugInfo() < codegenoptions::LimitedDebugInfo) + if (!CGM.getCodeGenOpts().isFullDebug()) return nullptr; auto &VH = NamespaceAliasCache[&NA]; if (VH) @@ -4795,7 +4813,7 @@ } void CGDebugInfo::EmitExplicitCastType(QualType Ty) { - if (CGM.getCodeGenOpts().getDebugInfo() < codegenoptions::LimitedDebugInfo) + if (!CGM.getCodeGenOpts().isFullDebug()) return; if (auto *DieTy = getOrCreateType(Ty, TheCU->getFile())) diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -446,7 +446,7 @@ // Emit global variable debug descriptor for static vars. CGDebugInfo *DI = getDebugInfo(); if (DI && - CGM.getCodeGenOpts().getDebugInfo() >= codegenoptions::LimitedDebugInfo) { + CGM.getCodeGenOpts().isFullDebug()) { DI->setLocation(D.getLocation()); DI->EmitGlobalVariable(var, &D); } @@ -1393,8 +1393,7 @@ EmitVariablyModifiedType(Ty); auto *DI = getDebugInfo(); - bool EmitDebugInfo = DI && CGM.getCodeGenOpts().getDebugInfo() >= - codegenoptions::LimitedDebugInfo; + bool EmitDebugInfo = DI && CGM.getCodeGenOpts().isFullDebug(); Address address = Address::invalid(); Address AllocaAddr = Address::invalid(); @@ -2495,9 +2494,7 @@ // Emit debug info for param declarations in non-thunk functions. if (CGDebugInfo *DI = getDebugInfo()) { - if (CGM.getCodeGenOpts().getDebugInfo() >= - codegenoptions::LimitedDebugInfo && - !CurFuncIsThunk) { + if (CGM.getCodeGenOpts().isFullDebug() && !CurFuncIsThunk) { DI->EmitDeclareOfArgVariable(&D, DeclPtr.getPointer(), ArgNo, Builder); } } diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp --- a/clang/lib/CodeGen/CGStmt.cpp +++ b/clang/lib/CodeGen/CGStmt.cpp @@ -562,8 +562,7 @@ // Emit debug info for labels. if (CGDebugInfo *DI = getDebugInfo()) { - if (CGM.getCodeGenOpts().getDebugInfo() >= - codegenoptions::LimitedDebugInfo) { + if (CGM.getCodeGenOpts().isFullDebug()) { DI->setLocation(D->getLocation()); DI->EmitLabel(D, Builder); } diff --git a/clang/lib/CodeGen/CGStmtOpenMP.cpp b/clang/lib/CodeGen/CGStmtOpenMP.cpp --- a/clang/lib/CodeGen/CGStmtOpenMP.cpp +++ b/clang/lib/CodeGen/CGStmtOpenMP.cpp @@ -567,8 +567,7 @@ const CapturedDecl *CD = S.getCapturedDecl(); // Build the argument list. bool NeedWrapperFunction = - getDebugInfo() && - CGM.getCodeGenOpts().getDebugInfo() >= codegenoptions::LimitedDebugInfo; + getDebugInfo() && CGM.getCodeGenOpts().isFullDebug(); FunctionArgList Args; llvm::MapVector> LocalAddrs; llvm::DenseMap> VLASizes; diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -2101,7 +2101,7 @@ const APValue &Init) { assert(Init.hasValue() && "Invalid DeclRefExpr initializer!"); if (CGDebugInfo *Dbg = getDebugInfo()) - if (CGM.getCodeGenOpts().getDebugInfo() >= codegenoptions::LimitedDebugInfo) + if (CGM.getCodeGenOpts().isFullDebug()) Dbg->EmitGlobalVariable(E->getDecl(), Init); } diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -4113,13 +4113,13 @@ // Emit global variable debug information. if (CGDebugInfo *DI = getModuleDebugInfo()) - if (getCodeGenOpts().getDebugInfo() >= codegenoptions::LimitedDebugInfo) + if (getCodeGenOpts().isFullDebug()) DI->EmitGlobalVariable(GV, D); } void CodeGenModule::EmitExternalVarDeclaration(const VarDecl *D) { if (CGDebugInfo *DI = getModuleDebugInfo()) - if (getCodeGenOpts().getDebugInfo() >= codegenoptions::LimitedDebugInfo) { + if (getCodeGenOpts().isFullDebug()) { QualType ASTTy = D->getType(); llvm::Type *Ty = getTypes().ConvertTypeForMem(D->getType()); llvm::PointerType *PTy = @@ -5366,7 +5366,7 @@ ObjCRuntime->GenerateClass(OMD); // Emit global variable debug information. if (CGDebugInfo *DI = getModuleDebugInfo()) - if (getCodeGenOpts().getDebugInfo() >= codegenoptions::LimitedDebugInfo) + if (getCodeGenOpts().isFullDebug()) DI->getOrCreateInterfaceType(getContext().getObjCInterfaceType( OMD->getClassInterface()), OMD->getLocation()); break; diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -999,6 +999,9 @@ case codegenoptions::DebugLineTablesOnly: CmdArgs.push_back("-debug-info-kind=line-tables-only"); break; + case codegenoptions::DebugInfoConstructor: + CmdArgs.push_back("-debug-info-kind=constructor"); + break; case codegenoptions::LimitedDebugInfo: CmdArgs.push_back("-debug-info-kind=limited"); break; @@ -3682,6 +3685,13 @@ if (DebugInfoKind == codegenoptions::LimitedDebugInfo && NeedFullDebug) DebugInfoKind = codegenoptions::FullDebugInfo; + if (Args.hasFlag(options::OPT_flimit_debug_info_constructor, + options::OPT_fno_limit_debug_info_constructor, false)) + if (checkDebugInfoOption( + Args.getLastArg(options::OPT_flimit_debug_info_constructor), Args, D, TC) && + DebugInfoKind == codegenoptions::LimitedDebugInfo) + DebugInfoKind = codegenoptions::DebugInfoConstructor; + if (Args.hasFlag(options::OPT_gembed_source, options::OPT_gno_embed_source, false)) { // Source embedding is a vendor extension to DWARF v5. By now we have diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -731,6 +731,7 @@ llvm::StringSwitch(A->getValue()) .Case("line-tables-only", codegenoptions::DebugLineTablesOnly) .Case("line-directives-only", codegenoptions::DebugDirectivesOnly) + .Case("constructor", codegenoptions::DebugInfoConstructor) .Case("limited", codegenoptions::LimitedDebugInfo) .Case("standalone", codegenoptions::FullDebugInfo) .Default(~0U); @@ -787,8 +788,7 @@ llvm::Triple::arm, llvm::Triple::armeb}; llvm::Triple T(TargetOpts.Triple); - if (Opts.OptimizationLevel > 0 && - Opts.getDebugInfo() >= codegenoptions::LimitedDebugInfo && + if (Opts.OptimizationLevel > 0 && Opts.isFullDebug() && llvm::is_contained(DebugEntryValueArchs, T.getArch())) Opts.EnableDebugEntryValues = Args.hasArg(OPT_femit_debug_entry_values); diff --git a/clang/test/CodeGenCXX/debug-info-limited-ctor.cpp b/clang/test/CodeGenCXX/debug-info-limited-ctor.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/debug-info-limited-ctor.cpp @@ -0,0 +1,72 @@ +// RUN: %clang -flimit-debug-info-constructor -emit-llvm -g -S %s -o - | FileCheck %s + +// CHECK: !DICompositeType(tag: DW_TAG_class_type, name: "A" +// CHECK-NOT: DIFlagFwdDecl +// CHECK-SAME: ){{$}} +class A { +public: + int z; +}; + +A *TestA(A* x) { + A *a = new A(*x); + return a; +} + +// CHECK: !DICompositeType(tag: DW_TAG_class_type, name: "B" +// CHECK-SAME: flags: DIFlagFwdDecl +class B { +public: + int y; +}; + +extern int bar(B *b); +int TestB(B *b) { + return bar(b); +} + +// CHECK: !DICompositeType(tag: DW_TAG_class_type, name: "C" +// CHECK-SAME: flags: DIFlagFwdDecl +class C { +public: + C(); +}; +C *TestC() { + C *c = new C(); + return c; +} + +// CHECK: !DICompositeType(tag: DW_TAG_class_type, name: "D" +// CHECK-NOT: flags: DIFlagFwdDecl +// CHECK-SAME: ){{$}} +class D { +public: + D() {} +}; +D *TestD() { + D *d = new D(); + return d; +} + +// CHECK: !DICompositeType(tag: DW_TAG_class_type, name: "E" +// CHECK-NOT: flags: DIFlagFwdDecl +// CHECK-SAME: ){{$}} +class E { +public: +*d = new D(); + return d; +} + +// CHECK: !DICompositeType(tag: DW_TAG_class_type, name: "E" +// CHECK-NOT: flags: DIFlagFwdDecl +// CHECK-SAME: ){{$}} +class E { +public: + E(); +}; +E::E() {} +E *TestE() { + E *e = new E(); + return e; +} +