Author: Nick Desaulniers Date: 2020-08-07T14:13:48-07:00 New Revision: e486921fd6cf96ae9114adac455f7c0b5c1088a7
URL: https://github.com/llvm/llvm-project/commit/e486921fd6cf96ae9114adac455f7c0b5c1088a7 DIFF: https://github.com/llvm/llvm-project/commit/e486921fd6cf96ae9114adac455f7c0b5c1088a7.diff LOG: [Clang] implement -fno-eliminate-unused-debug-types Fixes pr/11710. Signed-off-by: Nick Desaulniers <ndesaulni...@google.com> Reviewed By: dblaikie Differential Revision: https://reviews.llvm.org/D80242 Added: clang/test/CodeGen/debug-info-unused-types.c clang/test/CodeGen/debug-info-unused-types.cpp Modified: clang/docs/ClangCommandLineReference.rst clang/docs/CommandGuide/clang.rst clang/docs/UsersManual.rst clang/include/clang/Basic/CodeGenOptions.def clang/include/clang/Basic/CodeGenOptions.h clang/include/clang/Basic/DebugInfoOptions.h clang/include/clang/Driver/Options.td clang/lib/CodeGen/CGDebugInfo.cpp clang/lib/CodeGen/CGDebugInfo.h clang/lib/CodeGen/CGDecl.cpp clang/lib/CodeGen/CodeGenModule.cpp clang/lib/Driver/ToolChains/Clang.cpp clang/lib/Frontend/CompilerInvocation.cpp clang/test/Driver/debug-options.c Removed: ################################################################################ diff --git a/clang/docs/ClangCommandLineReference.rst b/clang/docs/ClangCommandLineReference.rst index 4caa08a82a72..370f13d6c955 100644 --- a/clang/docs/ClangCommandLineReference.rst +++ b/clang/docs/ClangCommandLineReference.rst @@ -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 diff --git a/clang/docs/CommandGuide/clang.rst b/clang/docs/CommandGuide/clang.rst index 2cca04fb31f1..394bd1be24e8 100644 --- a/clang/docs/CommandGuide/clang.rst +++ b/clang/docs/CommandGuide/clang.rst @@ -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 diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst index 8615a77596b4..19009b43a9e8 100644 --- a/clang/docs/UsersManual.rst +++ b/clang/docs/UsersManual.rst @@ -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 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def index c7e01eb12851..cbd9df998e78 100644 --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -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) diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h index 83c4463c3639..ca391bf8f186 100644 --- a/clang/include/clang/Basic/CodeGenOptions.h +++ b/clang/include/clang/Basic/CodeGenOptions.h @@ -388,6 +388,11 @@ class CodeGenOptions : public CodeGenOptionsBase { 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 diff --git a/clang/include/clang/Basic/DebugInfoOptions.h b/clang/include/clang/Basic/DebugInfoOptions.h index 586168bd015f..7f5669c1760f 100644 --- a/clang/include/clang/Basic/DebugInfoOptions.h +++ b/clang/include/clang/Basic/DebugInfoOptions.h @@ -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 diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index fc31c23e4240..e09b6468eea7 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -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>; diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index 461f2eee965d..2faf944d07d1 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -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) { diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h index 96ef6c7c1d27..59fe7929ef88 100644 --- a/clang/lib/CodeGen/CGDebugInfo.h +++ b/clang/lib/CodeGen/CGDebugInfo.h @@ -490,6 +490,9 @@ class CGDebugInfo { /// 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); diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index 1729c7ed3c31..0ad5050fabbb 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -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::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::CXXRecord: // struct/union/class X; [C++] 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; } } diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index f3712ea1f541..67f06ac1c07c 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -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 diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 68e4eb0eedda..ff252998e6f5 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -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)) { diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 8e8bf9d9028e..c3c8f3b9c6a9 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -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) diff --git a/clang/test/CodeGen/debug-info-unused-types.c b/clang/test/CodeGen/debug-info-unused-types.c new file mode 100644 index 000000000000..9ecd39411808 --- /dev/null +++ b/clang/test/CodeGen/debug-info-unused-types.c @@ -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_ diff --git a/clang/test/CodeGen/debug-info-unused-types.cpp b/clang/test/CodeGen/debug-info-unused-types.cpp new file mode 100644 index 000000000000..ebe1e94624b6 --- /dev/null +++ b/clang/test/CodeGen/debug-info-unused-types.cpp @@ -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_ diff --git a/clang/test/Driver/debug-options.c b/clang/test/Driver/debug-options.c index 189c1f9addeb..676e2ce2f3bb 100644 --- a/clang/test/Driver/debug-options.c +++ b/clang/test/Driver/debug-options.c @@ -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" _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits