Set a special flag in class metadata when an Objective-C class
authorJohn McCall <rjmccall@apple.com>
Wed, 17 Oct 2012 04:53:31 +0000 (04:53 +0000)
committerJohn McCall <rjmccall@apple.com>
Wed, 17 Oct 2012 04:53:31 +0000 (04:53 +0000)
has ivars that require destruction, but none that require anything
except zero-initialization.  This is common in ARC and (when true
throughout a class hierarchy) permits the elimination of an
unnecessary message-send during allocation.

llvm-svn: 166088

clang/include/clang/AST/DeclObjC.h
clang/lib/CodeGen/CGObjCMac.cpp
clang/lib/CodeGen/CodeGenModule.cpp
clang/lib/Serialization/ASTReaderDecl.cpp
clang/lib/Serialization/ASTWriterDecl.cpp
clang/test/CodeGenObjC/arc.m

index 99dd293..3372b8a 100644 (file)
@@ -1581,8 +1581,12 @@ class ObjCImplementationDecl : public ObjCImplDecl {
   CXXCtorInitializer **IvarInitializers;
   unsigned NumIvarInitializers;
 
-  /// true if class has a .cxx_[construct,destruct] method.
-  bool HasCXXStructors : 1;
+  /// Do the ivars of this class require initialization other than
+  /// zero-initialization?
+  bool HasNonZeroConstructors : 1;
+
+  /// Do the ivars of this class require non-trivial destruction?
+  bool HasDestructors : 1;
 
   ObjCImplementationDecl(DeclContext *DC,
                          ObjCInterfaceDecl *classInterface,
@@ -1594,7 +1598,7 @@ class ObjCImplementationDecl : public ObjCImplDecl {
        SuperClass(superDecl), IvarLBraceLoc(IvarLBraceLoc), 
        IvarRBraceLoc(IvarRBraceLoc),
        IvarInitializers(0), NumIvarInitializers(0),
-       HasCXXStructors(false) {}
+       HasNonZeroConstructors(false), HasDestructors(false) {}
 public:
   static ObjCImplementationDecl *Create(ASTContext &C, DeclContext *DC,
                                         ObjCInterfaceDecl *classInterface,
@@ -1638,8 +1642,15 @@ public:
                            CXXCtorInitializer ** initializers,
                            unsigned numInitializers);
 
-  bool hasCXXStructors() const { return HasCXXStructors; }
-  void setHasCXXStructors(bool val) { HasCXXStructors = val; }
+  /// Do any of the ivars of this class (not counting its base classes)
+  /// require construction other than zero-initialization?
+  bool hasNonZeroConstructors() const { return HasNonZeroConstructors; }
+  void setHasNonZeroConstructors(bool val) { HasNonZeroConstructors = val; }
+
+  /// Do any of the ivars of this class (not counting its base classes)
+  /// require non-trivial destruction?
+  bool hasDestructors() const { return HasDestructors; }
+  void setHasDestructors(bool val) { HasDestructors = val; }
 
   /// getIdentifier - Get the identifier that names the class
   /// interface associated with this implementation.
index 01179cc..9954851 100644 (file)
@@ -2367,7 +2367,10 @@ enum NonFragileClassFlags {
   NonFragileABI_Class_HasIvarReleaser      = 0x00040,
 
   /// Class implementation was compiled under ARC.
-  NonFragileABI_Class_CompiledByARC        = 0x00080
+  NonFragileABI_Class_CompiledByARC        = 0x00080,
+
+  /// Class has non-trivial destructors, but zero-initialization is okay.
+  NonFragileABI_Class_HasCXXDestructorOnly = 0x00100
 };
 
 /*
@@ -2401,7 +2404,7 @@ void CGObjCMac::GenerateClass(const ObjCImplementationDecl *ID) {
                      Interface->all_referenced_protocol_begin(),
                      Interface->all_referenced_protocol_end());
   unsigned Flags = FragileABI_Class_Factory;
-  if (ID->hasCXXStructors())
+  if (ID->hasNonZeroConstructors() || ID->hasDestructors())
     Flags |= FragileABI_Class_HasCXXStructors;
   unsigned Size =
     CGM.getContext().getASTObjCImplementationLayout(ID).getSize().getQuantity();
@@ -5154,12 +5157,20 @@ void CGObjCNonFragileABIMac::GenerateClass(const ObjCImplementationDecl *ID) {
 
   llvm::GlobalVariable *SuperClassGV, *IsAGV;
 
+  // Build the flags for the metaclass.
   bool classIsHidden =
     ID->getClassInterface()->getVisibility() == HiddenVisibility;
   if (classIsHidden)
     flags |= NonFragileABI_Class_Hidden;
-  if (ID->hasCXXStructors())
+
+  // FIXME: why is this flag set on the metaclass?
+  // ObjC metaclasses have no fields and don't really get constructed.
+  if (ID->hasNonZeroConstructors() || ID->hasDestructors()) {
     flags |= NonFragileABI_Class_HasCXXStructors;
+    if (!ID->hasNonZeroConstructors())
+      flags |= NonFragileABI_Class_HasCXXDestructorOnly;  
+  }
+
   if (!ID->getClassInterface()->getSuperClass()) {
     // class is root
     flags |= NonFragileABI_Class_Root;
@@ -5194,9 +5205,20 @@ void CGObjCNonFragileABIMac::GenerateClass(const ObjCImplementationDecl *ID) {
   flags = 0;
   if (classIsHidden)
     flags |= NonFragileABI_Class_Hidden;
-  if (ID->hasCXXStructors())
+
+  if (ID->hasNonZeroConstructors() || ID->hasDestructors()) {
     flags |= NonFragileABI_Class_HasCXXStructors;
 
+    // Set a flag to enable a runtime optimization when a class has
+    // fields that require destruction but which don't require
+    // anything except zero-initialization during construction.  This
+    // is most notably true of __strong and __weak types, but you can
+    // also imagine there being C++ types with non-trivial default
+    // constructors that merely set all fields to null.
+    if (!ID->hasNonZeroConstructors())
+      flags |= NonFragileABI_Class_HasCXXDestructorOnly;
+  }
+
   if (hasObjCExceptionAttribute(CGM.getContext(), ID->getClassInterface()))
     flags |= NonFragileABI_Class_Exception;
 
index e9344a1..95753d5 100644 (file)
@@ -2541,7 +2541,7 @@ void CodeGenModule::EmitObjCIvarInitializations(ObjCImplementationDecl *D) {
                              /*isDefined=*/false, ObjCMethodDecl::Required);
     D->addInstanceMethod(DTORMethod);
     CodeGenFunction(*this).GenerateObjCCtorDtorMethod(D, DTORMethod, false);
-    D->setHasCXXStructors(true);
+    D->setHasDestructors(true);
   }
 
   // If the implementation doesn't have any ivar initializers, we don't need
@@ -2565,7 +2565,7 @@ void CodeGenModule::EmitObjCIvarInitializations(ObjCImplementationDecl *D) {
                                                 ObjCMethodDecl::Required);
   D->addInstanceMethod(CTORMethod);
   CodeGenFunction(*this).GenerateObjCCtorDtorMethod(D, CTORMethod, true);
-  D->setHasCXXStructors(true);
+  D->setHasNonZeroConstructors(true);
 }
 
 /// EmitNamespace - Emit all declarations in a namespace.
