[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