diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index 1ae2c57b97b745ed10dcaf4227711c816116b7f8..1f86b09cc978a939c10c834e01efd30b4a6eff0a 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -973,7 +973,7 @@ def NoCommon : InheritableAttr { def NoDebug : InheritableAttr { let Spellings = [GCC<"nodebug">]; - let Documentation = [Undocumented]; + let Documentation = [NoDebugDocs]; } def NoDuplicate : InheritableAttr { diff --git a/include/clang/Basic/AttrDocs.td b/include/clang/Basic/AttrDocs.td index a67fd75600b9066abb6d8b69466d705cb24db1a0..b1b9e444383124c9d9907fe65666d5fb4f2b3028 100644 --- a/include/clang/Basic/AttrDocs.td +++ b/include/clang/Basic/AttrDocs.td @@ -494,6 +494,15 @@ Query for this feature with ``__has_attribute(objc_method_family)``. }]; } +def NoDebugDocs : Documentation { + let Category = DocCatVariable; + let Content = [{ +The ``nodebug`` attribute allows you to suppress debugging information for a +function, or for a variable declared with static storage duration, such as +globals, class static data members, and static locals. + }]; +} + def NoDuplicateDocs : Documentation { let Category = DocCatFunction; let Content = [{ diff --git a/lib/CodeGen/CGDebugInfo.cpp b/lib/CodeGen/CGDebugInfo.cpp index b44ddc4d1878d8d90ab5ed1c8d60a639ce4ebb1e..29f597112c1c7b71ca21f394c164a84fcf386971 100644 --- a/lib/CodeGen/CGDebugInfo.cpp +++ b/lib/CodeGen/CGDebugInfo.cpp @@ -1014,6 +1014,8 @@ void CGDebugInfo::CollectRecordFields( // the corresponding declarations in the source program. for (const auto *I : record->decls()) if (const auto *V = dyn_cast<VarDecl>(I)) { + if (V->hasAttr<NoDebugAttr>()) + continue; // Reuse the existing static member declaration if one exists auto MI = StaticDataMemberCache.find(V->getCanonicalDecl()); if (MI != StaticDataMemberCache.end()) { @@ -3390,6 +3392,8 @@ llvm::DIGlobalVariable *CGDebugInfo::CollectAnonRecordDecls( void CGDebugInfo::EmitGlobalVariable(llvm::GlobalVariable *Var, const VarDecl *D) { assert(DebugKind >= codegenoptions::LimitedDebugInfo); + if (D->hasAttr<NoDebugAttr>()) + return; // Create global variable debug descriptor. llvm::DIFile *Unit = nullptr; llvm::DIScope *DContext = nullptr; @@ -3422,6 +3426,8 @@ void CGDebugInfo::EmitGlobalVariable(llvm::GlobalVariable *Var, void CGDebugInfo::EmitGlobalVariable(const ValueDecl *VD, llvm::Constant *Init) { assert(DebugKind >= codegenoptions::LimitedDebugInfo); + if (VD->hasAttr<NoDebugAttr>()) + return; // Create the descriptor for the variable. llvm::DIFile *Unit = getOrCreateFile(VD->getLocation()); StringRef Name = VD->getName(); diff --git a/test/CodeGenCXX/debug-info-nodebug.cpp b/test/CodeGenCXX/debug-info-nodebug.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e2f5fb6da851cfdde3eb442f873b39f4f9a9de0a --- /dev/null +++ b/test/CodeGenCXX/debug-info-nodebug.cpp @@ -0,0 +1,55 @@ +// RUN: %clang_cc1 -DSETNODEBUG=0 -emit-llvm -debug-info-kind=limited %s -o - | FileCheck %s --check-prefix=YESINFO +// RUN: %clang_cc1 -DSETNODEBUG=1 -emit-llvm -debug-info-kind=limited %s -o - | FileCheck %s --check-prefix=NOINFO + +#if SETNODEBUG +#define NODEBUG __attribute__((nodebug)) +#else +#define NODEBUG +#endif + +// Simple global variable declaration and definition. +// Use the declaration so it gets emitted. +NODEBUG int global_int_decl; +NODEBUG int global_int_def = global_int_decl; +// YESINFO-DAG: !DIGlobalVariable(name: "global_int_decl" +// NOINFO-NOT: !DIGlobalVariable(name: "global_int_decl" +// YESINFO-DAG: !DIGlobalVariable(name: "global_int_def" +// NOINFO-NOT: !DIGlobalVariable(name: "global_int_def" + +// Const global variable. Use it so it gets emitted. +NODEBUG static const int const_global_int_def = 1; +void func1(int); +void func2() { func1(const_global_int_def); } +// YESINFO-DAG: !DIGlobalVariable(name: "const_global_int_def" +// NOINFO-NOT: !DIGlobalVariable(name: "const_global_int_def" + +// Global variable with a more involved type. +// If the variable has no debug info, the type should not appear either. +struct S1 { + int a; + int b; +}; +NODEBUG S1 global_struct = { 2, 3 }; +// YESINFO-DAG: !DICompositeType({{.*}} name: "S1" +// NOINFO-NOT: !DICompositeType({{.*}} name: "S1" +// YESINFO-DAG: !DIGlobalVariable(name: "global_struct" +// NOINFO-NOT: !DIGlobalVariable(name: "global_struct" + +// Static data members. Const member needs a use. +struct S2 { + NODEBUG static int static_member; + NODEBUG static const int static_const_member = 4; +}; +int S2::static_member = 5; +int junk = S2::static_const_member; +// YESINFO-DAG: !DIGlobalVariable(name: "static_member" +// NOINFO-NOT: !DIGlobalVariable(name: "static_member" +// YESINFO-DAG: !DIDerivedType({{.*}} name: "static_const_member" +// NOINFO-NOT: !DIDerivedType({{.*}} name: "static_const_member" + +// Function-local static variable. +void func3() { + NODEBUG static int func_local = 6; +} +// YESINFO-DAG: !DIGlobalVariable(name: "func_local" +// NOINFO-NOT: !DIGlobalVariable(name: "func_local"