[cfe-dev] clang CL breaks LLDB std::string printing

Vince Harron vharron at google.com
Wed Feb 25 22:31:47 PST 2015


Hi David,

There are some LLDB tests that have been failing against clang-3.6 for a
long time and just started failing in clang-3.5 when my Ubuntu distro
updated clang-3.5.

I tracked it back to a clang CL that you submitted nearly a year ago.

This test passes when compiling with gcc 4.8.2 and clang-3.5 before this
CL.  I'm very new to the project and I don't really understand what's going
on here.  Any guidance you can offer would be very much appreciated.

Thanks,

Vince

------------------------------------------------------------------------
r202769 | dblaikie | 2014-03-03 15:48:23 -0800 (Mon, 03 Mar 2014) | 18 lines

DebugInfo: Emit only the declaration of a class template that has an
explicit instantiation declaration (& always emit such a type when there's
an explicit instantiation definition)

We should only have this optimization fire when the explicit
instantiation definition would cause at  least one member function to be
emitted, thus ensuring that even a compiler not performing this
optimization would still emit the full type information elsewhere.

But we should also pessimize output still by always emitting the
definition when the explicit instantiation definition appears so that at
some point in the future we can depend on that information even when no
code had to be emitted in that TU. (this shouldn't happen very often,
since people mostly use explicit spec decl/defs to reduce code size -
but perhaps one day they could use it to explicitly reduce debug info
size too)

This was worth about 2% for Clang and LLVM - so not a huge win, but a
win. It looks really great for simple STL programs (include <string> and
just declare a string - 14k -> 1.4k of .dwo)





-- 

Vince Harron | Technical Lead Manager | vharron at google.com | 858-442-0868
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20150226/1294fdbf/attachment.html>
-------------- next part --------------
Index: test/CodeGenCXX/debug-info-template-explicit-specialization.cpp
===================================================================
--- test/CodeGenCXX/debug-info-template-explicit-specialization.cpp	(revision 0)
+++ test/CodeGenCXX/debug-info-template-explicit-specialization.cpp	(revision 202769)
@@ -0,0 +1,72 @@
+// RUN: %clang_cc1 -S -emit-llvm -g %s -o - | FileCheck %s
+
+template <typename T>
+struct a {
+};
+extern template class a<int>;
+// CHECK-NOT: ; [ DW_TAG_structure_type ] [a<int>]
+
+template <typename T>
+struct b {
+};
+extern template class b<int>;
+b<int> bi;
+// CHECK: ; [ DW_TAG_structure_type ] [b<int>] {{.*}} [def]
+
+template <typename T>
+struct c {
+  void f() {}
+};
+extern template class c<int>;
+c<int> ci;
+// CHECK: ; [ DW_TAG_structure_type ] [c<int>] {{.*}} [decl]
+
+template <typename T>
+struct d {
+  void f();
+};
+extern template class d<int>;
+d<int> di;
+// CHECK: ; [ DW_TAG_structure_type ] [d<int>] {{.*}} [def]
+
+template <typename T>
+struct e {
+  void f();
+};
+template <typename T>
+void e<T>::f() {
+}
+extern template class e<int>;
+e<int> ei;
+// CHECK: ; [ DW_TAG_structure_type ] [e<int>] {{.*}} [decl]
+
+template <typename T>
+struct f {
+  void g();
+};
+extern template class f<int>;
+template <typename T>
+void f<T>::g() {
+}
+f<int> fi;
+// Is this right? We don't seem to emit a def for 'f<int>::g' (even if it is
+// called in this translation unit) so I guess if we're relying on its
+// definition to be wherever the explicit instantiation definition is, we can do
+// the same for the debug info.
+// CHECK: ; [ DW_TAG_structure_type ] [f<int>] {{.*}} [decl]
+
+template <typename T>
+struct g {
+  void f();
+};
+template <>
+void g<int>::f();
+extern template class g<int>;
+g<int> gi;
+// CHECK: ; [ DW_TAG_structure_type ] [g<int>] {{.*}} [def]
+
+template <typename T>
+struct h {
+};
+template class h<int>;
+// CHECK: ; [ DW_TAG_structure_type ] [h<int>] {{.*}} [def]
Index: lib/CodeGen/CGDebugInfo.h
===================================================================
--- lib/CodeGen/CGDebugInfo.h	(revision 202768)
+++ lib/CodeGen/CGDebugInfo.h	(working copy)
@@ -288,6 +288,8 @@
   void completeRequiredType(const RecordDecl *RD);
   void completeClassData(const RecordDecl *RD);
 
+  void completeTemplateDefinition(const ClassTemplateSpecializationDecl &SD);
+
 private:
   /// EmitDeclare - Emit call to llvm.dbg.declare for a variable declaration.
   void EmitDeclare(const VarDecl *decl, unsigned Tag, llvm::Value *AI,
Index: lib/CodeGen/CodeGenModule.cpp
===================================================================
--- lib/CodeGen/CodeGenModule.cpp	(revision 202768)
+++ lib/CodeGen/CodeGenModule.cpp	(working copy)
@@ -3000,8 +3000,16 @@
 
     ImportedModules.insert(Import->getImportedModule());
     break;
- }
+  }
 
