Index: include/clang/Driver/CC1Options.td =================================================================== --- include/clang/Driver/CC1Options.td +++ include/clang/Driver/CC1Options.td @@ -135,6 +135,8 @@ def debug_info_kind_EQ : Joined<["-"], "debug-info-kind=">; def dwarf_version_EQ : Joined<["-"], "dwarf-version=">; def debugger_tuning_EQ : Joined<["-"], "debugger-tuning=">; +def debug_info_retain_types : Flag<["-"], "debug-info-retain-types">, + HelpText<"Retain debug info for types that were optimized away">; def fdebug_compilation_dir : Separate<["-"], "fdebug-compilation-dir">, HelpText<"The compilation directory to embed in the debug info.">; def dwarf_debug_flags : Separate<["-"], "dwarf-debug-flags">, Index: include/clang/Frontend/CodeGenOptions.def =================================================================== --- include/clang/Frontend/CodeGenOptions.def +++ include/clang/Frontend/CodeGenOptions.def @@ -180,6 +180,9 @@ ///< contain explicit imports for ///< anonymous namespaces +CODEGENOPT(RetainAllDebugTypes, 1, 0) ///< Whether to keep debug types for + ///< code that is optimized away. + CODEGENOPT(EmitLLVMUseLists, 1, 0) ///< Control whether to serialize use-lists. CODEGENOPT(WholeProgramVTables, 1, 0) ///< Whether to apply whole-program Index: lib/CodeGen/CGDebugInfo.h =================================================================== --- lib/CodeGen/CGDebugInfo.h +++ lib/CodeGen/CGDebugInfo.h @@ -55,6 +55,7 @@ CodeGenModule &CGM; const codegenoptions::DebugInfoKind DebugKind; bool DebugTypeExtRefs; + bool RetainAllDebugTypes; llvm::DIBuilder DBuilder; llvm::DICompileUnit *TheCU = nullptr; ModuleMap *ClangModuleMap = nullptr; @@ -425,7 +426,10 @@ llvm::DIFile *getOrCreateMainFile(); /// Get the type from the cache or create a new type if necessary. - llvm::DIType *getOrCreateType(QualType Ty, llvm::DIFile *Fg); + /// If Retain is true, the type will be added to the list of + /// retained types if it isn't already cached. + llvm::DIType *getOrCreateType(QualType Ty, llvm::DIFile *Fg, + bool Retain = false); /// Get a reference to a clang module. If \p CreateSkeletonCU is true, /// this also creates a split dwarf skeleton compile unit. Index: lib/CodeGen/CGDebugInfo.cpp =================================================================== --- lib/CodeGen/CGDebugInfo.cpp +++ lib/CodeGen/CGDebugInfo.cpp @@ -46,6 +46,7 @@ CGDebugInfo::CGDebugInfo(CodeGenModule &CGM) : CGM(CGM), DebugKind(CGM.getCodeGenOpts().getDebugInfo()), DebugTypeExtRefs(CGM.getCodeGenOpts().DebugTypeExtRefs), + RetainAllDebugTypes(CGM.getCodeGenOpts().RetainAllDebugTypes), DBuilder(CGM.getModule()) { for (const auto &KV : CGM.getCodeGenOpts().DebugPrefixMap) DebugPrefixMap[KV.first] = KV.second; @@ -1447,8 +1448,7 @@ llvm::DIType *CGDebugInfo::getOrCreateRecordType(QualType RTy, SourceLocation Loc) { assert(DebugKind >= codegenoptions::LimitedDebugInfo); - llvm::DIType *T = getOrCreateType(RTy, getOrCreateFile(Loc)); - return T; + return getOrCreateType(RTy, getOrCreateFile(Loc)); } llvm::DIType *CGDebugInfo::getOrCreateInterfaceType(QualType D, @@ -2190,7 +2190,8 @@ RetainedTypes.push_back(CGM.getContext().getRecordType(&SD).getAsOpaquePtr()); } -llvm::DIType *CGDebugInfo::getOrCreateType(QualType Ty, llvm::DIFile *Unit) { +llvm::DIType *CGDebugInfo::getOrCreateType(QualType Ty, llvm::DIFile *Unit, + bool Retain) { if (Ty.isNull()) return nullptr; @@ -2206,6 +2207,9 @@ // And update the type cache. TypeCache[TyPtr].reset(Res); + if (Retain) + RetainedTypes.push_back(TyPtr); + return Res; } @@ -2739,17 +2743,20 @@ } unsigned LineNo = getLineNumber(Loc); unsigned ScopeLine = getLineNumber(ScopeLoc); + auto *FnTy = getOrCreateFunctionType(D, FnType, Unit); + if (RetainAllDebugTypes) + DBuilder.retainType(FnTy); // FIXME: The function declaration we're constructing here is mostly reusing // declarations from CXXMethodDecl and not constructing new ones for arbitrary // FunctionDecls. When/if we fix this we can have FDContext be TheCU/null for // all subprograms instead of the actual context since subprogram definitions // are emitted as CU level entities by the backend. - llvm::DISubprogram *SP = DBuilder.createFunction( - FDContext, Name, LinkageName, Unit, LineNo, - getOrCreateFunctionType(D, FnType, Unit), Fn->hasInternalLinkage(), - true /*definition*/, ScopeLine, Flags, CGM.getLangOpts().Optimize, - TParamsArray.get(), getFunctionDeclaration(D)); + llvm::DISubprogram *SP = + DBuilder.createFunction(FDContext, Name, LinkageName, Unit, LineNo, FnTy, + Fn->hasInternalLinkage(), true /*definition*/, + ScopeLine, Flags, CGM.getLangOpts().Optimize, + TParamsArray.get(), getFunctionDeclaration(D)); Fn->setSubprogram(SP); // We might get here with a VarDecl in the case we're generating // code for the initialization of globals. Do not record these decls @@ -2959,7 +2966,7 @@ if (VD->hasAttr()) Ty = EmitTypeForVarWithBlocksAttr(VD, &XOffset); else - Ty = getOrCreateType(VD->getType(), Unit); + Ty = getOrCreateType(VD->getType(), Unit, RetainAllDebugTypes); // If there is no debug info for this type then do not emit debug info // for this variable. @@ -3100,7 +3107,7 @@ if (isByRef) Ty = EmitTypeForVarWithBlocksAttr(VD, &XOffset); else - Ty = getOrCreateType(VD->getType(), Unit); + Ty = getOrCreateType(VD->getType(), Unit, RetainAllDebugTypes); // Self is passed along as an implicit non-arg variable in a // block. Mark it as the object pointer. @@ -3382,7 +3389,8 @@ GV = CollectAnonRecordDecls(RD, Unit, LineNo, LinkageName, Var, DContext); } else { GV = DBuilder.createGlobalVariable( - DContext, DeclName, LinkageName, Unit, LineNo, getOrCreateType(T, Unit), + DContext, DeclName, LinkageName, Unit, LineNo, + getOrCreateType(T, Unit, RetainAllDebugTypes), Var->hasInternalLinkage(), Var, getOrCreateStaticDataMemberDeclarationOrNull(D)); } Index: lib/Driver/Tools.cpp =================================================================== --- lib/Driver/Tools.cpp +++ lib/Driver/Tools.cpp @@ -2600,7 +2600,8 @@ static void RenderDebugEnablingArgs(const ArgList &Args, ArgStringList &CmdArgs, codegenoptions::DebugInfoKind DebugInfoKind, unsigned DwarfVersion, - llvm::DebuggerKind DebuggerTuning) { + llvm::DebuggerKind DebuggerTuning, + bool RetainAllDebugTypes) { switch (DebugInfoKind) { case codegenoptions::DebugLineTablesOnly: CmdArgs.push_back("-debug-info-kind=line-tables-only"); @@ -2630,6 +2631,8 @@ default: break; } + if (RetainAllDebugTypes) + CmdArgs.push_back(Args.MakeArgString("-debug-info-retain-types")); } static void CollectArgsForIntegratedAssembler(Compilation &C, @@ -2747,7 +2750,8 @@ } else { RenderDebugEnablingArgs(Args, CmdArgs, codegenoptions::LimitedDebugInfo, - DwarfVersion, llvm::DebuggerKind::Default); + DwarfVersion, llvm::DebuggerKind::Default, + /*retain types*/ false); } } else if (Value.startswith("-mcpu") || Value.startswith("-mfpu") || Value.startswith("-mhwdiv") || Value.startswith("-march")) { @@ -4311,6 +4315,10 @@ CmdArgs.push_back("-split-dwarf=Enable"); } + bool RetainTypes = + !Args.hasFlag(options::OPT_feliminate_unused_debug_symbols, + options::OPT_fno_eliminate_unused_debug_symbols, true); + // After we've dealt with all combinations of things that could // make DebugInfoKind be other than None or DebugLineTablesOnly, // figure out if we need to "upgrade" it to standalone debug info. @@ -4322,7 +4330,7 @@ if (DebugInfoKind == codegenoptions::LimitedDebugInfo && NeedFullDebug) DebugInfoKind = codegenoptions::FullDebugInfo; RenderDebugEnablingArgs(Args, CmdArgs, DebugInfoKind, DwarfVersion, - DebuggerTuning); + DebuggerTuning, RetainTypes); // -ggnu-pubnames turns on gnu style pubnames in the backend. if (Args.hasArg(options::OPT_ggnu_pubnames)) { @@ -6183,7 +6191,8 @@ RenderDebugEnablingArgs(Args, CmdArgs, (WantDebug ? codegenoptions::LimitedDebugInfo : codegenoptions::NoDebugInfo), - DwarfVersion, llvm::DebuggerKind::Default); + DwarfVersion, llvm::DebuggerKind::Default, + /*retain types*/ false); // Add the -fdebug-compilation-dir flag if needed. addDebugCompDirArg(Args, CmdArgs); Index: lib/Frontend/CompilerInvocation.cpp =================================================================== --- lib/Frontend/CompilerInvocation.cpp +++ lib/Frontend/CompilerInvocation.cpp @@ -486,6 +486,7 @@ Args.getAllArgValues(OPT_fwhole_program_vtables_blacklist_EQ); Opts.SplitDwarfFile = Args.getLastArgValue(OPT_split_dwarf_file); Opts.DebugTypeExtRefs = Args.hasArg(OPT_dwarf_ext_refs); + Opts.RetainAllDebugTypes = Args.hasArg(OPT_debug_info_retain_types); Opts.DebugExplicitImport = Triple.isPS4CPU(); for (const auto &Arg : Args.getAllArgValues(OPT_fdebug_prefix_map_EQ)) Index: test/CodeGen/debug-info-retain-types.c =================================================================== --- /dev/null +++ test/CodeGen/debug-info-retain-types.c @@ -0,0 +1,48 @@ +// RUN: %clang_cc1 -fblocks -emit-llvm -o - %s \ +// RUN: -debug-info-kind=standalone -debug-info-retain-types | FileCheck %s +// RUN: %clang_cc1 -fblocks -emit-llvm -o - %s \ +// RUN: -debug-info-kind=standalone -debug-info-retain-types \ +// RUN: | FileCheck %s --check-prefix=CHECK-MIN +// RUN: %clang_cc1 -fblocks -emit-llvm -o - %s \ +// RUN: -debug-info-kind=standalone | FileCheck %s --check-prefix=NORETAIN +// NORETAIN-NOT: !DICompileUnit{{.*}}retainedTypes + +// CHECK-DAG: !DICompileUnit({{.*}}retainedTypes: ![[RETAINED:[0-9]+]] + +// Function type. +// CHECK-DAG: !DISubprogram(name: "foo", {{.*}}type: ![[FNTY:[0-9]+]] +static int foo(int i) { return i; } + +// Global variable. +// CHECK-DAG: !DIGlobalVariable(name: "g", {{.*}}type: ![[GLOBTY:[0-9]+]] + +// Block capture. +// CHECK-DAG: !DILocalVariable(name: "d", {{.*}}type: ![[BLOCKTY:[0-9]+]] + +// Local variable. +// CHECK-DAG: !DILocalVariable(name: "xyz", {{.*}}type: ![[VARTY:[0-9]+]] +static void bar() { + struct X { int a; int b; } xyz; +} + +float g; +int g1; + +void baz() { + double d; + void (^blockptr)(void) = ^(void) { + foo(0); + bar(); + }; +} + +// CHECK-DAG: ![[RETAINED]] = {{.*}}![[FNTY]],{{.*}}![[BLOCKTY]],{{.*}}![[GLOBTY]],{{.*}}![[VARTY]] + + +// CHECK-MIN: !DICompileUnit({{.*}}retainedTypes: ![[RETAINED:[0-9]+]] +// CHECK-MIN: ![[RETAINED]] = !{![[A:.*]], ![[B:.*]], ![[C:.*]], ![[D:.*]], ![[E:.*]], +// CHECK-MIN-NOT: ![[A]] = !DIBasicType(name: "int" +// CHECK-MIN-NOT: ![[B]] = !DIBasicType(name: "int" +// CHECK-MIN-NOT: ![[C]] = !DIBasicType(name: "int" +// CHECK-MIN-NOT: ![[D]] = !DIBasicType(name: "int" +// CHECK-MIN-NOT: ![[E]] = !DIBasicType(name: "int" Index: test/Driver/debug-options.c =================================================================== --- test/Driver/debug-options.c +++ test/Driver/debug-options.c @@ -109,6 +109,12 @@ // RUN: %clang -### -gmodules %s 2>&1 \ // RUN: | FileCheck -check-prefix=GEXTREFS %s // +// RUN: %clang -### -S %s -gfull 2>&1 \ +// RUN: | FileCheck --check-prefix=RETAIN %s +// +// RUN: %clang -### -S %s -g 2>&1 \ +// RUN: | FileCheck --check-prefix=NO_RETAIN %s +// // G: "-cc1" // G: "-debug-info-kind=limited" // @@ -169,7 +175,8 @@ // NOCI-NOT: "-dwarf-column-info" // // GEXTREFS: "-dwarf-ext-refs" "-fmodule-format=obj" "-debug-info-kind={{standalone|limited}}" - +// RETAIN: "-debug-info-retain-types" +// NO_RETAIN-NOT: "-debug-info-retain-types" // RUN: not %clang -cc1 -debug-info-kind=watkind 2>&1 | FileCheck -check-prefix=BADSTRING1 %s // BADSTRING1: error: invalid value 'watkind' in '-debug-info-kind=watkind' // RUN: not %clang -cc1 -debugger-tuning=gmodal 2>&1 | FileCheck -check-prefix=BADSTRING2 %s