index d85cb4e..17509c8 100644 (file)
@@ -855,6 +855,8 @@ void ASTDeclReader::VisitObjCImplementationDecl(ObjCImplementationDecl *D) {
   D->setSuperClass(ReadDeclAs<ObjCInterfaceDecl>(Record, Idx));
   D->setIvarLBraceLoc(ReadSourceLocation(Record, Idx));
   D->setIvarRBraceLoc(ReadSourceLocation(Record, Idx));
+  D->setHasNonZeroConstructors(Record[Idx++]);
+  D->setHasDestructors(Record[Idx++]);
   llvm::tie(D->IvarInitializers, D->NumIvarInitializers)
       = Reader.ReadCXXCtorInitializers(F, Record, Idx);
 }
index 2270e8f..e84afba 100644 (file)
@@ -606,6 +606,8 @@ void ASTDeclWriter::VisitObjCImplementationDecl(ObjCImplementationDecl *D) {
   Writer.AddDeclRef(D->getSuperClass(), Record);
   Writer.AddSourceLocation(D->getIvarLBraceLoc(), Record);
   Writer.AddSourceLocation(D->getIvarRBraceLoc(), Record);
+  Record.push_back(D->hasNonZeroConstructors());
+  Record.push_back(D->hasDestructors());
   Writer.AddCXXCtorInitializers(D->IvarInitializers, D->NumIvarInitializers,
                                 Record);
   Code = serialization::DECL_OBJC_IMPLEMENTATION;
index d6bce5f..8e38019 100644 (file)
@@ -602,7 +602,10 @@ void test22(_Bool cond) {
 
 // rdar://problem/8922540
 //   Note that we no longer emit .release_ivars flags.
-// CHECK-GLOBALS: @"\01l_OBJC_CLASS_RO_$_Test23" = internal global [[RO_T:%.*]] { i32 134,
+// rdar://problem/12492434
+//   Note that we set the flag saying that we need destruction *and*
+//   the flag saying that we don't also need construction.
+// CHECK-GLOBALS: @"\01l_OBJC_CLASS_RO_$_Test23" = internal global [[RO_T:%.*]] { i32 390,
 @interface Test23 { id x; } @end
 @implementation Test23 @end