forked from OSchip/llvm-project
Extend the const capture optimization to C++ record types with no
mutable fields and with trivial destructors and copy constructors. llvm-svn: 125073
This commit is contained in:
parent
7012049c7f
commit
b0a3ecb41c
|
@ -201,6 +201,42 @@ namespace {
|
|||
}
|
||||
}
|
||||
|
||||
/// 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 @@ static llvm::Constant *tryCaptureAsConstant(CodeGenModule &CGM,
|
|||
// 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
|
||||
|
|
|
@ -8,3 +8,50 @@ namespace test0 {
|
|||
^{ ^{ (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.
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue