forked from OSchip/llvm-project
[Clang] implement -fno-eliminate-unused-debug-types
Fixes pr/11710. Signed-off-by: Nick Desaulniers <ndesaulniers@google.com> Reviewed By: dblaikie Differential Revision: https://reviews.llvm.org/D80242
This commit is contained in:
parent
645de3664a
commit
e486921fd6
|
@ -2154,6 +2154,10 @@ Emit section containing metadata on function stack sizes
|
|||
|
||||
Emit full debug info for all types used by the program
|
||||
|
||||
.. option:: -feliminate-unused-debug-types, -fno-eliminate-unused-debug-types
|
||||
|
||||
Suppress (or emit) debug info for types that are unused but defined by the program.
|
||||
|
||||
.. option:: -fstrict-aliasing, -fno-strict-aliasing
|
||||
|
||||
.. option:: -fstrict-enums, -fno-strict-enums
|
||||
|
|
|
@ -433,6 +433,12 @@ Code Generation Options
|
|||
never emit type information for types that are not referenced at all by the
|
||||
program.
|
||||
|
||||
.. option:: -feliminate-unused-debug-types
|
||||
|
||||
By default, Clang does not emit type information for types that are defined
|
||||
but not used in a program. To retain the debug info for these unused types,
|
||||
the negation **-fno-eliminate-unused-debug-types** can be used.
|
||||
|
||||
.. option:: -fexceptions
|
||||
|
||||
Enable generation of unwind information. This allows exceptions to be thrown
|
||||
|
|
|
@ -2352,6 +2352,12 @@ below. If multiple flags are present, the last one is used.
|
|||
|
||||
Generate complete debug info.
|
||||
|
||||
.. option:: -feliminate-unused-debug-types
|
||||
|
||||
By default, Clang does not emit type information for types that are defined
|
||||
but not used in a program. To retain the debug info for these unused types,
|
||||
the negation **-fno-eliminate-unused-debug-types** can be used.
|
||||
|
||||
Controlling Macro Debug Info Generation
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
|
|
@ -288,7 +288,6 @@ CODEGENOPT(DebugFwdTemplateParams, 1, 0) ///< Whether to emit complete
|
|||
///< template parameter descriptions in
|
||||
///< forward declarations (versus just
|
||||
///< including them in the name).
|
||||
|
||||
CODEGENOPT(EmitLLVMUseLists, 1, 0) ///< Control whether to serialize use-lists.
|
||||
|
||||
CODEGENOPT(WholeProgramVTables, 1, 0) ///< Whether to apply whole-program
|
||||
|
@ -313,7 +312,7 @@ VALUE_CODEGENOPT(SmallDataLimit, 32, 0)
|
|||
VALUE_CODEGENOPT(SSPBufferSize, 32, 0)
|
||||
|
||||
/// The kind of generated debug info.
|
||||
ENUM_CODEGENOPT(DebugInfo, codegenoptions::DebugInfoKind, 3, codegenoptions::NoDebugInfo)
|
||||
ENUM_CODEGENOPT(DebugInfo, codegenoptions::DebugInfoKind, 4, codegenoptions::NoDebugInfo)
|
||||
|
||||
/// Whether to generate macro debug info.
|
||||
CODEGENOPT(MacroDebugInfo, 1, 0)
|
||||
|
|
|
@ -388,6 +388,11 @@ public:
|
|||
bool hasReducedDebugInfo() const {
|
||||
return getDebugInfo() >= codegenoptions::DebugInfoConstructor;
|
||||
}
|
||||
|
||||
/// Check if maybe unused type info should be emitted.
|
||||
bool hasMaybeUnusedDebugInfo() const {
|
||||
return getDebugInfo() >= codegenoptions::UnusedTypeInfo;
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace clang
|
||||
|
|
|
@ -46,7 +46,11 @@ enum DebugInfoKind {
|
|||
LimitedDebugInfo,
|
||||
|
||||
/// Generate complete debug info.
|
||||
FullDebugInfo
|
||||
FullDebugInfo,
|
||||
|
||||
/// Generate debug info for types that may be unused in the source
|
||||
/// (-fno-eliminate-unused-debug-types).
|
||||
UnusedTypeInfo,
|
||||
};
|
||||
|
||||
} // end namespace codegenoptions
|
||||
|
|
|
@ -943,6 +943,8 @@ def fno_elide_type : Flag<["-"], "fno-elide-type">, Group<f_Group>,
|
|||
Flags<[CC1Option]>,
|
||||
HelpText<"Do not elide types when printing diagnostics">;
|
||||
def feliminate_unused_debug_symbols : Flag<["-"], "feliminate-unused-debug-symbols">, Group<f_Group>;
|
||||
defm eliminate_unused_debug_types : OptOutFFlag<"eliminate-unused-debug-types",
|
||||
"Do not emit ", "Emit ", " debug info for defined but unused types">;
|
||||
def femit_all_decls : Flag<["-"], "femit-all-decls">, Group<f_Group>, Flags<[CC1Option]>,
|
||||
HelpText<"Emit all declarations, even if unused">;
|
||||
def femulated_tls : Flag<["-"], "femulated-tls">, Group<f_Group>, Flags<[CC1Option]>,
|
||||
|
@ -3321,7 +3323,6 @@ def fdiagnostics_show_location_EQ : Joined<["-"], "fdiagnostics-show-location=">
|
|||
defm fcheck_new : BooleanFFlag<"check-new">, Group<clang_ignored_f_Group>;
|
||||
defm caller_saves : BooleanFFlag<"caller-saves">, Group<clang_ignored_gcc_optimization_f_Group>;
|
||||
defm reorder_blocks : BooleanFFlag<"reorder-blocks">, Group<clang_ignored_gcc_optimization_f_Group>;
|
||||
defm eliminate_unused_debug_types : BooleanFFlag<"eliminate-unused-debug-types">, Group<clang_ignored_f_Group>;
|
||||
defm branch_count_reg : BooleanFFlag<"branch-count-reg">, Group<clang_ignored_gcc_optimization_f_Group>;
|
||||
defm default_inline : BooleanFFlag<"default-inline">, Group<clang_ignored_gcc_optimization_f_Group>;
|
||||
defm fat_lto_objects : BooleanFFlag<"fat-lto-objects">, Group<clang_ignored_gcc_optimization_f_Group>;
|
||||
|
|
|
@ -606,6 +606,7 @@ void CGDebugInfo::CreateCompileUnit() {
|
|||
case codegenoptions::DebugInfoConstructor:
|
||||
case codegenoptions::LimitedDebugInfo:
|
||||
case codegenoptions::FullDebugInfo:
|
||||
case codegenoptions::UnusedTypeInfo:
|
||||
EmissionKind = llvm::DICompileUnit::FullDebug;
|
||||
break;
|
||||
}
|
||||
|
@ -4960,13 +4961,17 @@ void CGDebugInfo::finalize() {
|
|||
DBuilder.finalize();
|
||||
}
|
||||
|
||||
// Don't ignore in case of explicit cast where it is referenced indirectly.
|
||||
void CGDebugInfo::EmitExplicitCastType(QualType Ty) {
|
||||
if (!CGM.getCodeGenOpts().hasReducedDebugInfo())
|
||||
return;
|
||||
if (CGM.getCodeGenOpts().hasReducedDebugInfo())
|
||||
if (auto *DieTy = getOrCreateType(Ty, TheCU->getFile()))
|
||||
DBuilder.retainType(DieTy);
|
||||
}
|
||||
|
||||
if (auto *DieTy = getOrCreateType(Ty, TheCU->getFile()))
|
||||
// Don't ignore in case of explicit cast where it is referenced indirectly.
|
||||
DBuilder.retainType(DieTy);
|
||||
void CGDebugInfo::EmitAndRetainType(QualType Ty) {
|
||||
if (CGM.getCodeGenOpts().hasMaybeUnusedDebugInfo())
|
||||
if (auto *DieTy = getOrCreateType(Ty, TheCU->getFile()))
|
||||
DBuilder.retainType(DieTy);
|
||||
}
|
||||
|
||||
llvm::DebugLoc CGDebugInfo::SourceLocToDebugLoc(SourceLocation Loc) {
|
||||
|
|
|
@ -490,6 +490,9 @@ public:
|
|||
/// Emit the type explicitly casted to.
|
||||
void EmitExplicitCastType(QualType Ty);
|
||||
|
||||
/// Emit the type even if it might not be used.
|
||||
void EmitAndRetainType(QualType Ty);
|
||||
|
||||
/// Emit C++ using declaration.
|
||||
void EmitUsingDecl(const UsingDecl &UD);
|
||||
|
||||
|
|
|
@ -100,11 +100,19 @@ void CodeGenFunction::EmitDecl(const Decl &D) {
|
|||
case Decl::ObjCTypeParam:
|
||||
case Decl::Binding:
|
||||
llvm_unreachable("Declaration should not be in declstmts!");
|
||||
case Decl::Function: // void X();
|
||||
case Decl::Record: // struct/union/class X;
|
||||
case Decl::Enum: // enum X;
|
||||
case Decl::EnumConstant: // enum ? { X = ? }
|
||||
case Decl::CXXRecord: // struct/union/class X; [C++]
|
||||
if (CGDebugInfo *DI = getDebugInfo())
|
||||
if (cast<RecordDecl>(D).getDefinition())
|
||||
DI->EmitAndRetainType(getContext().getRecordType(cast<RecordDecl>(&D)));
|
||||
return;
|
||||
case Decl::Enum: // enum X;
|
||||
if (CGDebugInfo *DI = getDebugInfo())
|
||||
if (cast<EnumDecl>(D).getDefinition())
|
||||
DI->EmitAndRetainType(getContext().getEnumType(cast<EnumDecl>(&D)));
|
||||
return;
|
||||
case Decl::Function: // void X();
|
||||
case Decl::EnumConstant: // enum ? { X = ? }
|
||||
case Decl::StaticAssert: // static_assert(X, ""); [C++0x]
|
||||
case Decl::Label: // __label__ x;
|
||||
case Decl::Import:
|
||||
|
@ -157,12 +165,11 @@ void CodeGenFunction::EmitDecl(const Decl &D) {
|
|||
|
||||
case Decl::Typedef: // typedef int X;
|
||||
case Decl::TypeAlias: { // using X = int; [C++0x]
|
||||
const TypedefNameDecl &TD = cast<TypedefNameDecl>(D);
|
||||
QualType Ty = TD.getUnderlyingType();
|
||||
|
||||
QualType Ty = cast<TypedefNameDecl>(D).getUnderlyingType();
|
||||
if (CGDebugInfo *DI = getDebugInfo())
|
||||
DI->EmitAndRetainType(Ty);
|
||||
if (Ty->isVariablyModifiedType())
|
||||
EmitVariablyModifiedType(Ty);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5392,16 +5392,21 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) {
|
|||
Spec->hasDefinition())
|
||||
DI->completeTemplateDefinition(*Spec);
|
||||
} LLVM_FALLTHROUGH;
|
||||
case Decl::CXXRecord:
|
||||
if (CGDebugInfo *DI = getModuleDebugInfo())
|
||||
case Decl::CXXRecord: {
|
||||
CXXRecordDecl *CRD = cast<CXXRecordDecl>(D);
|
||||
if (CGDebugInfo *DI = getModuleDebugInfo()) {
|
||||
if (CRD->hasDefinition())
|
||||
DI->EmitAndRetainType(getContext().getRecordType(cast<RecordDecl>(D)));
|
||||
if (auto *ES = D->getASTContext().getExternalSource())
|
||||
if (ES->hasExternalDefinitions(D) == ExternalASTSource::EK_Never)
|
||||
DI->completeUnusedClass(cast<CXXRecordDecl>(*D));
|
||||
DI->completeUnusedClass(*CRD);
|
||||
}
|
||||
// Emit any static data members, they may be definitions.
|
||||
for (auto *I : cast<CXXRecordDecl>(D)->decls())
|
||||
for (auto *I : CRD->decls())
|
||||
if (isa<VarDecl>(I) || isa<CXXRecordDecl>(I))
|
||||
EmitTopLevelDecl(I);
|
||||
break;
|
||||
}
|
||||
// No code generation needed.
|
||||
case Decl::UsingShadow:
|
||||
case Decl::ClassTemplate:
|
||||
|
@ -5587,6 +5592,25 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) {
|
|||
EmitOMPRequiresDecl(cast<OMPRequiresDecl>(D));
|
||||
break;
|
||||
|
||||
case Decl::Typedef:
|
||||
case Decl::TypeAlias: // using foo = bar; [C++11]
|
||||
if (CGDebugInfo *DI = getModuleDebugInfo())
|
||||
DI->EmitAndRetainType(
|
||||
getContext().getTypedefType(cast<TypedefNameDecl>(D)));
|
||||
break;
|
||||
|
||||
case Decl::Record:
|
||||
if (CGDebugInfo *DI = getModuleDebugInfo())
|
||||
if (cast<RecordDecl>(D)->getDefinition())
|
||||
DI->EmitAndRetainType(getContext().getRecordType(cast<RecordDecl>(D)));
|
||||
break;
|
||||
|
||||
case Decl::Enum:
|
||||
if (CGDebugInfo *DI = getModuleDebugInfo())
|
||||
if (cast<EnumDecl>(D)->getDefinition())
|
||||
DI->EmitAndRetainType(getContext().getEnumType(cast<EnumDecl>(D)));
|
||||
break;
|
||||
|
||||
default:
|
||||
// Make sure we handled everything we should, every other kind is a
|
||||
// non-top-level decl. FIXME: Would be nice to have an isTopLevelDeclKind
|
||||
|
|
|
@ -976,6 +976,9 @@ static void RenderDebugEnablingArgs(const ArgList &Args, ArgStringList &CmdArgs,
|
|||
case codegenoptions::FullDebugInfo:
|
||||
CmdArgs.push_back("-debug-info-kind=standalone");
|
||||
break;
|
||||
case codegenoptions::UnusedTypeInfo:
|
||||
CmdArgs.push_back("-debug-info-kind=unused-types");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -3781,8 +3784,14 @@ static void RenderDebugOptions(const ToolChain &TC, const Driver &D,
|
|||
TC.GetDefaultStandaloneDebug());
|
||||
if (const Arg *A = Args.getLastArg(options::OPT_fstandalone_debug))
|
||||
(void)checkDebugInfoOption(A, Args, D, TC);
|
||||
if (DebugInfoKind == codegenoptions::LimitedDebugInfo && NeedFullDebug)
|
||||
DebugInfoKind = codegenoptions::FullDebugInfo;
|
||||
|
||||
if (DebugInfoKind == codegenoptions::LimitedDebugInfo) {
|
||||
if (Args.hasFlag(options::OPT_fno_eliminate_unused_debug_types,
|
||||
options::OPT_feliminate_unused_debug_types, false))
|
||||
DebugInfoKind = codegenoptions::UnusedTypeInfo;
|
||||
else if (NeedFullDebug)
|
||||
DebugInfoKind = codegenoptions::FullDebugInfo;
|
||||
}
|
||||
|
||||
if (Args.hasFlag(options::OPT_gembed_source, options::OPT_gno_embed_source,
|
||||
false)) {
|
||||
|
|
|
@ -767,6 +767,7 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK,
|
|||
.Case("constructor", codegenoptions::DebugInfoConstructor)
|
||||
.Case("limited", codegenoptions::LimitedDebugInfo)
|
||||
.Case("standalone", codegenoptions::FullDebugInfo)
|
||||
.Case("unused-types", codegenoptions::UnusedTypeInfo)
|
||||
.Default(~0U);
|
||||
if (Val == ~0U)
|
||||
Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args)
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
// RUN: %clang -fno-eliminate-unused-debug-types -g -emit-llvm -S -o - %s | FileCheck %s
|
||||
// RUN: %clang -fno-eliminate-unused-debug-types -g1 -emit-llvm -S -o - %s | FileCheck --check-prefix=NODBG %s
|
||||
// RUN: %clang -feliminate-unused-debug-types -g -emit-llvm -S -o - %s | FileCheck --check-prefix=NODBG %s
|
||||
// RUN: %clang -g -emit-llvm -S -o - %s | FileCheck --check-prefix=NODBG %s
|
||||
// RUN: %clang -emit-llvm -S -o - %s | FileCheck --check-prefix=NODBG %s
|
||||
typedef int my_int;
|
||||
struct foo {};
|
||||
enum bar { BAR };
|
||||
union baz {};
|
||||
|
||||
void quux(void) {
|
||||
typedef int x;
|
||||
struct y {};
|
||||
enum z { Z };
|
||||
union w {};
|
||||
}
|
||||
|
||||
// Check that debug info is emitted for the typedef, struct, enum, and union
|
||||
// when -fno-eliminate-unused-debug-types and -g are set.
|
||||
|
||||
// CHECK: !DICompileUnit{{.+}}retainedTypes: [[RETTYPES:![0-9]+]]
|
||||
// CHECK: [[TYPE0:![0-9]+]] = !DICompositeType(tag: DW_TAG_enumeration_type, name: "bar"
|
||||
// CHECK: [[TYPE1:![0-9]+]] = !DIEnumerator(name: "BAR"
|
||||
// CHECK: [[TYPE2:![0-9]+]] = !DICompositeType(tag: DW_TAG_enumeration_type, name: "z"
|
||||
// CHECK: [[TYPE3:![0-9]+]] = !DIEnumerator(name: "Z"
|
||||
// CHECK: [[RETTYPES]] = !{[[TYPE4:![0-9]+]], [[TYPE5:![0-9]+]], [[TYPE0]], [[TYPE6:![0-9]+]], !17, [[TYPE7:![0-9]+]], [[TYPE2]], [[TYPE8:![0-9]+]]}
|
||||
// CHECK: [[TYPE4]] = !DIDerivedType(tag: DW_TAG_typedef, name: "my_int"
|
||||
// CHECK: [[TYPE5]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "foo"
|
||||
// CHECK: [[TYPE6]] = distinct !DICompositeType(tag: DW_TAG_union_type, name: "baz"
|
||||
// CHECK: [[TYPE7]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "y"
|
||||
// CHECK: [[TYPE8]] = distinct !DICompositeType(tag: DW_TAG_union_type, name: "w"
|
||||
|
||||
// Check that debug info is not emitted for the typedef, struct, enum, and
|
||||
// union when -fno-eliminate-unused-debug-types and -g are not set. These are
|
||||
// the same checks as above with `NODBG-NOT` rather than `CHECK`.
|
||||
|
||||
// NODBG-NOT: !DI{{CompositeType|Enumerator|DerivedType}}
|
||||
|
||||
// Check that debug info is not emitted for declarations. Obnoxious
|
||||
// indentifiers are to avoid collisions with the SHA emittied as debug info.
|
||||
struct unused_struct;
|
||||
enum unused_enum;
|
||||
union unused_union;
|
||||
void b0(void) {
|
||||
struct unused_local_struct;
|
||||
enum unused_local_enum;
|
||||
union unused_local_union;
|
||||
}
|
||||
|
||||
// NODBG-NOT: name: "unused_
|
|
@ -0,0 +1,31 @@
|
|||
// RUN: %clang++ -fno-eliminate-unused-debug-types -g -emit-llvm -S -o - %s | FileCheck %s
|
||||
// RUN: %clang++ -fno-eliminate-unused-debug-types -g1 -emit-llvm -S -o - %s | FileCheck --check-prefix=NODBG %s
|
||||
// RUN: %clang++ -feliminate-unused-debug-types -g -emit-llvm -S -o - %s | FileCheck --check-prefix=NODBG %s
|
||||
// RUN: %clang++ -g -emit-llvm -S -o - %s | FileCheck --check-prefix=NODBG %s
|
||||
// RUN: %clang++ -emit-llvm -S -o - %s | FileCheck --check-prefix=NODBG %s
|
||||
using foo = int;
|
||||
class bar {};
|
||||
enum class baz { BAZ };
|
||||
|
||||
void quux() {
|
||||
using x = int;
|
||||
class y {};
|
||||
enum class z { Z };
|
||||
}
|
||||
|
||||
// CHECK: !DICompileUnit{{.+}}retainedTypes: [[RETTYPES:![0-9]+]]
|
||||
// CHECK: [[TYPE0:![0-9]+]] = !DICompositeType(tag: DW_TAG_enumeration_type, name: "baz"
|
||||
// CHECK: [[TYPE1:![0-9]+]] = !DIEnumerator(name: "BAZ"
|
||||
// CHECK: [[TYPE2:![0-9]+]] = !DICompositeType(tag: DW_TAG_enumeration_type, name: "z"
|
||||
// CHECK: [[TYPE3:![0-9]+]] = !DIEnumerator(name: "Z"
|
||||
// CHECK: [[RETTYPES]] = !{[[TYPE4:![0-9]+]], [[TYPE5:![0-9]+]], [[TYPE0]], !5, [[TYPE6:![0-9]+]], [[TYPE2]]}
|
||||
// CHECK: [[TYPE4]] = !DIDerivedType(tag: DW_TAG_typedef, name: "foo"
|
||||
// CHECK: [[TYPE5]] = distinct !DICompositeType(tag: DW_TAG_class_type, name: "bar"
|
||||
// CHECK: [[TYPE6]] = distinct !DICompositeType(tag: DW_TAG_class_type, name: "y"
|
||||
|
||||
// NODBG-NOT: !DI{{CompositeType|Enumerator|DerivedType}}
|
||||
|
||||
class unused_class;
|
||||
enum class unused_enum_class;
|
||||
|
||||
// NODBG-NOT: name: "unused_
|
|
@ -361,3 +361,12 @@
|
|||
// GEMBED_2: error: invalid argument '-gembed-source' only allowed with '-gdwarf-5'
|
||||
// NOGEMBED_5-NOT: "-gembed-source"
|
||||
// NOGEMBED_2-NOT: error: invalid argument '-gembed-source' only allowed with '-gdwarf-5'
|
||||
//
|
||||
// RUN: %clang -### -g -fno-eliminate-unused-debug-types -c %s 2>&1 \
|
||||
// RUN: | FileCheck -check-prefix=DEBUG_UNUSED_TYPES %s
|
||||
// DEBUG_UNUSED_TYPES: "-debug-info-kind=unused-types"
|
||||
// DEBUG_UNUSED_TYPES-NOT: "-debug-info-kind=limited"
|
||||
// RUN: %clang -### -g -feliminate-unused-debug-types -c %s 2>&1 \
|
||||
// RUN: | FileCheck -check-prefix=NO_DEBUG_UNUSED_TYPES %s
|
||||
// NO_DEBUG_UNUSED_TYPES: "-debug-info-kind=limited"
|
||||
// NO_DEBUG_UNUSED_TYPES-NOT: "-debug-info-kind=unused-types"
|
||||
|
|
Loading…
Reference in New Issue