[cfe-commits] r125073 - in /cfe/trunk: lib/CodeGen/CGBlocks.cpp test/CodeGenCXX/blocks.cpp

John McCall rjmccall at apple.com
Mon Feb 7 19:07:00 PST 2011


Author: rjmccall
Date: Mon Feb  7 21:07:00 2011
New Revision: 125073

URL: http://llvm.org/viewvc/llvm-project?rev=125073&view=rev
Log:
Extend the const capture optimization to C++ record types with no
mutable fields and with trivial destructors and copy constructors.


Modified:
    cfe/trunk/lib/CodeGen/CGBlocks.cpp
    cfe/trunk/test/CodeGenCXX/blocks.cpp

Modified: cfe/trunk/lib/CodeGen/CGBlocks.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGBlocks.cpp?rev=125073&r1=125072&r2=125073&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGBlocks.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGBlocks.cpp Mon Feb  7 21:07:00 2011
@@ -201,6 +201,42 @@
   }
 }
 
+/// Determines if the given record type has a mutable field.
+static bool hasMutableField(const CXXRecordDecl *record) {
+  for (CXXRecordDecl::field_iterator
+         i = record->field_begin(), e = record->field_end(); i != e; ++i)
+    if ((*i)->isMutable())
+      return true;
+
+  for (CXXRecordDecl::base_class_const_iterator
+         i = record->bases_begin(), e = record->bases_end(); i != e; ++i) {
+    const RecordType *record = i->getType()->castAs<RecordType>();
+    if (hasMutableField(cast<CXXRecordDecl>(record->getDecl())))
+      return true;
+  }
+
+  return false;
+}
+
+/// Determines if the given type is safe for constant capture in C++.
+static bool isSafeForCXXConstantCapture(QualType type) {
+  const RecordType *recordType =
+    type->getBaseElementTypeUnsafe()->getAs<RecordType>();
+
+  // Only records can be unsafe.
+  if (!recordType) return true;
+
+  const CXXRecordDecl *record = cast<CXXRecordDecl>(recordType->getDecl());
+
+  // Maintain semantics for classes with non-trivial dtors or copy ctors.
+  if (!record->hasTrivialDestructor()) return false;
+  if (!record->hasTrivialCopyConstructor()) return false;
+
+  // Otherwise, we just have to make sure there aren't any mutable
+  // fields that might have changed since initialization.
+  return !hasMutableField(record);
+}
+
 /// It is illegal to modify a const object after initialization.
 /// Therefore, if a const object has a constant initializer, we don't
 /// actually need to keep storage for it in the block; we'll just
@@ -214,11 +250,12 @@
   // We can only do this if the variable is const.
   if (!type.isConstQualified()) return 0;
 
-  // Furthermore, in C++ we can't do this for classes.  TODO: we might
-  // actually be able to get away with it for classes with a trivial
-  // destructor and a trivial copy constructor and no mutable fields.
-  if (CGM.getLangOptions().CPlusPlus &&
-      type->getBaseElementTypeUnsafe()->isRecordType())
+  // Furthermore, in C++ we have to worry about mutable fields:
+  // C++ [dcl.type.cv]p4:
+  //   Except that any class member declared mutable can be
+  //   modified, any attempt to modify a const object during its
+  //   lifetime results in undefined behavior.
+  if (CGM.getLangOptions().CPlusPlus && !isSafeForCXXConstantCapture(type))
     return 0;
 
   // If the variable doesn't have any initializer (shouldn't this be

Modified: cfe/trunk/test/CodeGenCXX/blocks.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/blocks.cpp?rev=125073&r1=125072&r2=125073&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenCXX/blocks.cpp (original)
+++ cfe/trunk/test/CodeGenCXX/blocks.cpp Mon Feb  7 21:07:00 2011
@@ -8,3 +8,50 @@
     ^{ ^{ (void) x; }; };
   }
 }
+
+extern void (^out)();
+
+namespace test1 {
+  // Capturing const objects doesn't require a local block.
+  // CHECK: define void @_ZN5test15test1Ev()
+  // CHECK:   store void ()* bitcast ({{.*}} @__block_literal_global{{.*}} to void ()*), void ()** @out
+  void test1() {
+    const int NumHorsemen = 4;
+    out = ^{ (void) NumHorsemen; };
+  }
+
+  // That applies to structs too...
+  // CHECK: define void @_ZN5test15test2Ev()
+  // CHECK:   store void ()* bitcast ({{.*}} @__block_literal_global{{.*}} to void ()*), void ()** @out
+  struct loc { double x, y; };
+  void test2() {
+    const loc target = { 5, 6 };
+    out = ^{ (void) target; };
+  }
+
+  // ...unless they have mutable fields...
+  // CHECK: define void @_ZN5test15test3Ev()
+  // CHECK:   [[BLOCK:%.*]] = alloca [[BLOCK_T:%.*]],
+  // CHECK:   [[T0:%.*]] = bitcast [[BLOCK_T]]* [[BLOCK]] to void ()*
+  // CHECK:   store void ()* [[T0]], void ()** @out
+  struct mut { mutable int x; };
+  void test3() {
+    const mut obj = { 5 };
+    out = ^{ (void) obj; };
+  }
+
+  // ...or non-trivial destructors...
+  // CHECK: define void @_ZN5test15test4Ev()
+  // CHECK:   [[OBJ:%.*]] = alloca
+  // CHECK:   [[BLOCK:%.*]] = alloca [[BLOCK_T:%.*]],
+  // CHECK:   [[T0:%.*]] = bitcast [[BLOCK_T]]* [[BLOCK]] to void ()*
+  // CHECK:   store void ()* [[T0]], void ()** @out
+  struct scope { int x; ~scope(); };
+  void test4() {
+    const scope obj = { 5 };
+    out = ^{ (void) obj; };
+  }
+
+  // ...or non-trivial copy constructors, but it's not clear how to do
+  // that and still have a constant initializer in '03.
+}





More information about the cfe-commits mailing list