+  case Decl::ClassTemplateSpecialization: {
+    const ClassTemplateSpecializationDecl *Spec =
+        cast<ClassTemplateSpecializationDecl>(D);
+    if (DebugInfo &&
+        Spec->getSpecializationKind() == TSK_ExplicitInstantiationDefinition)
+      DebugInfo->completeTemplateDefinition(*Spec);
+  }
+
   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
Index: lib/CodeGen/CGDebugInfo.cpp
===================================================================
--- lib/CodeGen/CGDebugInfo.cpp	(revision 202768)
+++ lib/CodeGen/CGDebugInfo.cpp	(working copy)
@@ -1458,28 +1458,53 @@
   TypeCache[TyPtr] = Res;
 }
 
+static bool hasExplicitMemberDefinition(CXXRecordDecl::method_iterator I,
+                                        CXXRecordDecl::method_iterator End) {
+  for (; I != End; ++I)
+    if (FunctionDecl *Tmpl = I->getInstantiatedFromMemberFunction())
+      if (!Tmpl->isImplicit() && Tmpl->hasBody())
+        return true;
+  return false;
+}
+
+static bool shouldOmitDefinition(CodeGenOptions::DebugInfoKind DebugKind,
+                                 const RecordDecl *RD,
+                                 const LangOptions &LangOpts) {
+  if (DebugKind > CodeGenOptions::LimitedDebugInfo)
+    return false;
+
+  if (!LangOpts.CPlusPlus)
+    return false;
+
+  if (!RD->isCompleteDefinitionRequired())
+    return true;
+
+  const CXXRecordDecl *CXXDecl = dyn_cast<CXXRecordDecl>(RD);
+
+  if (!CXXDecl)
+    return false;
+
+  if (CXXDecl->hasDefinition() && CXXDecl->isDynamicClass())
+    return true;
+
+  TemplateSpecializationKind Spec = TSK_Undeclared;
+  if (const ClassTemplateSpecializationDecl *SD =
+          dyn_cast<ClassTemplateSpecializationDecl>(RD))
+    Spec = SD->getSpecializationKind();
+
+  if (Spec == TSK_ExplicitInstantiationDeclaration &&
+      hasExplicitMemberDefinition(CXXDecl->method_begin(),
+                                  CXXDecl->method_end()))
+    return true;
+
+  return false;
+}
+
 /// CreateType - get structure or union type.
 llvm::DIType CGDebugInfo::CreateType(const RecordType *Ty) {
   RecordDecl *RD = Ty->getDecl();
-  const CXXRecordDecl *CXXDecl = dyn_cast<CXXRecordDecl>(RD);
-  // Always emit declarations for types that aren't required to be complete when
-  // in limit-debug-info mode. If the type is later found to be required to be
-  // complete this declaration will be upgraded to a definition by
-  // `completeRequiredType`.
-  // If the type is dynamic, only emit the definition in TUs that require class
-  // data. This is handled by `completeClassData`.
   llvm::DICompositeType T(getTypeOrNull(QualType(Ty, 0)));
-  // If we've already emitted the type, just use that, even if it's only a
-  // declaration. The completeType, completeRequiredType, and completeClassData
-  // callbacks will handle promoting the declaration to a definition.
-  if (T ||
-      // Under -fno-standalone-debug:
-      (DebugKind <= CodeGenOptions::LimitedDebugInfo &&
-       // Emit only a forward declaration unless the type is required.
-       ((!RD->isCompleteDefinitionRequired() && CGM.getLangOpts().CPlusPlus) ||
-        // If the class is dynamic, only emit a declaration. A definition will
-        // be emitted whenever the vtable is emitted.
-        (CXXDecl && CXXDecl->hasDefinition() && CXXDecl->isDynamicClass())))) {
+  if (T || shouldOmitDefinition(DebugKind, RD, CGM.getLangOpts())) {
     if (!T)
       T = getOrCreateRecordFwdDecl(
           Ty, getContextDescriptor(cast<Decl>(RD->getDeclContext())));
@@ -2014,6 +2039,14 @@
   return llvm::DIType(cast_or_null<llvm::MDNode>(V));
 }
 
+void CGDebugInfo::completeTemplateDefinition(
+    const ClassTemplateSpecializationDecl &SD) {
+  completeClassData(&SD);
+  // In case this type has no member function definitions being emitted, ensure
+  // it is retained
+  RetainedTypes.push_back(CGM.getContext().getRecordType(&SD).getAsOpaquePtr());
+}
+
 /// getCachedInterfaceTypeOrNull - Get the type from the interface
 /// cache, unless it needs to regenerated. Otherwise return null.
 llvm::Value *CGDebugInfo::getCachedInterfaceTypeOrNull(QualType Ty) {


More information about the cfe-dev mailing list