ahatanak updated this revision to Diff 129359.
ahatanak marked an inline comment as done.
ahatanak added a comment.
In EmitCallArg and EmitParmDecl, use the existing code for Microsoft C++ ABI to
determine whether the argument should be destructed in the callee. Also, add a
test case that checks the destructor for a non-trivial C struct is called on
the EH path.
https://reviews.llvm.org/D41228
Files:
docs/LanguageExtensions.rst
include/clang/AST/ASTContext.h
include/clang/AST/Decl.h
include/clang/AST/Type.h
include/clang/Basic/DiagnosticSemaKinds.td
lib/AST/ASTContext.cpp
lib/AST/Decl.cpp
lib/AST/Type.cpp
lib/CodeGen/CGBlocks.cpp
lib/CodeGen/CGCall.cpp
lib/CodeGen/CGDecl.cpp
lib/CodeGen/CGDeclCXX.cpp
lib/CodeGen/CGExprAgg.cpp
lib/CodeGen/CGNonTrivialStruct.cpp
lib/CodeGen/CMakeLists.txt
lib/CodeGen/CodeGenFunction.h
lib/Lex/PPMacroExpansion.cpp
lib/Sema/JumpDiagnostics.cpp
lib/Sema/SemaDecl.cpp
lib/Sema/SemaExpr.cpp
test/ARCMT/checking.m
test/CodeGenObjC/nontrivial-c-struct-exception.m
test/CodeGenObjC/strong-in-c-struct.m
test/Lexer/has_feature_objc_arc.m
test/SemaObjC/arc-decls.m
test/SemaObjC/arc-system-header.m
test/SemaObjC/strong-in-c-struct.m
Index: test/SemaObjC/strong-in-c-struct.m
===================================================================
--- /dev/null
+++ test/SemaObjC/strong-in-c-struct.m
@@ -0,0 +1,56 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios11 -fobjc-arc -fblocks -fobjc-runtime=ios-11.0 -fsyntax-only -verify %s
+
+typedef struct {
+ id a;
+} Strong;
+
+void callee_variadic(const char *, ...);
+
+void test_variadic(void) {
+ Strong t;
+ callee_variadic("s", t); // expected-error {{cannot pass non-trivial C object of type 'Strong' by value to variadic function}}
+}
+
+void test_jump0(int cond) {
+ switch (cond) {
+ case 0:
+ ;
+ Strong x; // expected-note {{jump bypasses initialization of variable of non-trivial C struct type}}
+ break;
+ case 1: // expected-error {{cannot jump from switch statement to this case label}}
+ x.a = 0;
+ break;
+ }
+}
+
+void test_jump1(void) {
+ static void *ips[] = { &&L0 };
+L0: // expected-note {{possible target of indirect goto}}
+ ;
+ Strong x; // expected-note {{jump exits scope of variable with non-trivial destructor}}
+ goto *ips; // expected-error {{cannot jump}}
+}
+
+typedef void (^BlockTy)(void);
+void func(BlockTy);
+void func2(Strong);
+
+void test_block_scope0(int cond) {
+ Strong x; // expected-note {{jump enters lifetime of block which captures a C struct that is non-trivial to destroy}}
+ switch (cond) {
+ case 0:
+ func(^{ func2(x); });
+ break;
+ default: // expected-error {{cannot jump from switch statement to this case label}}
+ break;
+ }
+}
+
+void test_block_scope1(void) {
+ static void *ips[] = { &&L0 };
+L0: // expected-note {{possible target of indirect goto}}
+ ;
+ Strong x; // expected-note {{jump exits scope of variable with non-trivial destructor}} expected-note {{jump exits lifetime of block which captures a C struct that is non-trivial to destroy}}
+ func(^{ func2(x); });
+ goto *ips; // expected-error {{cannot jump}}
+}
Index: test/SemaObjC/arc-system-header.m
===================================================================
--- test/SemaObjC/arc-system-header.m
+++ test/SemaObjC/arc-system-header.m
@@ -23,8 +23,7 @@
}
void test5(struct Test5 *p) {
- p->field = 0; // expected-error {{'field' is unavailable in ARC}}
- // [email protected]:25 {{field has non-trivial ownership qualification}}
+ p->field = 0;
}
id test6() {
@@ -49,8 +48,7 @@
extern void doSomething(Test9 arg);
void test9() {
- Test9 foo2 = {0, 0}; // expected-error {{'field' is unavailable in ARC}}
- // [email protected]:56 {{field has non-trivial ownership qualification}}
+ Test9 foo2 = {0, 0};
doSomething(foo2);
}
#endif
Index: test/SemaObjC/arc-decls.m
===================================================================
--- test/SemaObjC/arc-decls.m
+++ test/SemaObjC/arc-decls.m
@@ -3,7 +3,7 @@
// rdar://8843524
struct A {
- id x; // expected-error {{ARC forbids Objective-C objects in struct}}
+ id x;
};
union u {
@@ -13,7 +13,7 @@
@interface I {
struct A a;
struct B {
- id y[10][20]; // expected-error {{ARC forbids Objective-C objects in struct}}
+ id y[10][20];
id z;
} b;
@@ -23,7 +23,7 @@
// rdar://10260525
struct r10260525 {
- id (^block) (); // expected-error {{ARC forbids blocks in struct}}
+ id (^block) ();
};
struct S {
Index: test/Lexer/has_feature_objc_arc.m
===================================================================
--- test/Lexer/has_feature_objc_arc.m
+++ test/Lexer/has_feature_objc_arc.m
@@ -13,8 +13,16 @@
void no_objc_arc_weak_feature();
#endif
+#if __has_feature(objc_arc_fields)
+void has_objc_arc_fields();
+#else
+void no_objc_arc_fields();
+#endif
+
// CHECK-ARC: void has_objc_arc_feature();
// CHECK-ARC: void has_objc_arc_weak_feature();
+// CHECK-ARC: void has_objc_arc_fields();
// CHECK-ARCLITE: void has_objc_arc_feature();
// CHECK-ARCLITE: void no_objc_arc_weak_feature();
+// CHECK-ARCLITE: void has_objc_arc_fields();
Index: test/CodeGenObjC/strong-in-c-struct.m
===================================================================
--- /dev/null
+++ test/CodeGenObjC/strong-in-c-struct.m
@@ -0,0 +1,486 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios11 -fobjc-arc -fblocks -fobjc-runtime=ios-11.0 -emit-llvm -o - %s | FileCheck %s
+
+typedef void (^BlockTy)(void);
+
+typedef struct {
+ int a[4];
+} Trivial;
+
+typedef struct {
+ Trivial f0;
+ id f1;
+} Strong;
+
+typedef struct {
+ int i;
+ id f1;
+} StrongSmall;
+
+typedef struct {
+ Strong f0;
+ id f1;
+ double d;
+} StrongOuter;
+
+typedef struct {
+ int f0;
+ volatile id f1;
+} StrongVolatile;
+
+typedef struct {
+ BlockTy f0;
+} StrongBlock;
+
+typedef struct {
+ int i;
+ id f0[2][2];
+} IDArray;
+
+typedef struct {
+ double d;
+ Strong f0[2][2];
+} StructArray;
+
+typedef struct {
+ id f0;
+ int i : 9;
+} Bitfield0;
+
+typedef struct {
+ char c;
+ int i0 : 2;
+ int i1 : 4;
+ id f0;
+ int i2 : 31;
+ int i3 : 1;
+ id f1;
+ int : 0;
+ int a[3];
+ id f2;
+ double d;
+ int i4 : 1;
+} Bitfield1;
+
+StrongSmall getStrongSmall(void);
+StrongOuter getStrongOuter(void);
+void calleeStrongSmall(StrongSmall);
+void func(Strong *);
+
+// CHECK: define void @test_constructor_destructor_StrongOuter()
+// CHECK: %[[T:.*]] = alloca %[[STRUCT_STRONGOUTER:.*]], align 8
+// CHECK: %[[V0:.*]] = bitcast %[[STRUCT_STRONGOUTER]]* %[[T]] to i8**
+// CHECK: call void @__default_constructor_8_s128_s192(i8** %[[V0]])
+// CHECK: %[[V1:.*]] = bitcast %[[STRUCT_STRONGOUTER]]* %[[T]] to i8**
+// CHECK: call void @__destructor_8_s128_s192(i8** %[[V1]])
+// CHECK: ret void
+
+// CHECK: define linkonce_odr hidden void @__default_constructor_8_s128_s192(i8** %[[DST:.*]])
+// CHECK: %[[DST_ADDR:.*]] = alloca i8**, align 8
+// CHECK: store i8** %[[DST]], i8*** %[[DST_ADDR]], align 8
+// CHECK: %[[V0:.*]] = load i8**, i8*** %[[DST_ADDR]], align 8
+// CHECK: call void @__default_constructor_8_s128(i8** %[[V0]])
+// CHECK: %[[V1:.*]] = bitcast i8** %[[V0]] to i8*
+// CHECK: %[[V2:.*]] = getelementptr inbounds i8, i8* %[[V1]], i64 24
+// CHECK: %[[V3:.*]] = bitcast i8* %[[V2]] to i8**
+// CHECK: %[[V4:.*]] = bitcast i8** %[[V3]] to i8*
+// CHECK: call void @llvm.memset.p0i8.i64(i8* %[[V4]], i8 0, i64 8, i32 8, i1 false)
+// CHECK: ret void
+
+// CHECK: define linkonce_odr hidden void @__default_constructor_8_s128(i8** %[[DST:.*]])
+// CHECK: %[[DST_ADDR:.*]] = alloca i8**, align 8
+// CHECK: store i8** %[[DST]], i8*** %[[DST_ADDR]], align 8
+// CHECK: %[[V0:.*]] = load i8**, i8*** %[[DST_ADDR]], align 8
+// CHECK: %[[V1:.*]] = bitcast i8** %[[V0]] to i8*
+// CHECK: %[[V2:.*]] = getelementptr inbounds i8, i8* %[[V1]], i64 16
+// CHECK: %[[V3:.*]] = bitcast i8* %[[V2]] to i8**
+// CHECK: %[[V4:.*]] = bitcast i8** %[[V3]] to i8*
+// CHECK: call void @llvm.memset.p0i8.i64(i8* %[[V4]], i8 0, i64 8, i32 8, i1 false)
+// CHECK: ret void
+
+// CHECK: define linkonce_odr hidden void @__destructor_8_s128_s192(i8** %[[DST:.*]])
+// CHECK: %[[DST_ADDR:.*]] = alloca i8**, align 8
+// CHECK: store i8** %[[DST]], i8*** %[[DST_ADDR]], align 8
+// CHECK: %[[V0:.*]] = load i8**, i8*** %[[DST_ADDR]], align 8
+// CHECK: call void @__destructor_8_s128(i8** %[[V0]])
+// CHECK: %[[V1:.*]] = bitcast i8** %[[V0]] to i8*
+// CHECK: %[[V2:.*]] = getelementptr inbounds i8, i8* %[[V1]], i64 24
+// CHECK: %[[V3:.*]] = bitcast i8* %[[V2]] to i8**
+// CHECK: call void @objc_storeStrong(i8** %[[V3]], i8* null)
+// CHECK: ret void
+
+// CHECK: define linkonce_odr hidden void @__destructor_8_s128(i8** %[[DST:.*]])
+// CHECK: %[[DST_ADDR:.*]] = alloca i8**, align 8
+// CHECK: store i8** %[[DST]], i8*** %[[DST_ADDR]], align 8
+// CHECK: %[[V0:.*]] = load i8**, i8*** %[[DST_ADDR]], align 8
+// CHECK: %[[V1:.*]] = bitcast i8** %[[V0]] to i8*
+// CHECK: %[[V2:.*]] = getelementptr inbounds i8, i8* %[[V1]], i64 16
+// CHECK: %[[V3:.*]] = bitcast i8* %[[V2]] to i8**
+// CHECK: call void @objc_storeStrong(i8** %[[V3]], i8* null)
+// CHECK: ret void
+
+void test_constructor_destructor_StrongOuter(void) {
+ StrongOuter t;
+}
+
+// CHECK: define void @test_copy_constructor_StrongOuter(%[[STRUCT_STRONGOUTER:.*]]* %[[S:.*]])
+// CHECK: %[[S_ADDR:.*]] = alloca %[[STRUCT_STRONGOUTER]]*, align 8
+// CHECK: %[[T:.*]] = alloca %[[STRUCT_STRONGOUTER]], align 8
+// CHECK: store %[[STRUCT_STRONGOUTER]]* %[[S]], %[[STRUCT_STRONGOUTER]]** %[[S_ADDR]], align 8
+// CHECK: %[[V0:.*]] = load %[[STRUCT_STRONGOUTER]]*, %[[STRUCT_STRONGOUTER]]** %[[S_ADDR]], align 8
+// CHECK: %[[V1:.*]] = bitcast %[[STRUCT_STRONGOUTER]]* %[[T]] to i8**
+// CHECK: %[[V2:.*]] = bitcast %[[STRUCT_STRONGOUTER]]* %[[V0]] to i8**
+// CHECK: call void @__copy_constructor_8_8_t0w128_s128_s192_t256w64(i8** %[[V1]], i8** %[[V2]])
+// CHECK: %[[V3:.*]] = bitcast %[[STRUCT_STRONGOUTER]]* %[[T]] to i8**
+// CHECK: call void @__destructor_8_s128_s192(i8** %[[V3]])
+// CHECK: ret void
+
+// CHECK: define linkonce_odr hidden void @__copy_constructor_8_8_t0w128_s128_s192_t256w64(i8** %[[DST:.*]], i8** %[[SRC:.*]])
+// CHECK: %[[DST_ADDR:.*]] = alloca i8**, align 8
+// CHECK: %[[SRC_ADDR:.*]] = alloca i8**, align 8
+// CHECK: store i8** %[[DST]], i8*** %[[DST_ADDR]], align 8
+// CHECK: store i8** %[[SRC]], i8*** %[[SRC_ADDR]], align 8
+// CHECK: %[[V0:.*]] = load i8**, i8*** %[[DST_ADDR]], align 8
+// CHECK: %[[V1:.*]] = load i8**, i8*** %[[SRC_ADDR]], align 8
+// CHECK: call void @__copy_constructor_8_8_t0w128_s128(i8** %[[V0]], i8** %[[V1]])
+// CHECK: %[[V2:.*]] = bitcast i8** %[[V0]] to i8*
+// CHECK: %[[V3:.*]] = getelementptr inbounds i8, i8* %[[V2]], i64 24
+// CHECK: %[[V4:.*]] = bitcast i8* %[[V3]] to i8**
+// CHECK: %[[V5:.*]] = bitcast i8** %[[V1]] to i8*
+// CHECK: %[[V6:.*]] = getelementptr inbounds i8, i8* %[[V5]], i64 24
+// CHECK: %[[V7:.*]] = bitcast i8* %[[V6]] to i8**
+// CHECK: %[[V8:.*]] = load i8*, i8** %[[V7]], align 8
+// CHECK: %[[V9:.*]] = call i8* @objc_retain(i8* %[[V8]])
+// CHECK: store i8* %[[V9]], i8** %[[V4]], align 8
+// CHECK: %[[V10:.*]] = bitcast i8** %[[V0]] to i8*
+// CHECK: %[[V11:.*]] = getelementptr inbounds i8, i8* %[[V10]], i64 32
+// CHECK: %[[V12:.*]] = bitcast i8* %[[V11]] to i8**
+// CHECK: %[[V13:.*]] = bitcast i8** %[[V1]] to i8*
+// CHECK: %[[V14:.*]] = getelementptr inbounds i8, i8* %[[V13]], i64 32
+// CHECK: %[[V15:.*]] = bitcast i8* %[[V14]] to i8**
+// CHECK: %[[V16:.*]] = bitcast i8** %[[V12]] to i64*
+// CHECK: %[[V17:.*]] = bitcast i8** %[[V15]] to i64*
+// CHECK: %[[V18:.*]] = load i64, i64* %[[V17]], align 8
+// CHECK: store i64 %[[V18]], i64* %[[V16]], align 8
+// CHECK: ret void
+
+// CHECK: define linkonce_odr hidden void @__copy_constructor_8_8_t0w128_s128(i8** %[[DST:.*]], i8** %[[SRC:.*]])
+// CHECK: %[[DST_ADDR:.*]] = alloca i8**, align 8
+// CHECK: %[[SRC_ADDR:.*]] = alloca i8**, align 8
+// CHECK: store i8** %[[DST]], i8*** %[[DST_ADDR]], align 8
+// CHECK: store i8** %[[SRC]], i8*** %[[SRC_ADDR]], align 8
+// CHECK: %[[V0:.*]] = load i8**, i8*** %[[DST_ADDR]], align 8
+// CHECK: %[[V1:.*]] = load i8**, i8*** %[[SRC_ADDR]], align 8
+// CHECK: %[[V2:.*]] = bitcast i8** %[[V0]] to i8*
+// CHECK: %[[V3:.*]] = bitcast i8** %[[V1]] to i8*
+// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* %[[V2]], i8* %[[V3]], i64 16, i32 8, i1 false)
+// CHECK: %[[V4:.*]] = bitcast i8** %[[V0]] to i8*
+// CHECK: %[[V5:.*]] = getelementptr inbounds i8, i8* %[[V4]], i64 16
+// CHECK: %[[V6:.*]] = bitcast i8* %[[V5]] to i8**
+// CHECK: %[[V7:.*]] = bitcast i8** %[[V1]] to i8*
+// CHECK: %[[V8:.*]] = getelementptr inbounds i8, i8* %[[V7]], i64 16
+// CHECK: %[[V9:.*]] = bitcast i8* %[[V8]] to i8**
+// CHECK: %[[V10:.*]] = load i8*, i8** %[[V9]], align 8
+// CHECK: %[[V11:.*]] = call i8* @objc_retain(i8* %[[V10]])
+// CHECK: store i8* %[[V11]], i8** %[[V6]], align 8
+// CHECK: ret void
+
+void test_copy_constructor_StrongOuter(StrongOuter *s) {
+ StrongOuter t = *s;
+}
+
+/// CHECK: define linkonce_odr hidden void @__copy_assignment_8_8_t0w128_s128_s192_t256w64(i8** %[[DST:.*]], i8** %[[SRC:.*]])
+// CHECK: %[[DST_ADDR:.*]] = alloca i8**, align 8
+// CHECK: %[[SRC_ADDR:.*]] = alloca i8**, align 8
+// CHECK: store i8** %[[DST]], i8*** %[[DST_ADDR]], align 8
+// CHECK: store i8** %[[SRC]], i8*** %[[SRC_ADDR]], align 8
+// CHECK: %[[V0:.*]] = load i8**, i8*** %[[DST_ADDR]], align 8
+// CHECK: %[[V1:.*]] = load i8**, i8*** %[[SRC_ADDR]], align 8
+// CHECK: %[[V2:.*]] = bitcast i8** %[[V0]] to i8*
+// CHECK: %[[V3:.*]] = getelementptr inbounds i8, i8* %[[V2]], i64 24
+// CHECK: %[[V4:.*]] = bitcast i8* %[[V3]] to i8**
+// CHECK: %[[V5:.*]] = bitcast i8** %[[V1]] to i8*
+// CHECK: %[[V6:.*]] = getelementptr inbounds i8, i8* %[[V5]], i64 24
+// CHECK: %[[V7:.*]] = bitcast i8* %[[V6]] to i8**
+// CHECK: %[[V8:.*]] = load i8*, i8** %[[V7]], align 8
+// CHECK: call void @objc_storeStrong(i8** %[[V4]], i8* %[[V8]])
+
+void test_copy_assignment_StrongOuter(StrongOuter *d, StrongOuter *s) {
+ *d = *s;
+}
+
+// CHECK: define internal void @__Block_byref_object_copy_(i8*, i8*)
+// CHECK: call void @__move_constructor_8_8_t0w128_s128_s192_t256w64(
+
+// CHECK: define linkonce_odr hidden void @__move_constructor_8_8_t0w128_s128_s192_t256w64(i8** %[[DST:.*]], i8** %[[SRC:.*]])
+// CHECK: %[[DST_ADDR:.*]] = alloca i8**, align 8
+// CHECK: %[[SRC_ADDR:.*]] = alloca i8**, align 8
+// CHECK: store i8** %[[DST]], i8*** %[[DST_ADDR]], align 8
+// CHECK: store i8** %[[SRC]], i8*** %[[SRC_ADDR]], align 8
+// CHECK: %[[V0:.*]] = load i8**, i8*** %[[DST_ADDR]], align 8
+// CHECK: %[[V1:.*]] = load i8**, i8*** %[[SRC_ADDR]], align 8
+// CHECK: call void @__move_constructor_8_8_t0w128_s128(i8** %[[V0]], i8** %[[V1]])
+// CHECK: %[[V2:.*]] = bitcast i8** %[[V0]] to i8*
+// CHECK: %[[V3:.*]] = getelementptr inbounds i8, i8* %[[V2]], i64 24
+// CHECK: %[[V4:.*]] = bitcast i8* %[[V3]] to i8**
+// CHECK: %[[V5:.*]] = bitcast i8** %[[V1]] to i8*
+// CHECK: %[[V6:.*]] = getelementptr inbounds i8, i8* %[[V5]], i64 24
+// CHECK: %[[V7:.*]] = bitcast i8* %[[V6]] to i8**
+// CHECK: %[[V8:.*]] = load i8*, i8** %[[V7]], align 8
+// CHECK: store i8* null, i8** %[[V7]], align 8
+// CHECK: store i8* %[[V8]], i8** %[[V4]], align 8
+
+// CHECK: define internal void @__Block_byref_object_dispose_(i8*)
+// CHECK: call void @__destructor_8_s128_s192(
+
+void test_move_constructor_StrongOuter(void) {
+ __block StrongOuter t;
+ BlockTy b = ^{ (void)t; };
+}
+
+// CHECK: define linkonce_odr hidden void @__move_assignment_8_8_t0w128_s128_s192_t256w64(i8** %[[DST:.*]], i8** %[[SRC:.*]])
+// CHECK: %[[DST_ADDR:.*]] = alloca i8**, align 8
+// CHECK: %[[SRC_ADDR:.*]] = alloca i8**, align 8
+// CHECK: store i8** %[[DST]], i8*** %[[DST_ADDR]], align 8
+// CHECK: store i8** %[[SRC]], i8*** %[[SRC_ADDR]], align 8
+// CHECK: %[[V0:.*]] = load i8**, i8*** %[[DST_ADDR]], align 8
+// CHECK: %[[V1:.*]] = load i8**, i8*** %[[SRC_ADDR]], align 8
+// CHECK: call void @__move_assignment_8_8_t0w128_s128(i8** %[[V0]], i8** %[[V1]])
+// CHECK: %[[V2:.*]] = bitcast i8** %[[V0]] to i8*
+// CHECK: %[[V3:.*]] = getelementptr inbounds i8, i8* %[[V2]], i64 24
+// CHECK: %[[V4:.*]] = bitcast i8* %[[V3]] to i8**
+// CHECK: %[[V5:.*]] = bitcast i8** %[[V1]] to i8*
+// CHECK: %[[V6:.*]] = getelementptr inbounds i8, i8* %[[V5]], i64 24
+// CHECK: %[[V7:.*]] = bitcast i8* %[[V6]] to i8**
+// CHECK: %[[V8:.*]] = load i8*, i8** %[[V7]], align 8
+// CHECK: store i8* null, i8** %[[V7]], align 8
+// CHECK: %[[V9:.*]] = load i8*, i8** %[[V4]], align 8
+// CHECK: store i8* %[[V8]], i8** %[[V4]], align 8
+// CHECK: call void @objc_release(i8* %[[V9]])
+
+void test_move_assignment_StrongOuter(StrongOuter *p) {
+ *p = getStrongOuter();
+}
+
+// CHECK: define void @test_parameter_StrongSmall([2 x i64] %[[A_COERCE:.*]])
+// CHECK: %[[A:.*]] = alloca %[[STRUCT_STRONG:.*]], align 8
+// CHECK: %[[V0:.*]] = bitcast %[[STRUCT_STRONG]]* %[[A]] to [2 x i64]*
+// CHECK: store [2 x i64] %[[A_COERCE]], [2 x i64]* %[[V0]], align 8
+// CHECK: %[[V1:.*]] = bitcast %[[STRUCT_STRONG]]* %[[A]] to i8**
+// CHECK: call void @__destructor_8_s64(i8** %[[V1]])
+// CHECK: ret void
+
+void test_parameter_StrongSmall(StrongSmall a) {
+}
+
+// CHECK: define void @test_argument_StrongSmall([2 x i64] %[[A_COERCE:.*]])
+// CHECK: %[[A:.*]] = alloca %[[STRUCT_STRONGSMALL:.*]], align 8
+// CHECK: %[[TEMP_LVALUE:.*]] = alloca %[[STRUCT_STRONGSMALL]], align 8
+// CHECK: %[[V0:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[A]] to [2 x i64]*
+// CHECK: store [2 x i64] %[[A_COERCE]], [2 x i64]* %[[V0]], align 8
+// CHECK: %[[V1:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[TEMP_LVALUE]] to i8**
+// CHECK: %[[V2:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[A]] to i8**
+// CHECK: call void @__copy_constructor_8_8_t0w32_s64(i8** %[[V1]], i8** %[[V2]])
+// CHECK: %[[V3:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[TEMP_LVALUE]] to [2 x i64]*
+// CHECK: %[[V4:.*]] = load [2 x i64], [2 x i64]* %[[V3]], align 8
+// CHECK: call void @calleeStrongSmall([2 x i64] %[[V4]])
+// CHECK: %[[V5:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[A]] to i8**
+// CHECK: call void @__destructor_8_s64(i8** %[[V5]])
+// CHECK: ret void
+
+void test_argument_StrongSmall(StrongSmall a) {
+ calleeStrongSmall(a);
+}
+
+// CHECK: define [2 x i64] @test_return_StrongSmall([2 x i64] %[[A_COERCE:.*]])
+// CHECK: %[[RETVAL:.*]] = alloca %[[STRUCT_STRONGSMALL:.*]], align 8
+// CHECK: %[[A:.*]] = alloca %[[STRUCT_STRONGSMALL]], align 8
+// CHECK: %[[V0:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[A]] to [2 x i64]*
+// CHECK: store [2 x i64] %[[A_COERCE]], [2 x i64]* %[[V0]], align 8
+// CHECK: %[[V1:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[RETVAL]] to i8**
+// CHECK: %[[V2:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[A]] to i8**
+// CHECK: call void @__copy_constructor_8_8_t0w32_s64(i8** %[[V1]], i8** %[[V2]])
+// CHECK: %[[V3:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[A]] to i8**
+// CHECK: call void @__destructor_8_s64(i8** %[[V3]])
+// CHECK: %[[V4:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[RETVAL]] to [2 x i64]*
+// CHECK: %[[V5:.*]] = load [2 x i64], [2 x i64]* %[[V4]], align 8
+// CHECK: ret [2 x i64] %[[V5]]
+
+StrongSmall test_return_StrongSmall(StrongSmall a) {
+ return a;
+}
+
+// CHECK: define void @test_destructor_ignored_result()
+// CHECK: %[[COERCE:.*]] = alloca %[[STRUCT_STRONGSMALL:.*]], align 8
+// CHECK: %[[CALL:.*]] = call [2 x i64] @getStrongSmall()
+// CHECK: %[[V0:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[COERCE]] to [2 x i64]*
+// CHECK: store [2 x i64] %[[CALL]], [2 x i64]* %[[V0]], align 8
+// CHECK: %[[V1:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[COERCE]] to i8**
+// CHECK: call void @__destructor_8_s64(i8** %[[V1]])
+// CHECK: ret void
+
+void test_destructor_ignored_result(void) {
+ getStrongSmall();
+}
+
+// CHECK: define void @test_copy_constructor_StrongBlock(
+// CHECK: call void @__copy_constructor_8_8_sb0(
+// CHECK: call void @__destructor_8_sb0(
+// CHECK: ret void
+
+// CHECK: define linkonce_odr hidden void @__copy_constructor_8_8_sb0(i8** %[[DST:.*]], i8** %[[SRC:.*]])
+// CHECK: %[[DST_ADDR:.*]] = alloca i8**, align 8
+// CHECK: %[[SRC_ADDR:.*]] = alloca i8**, align 8
+// CHECK: store i8** %[[DST]], i8*** %[[DST_ADDR]], align 8
+// CHECK: store i8** %[[SRC]], i8*** %[[SRC_ADDR]], align 8
+// CHECK: %[[V0:.*]] = load i8**, i8*** %[[DST_ADDR]], align 8
+// CHECK: %[[V1:.*]] = load i8**, i8*** %[[SRC_ADDR]], align 8
+// CHECK: %[[V2:.*]] = load i8*, i8** %[[V1]], align 8
+// CHECK: %[[V3:.*]] = call i8* @objc_retainBlock(i8* %[[V2]])
+// CHECK: store i8* %[[V3]], i8** %[[V0]], align 8
+// CHECK: ret void
+
+void test_copy_constructor_StrongBlock(StrongBlock *s) {
+ StrongBlock t = *s;
+}
+
+// CHECK: define void @test_copy_assignment_StrongBlock(%[[STRUCT_STRONGBLOCK:.*]]* %[[D:.*]], %[[STRUCT_STRONGBLOCK]]* %[[S:.*]])
+// CHECK: call void @__copy_assignment_8_8_sb0(
+
+// CHECK: define linkonce_odr hidden void @__copy_assignment_8_8_sb0(i8** %[[DST:.*]], i8** %[[SRC:.*]])
+// CHECK: %[[DST_ADDR:.*]] = alloca i8**, align 8
+// CHECK: %[[SRC_ADDR:.*]] = alloca i8**, align 8
+// CHECK: store i8** %[[DST]], i8*** %[[DST_ADDR]], align 8
+// CHECK: store i8** %[[SRC]], i8*** %[[SRC_ADDR]], align 8
+// CHECK: %[[V0:.*]] = load i8**, i8*** %[[DST_ADDR]], align 8
+// CHECK: %[[V1:.*]] = load i8**, i8*** %[[SRC_ADDR]], align 8
+// CHECK: %[[V2:.*]] = load i8*, i8** %[[V1]], align 8
+// CHECK: %[[V3:.*]] = call i8* @objc_retainBlock(i8* %[[V2]])
+// CHECK: %[[V4:.*]] = load i8*, i8** %[[V0]], align 8
+// CHECK: store i8* %[[V3]], i8** %[[V0]], align 8
+// CHECK: call void @objc_release(i8* %[[V4]])
+// CHECK: ret void
+
+void test_copy_assignment_StrongBlock(StrongBlock *d, StrongBlock *s) {
+ *d = *s;
+}
+
+// CHECK: define void @test_copy_constructor_StrongVolatile0(
+// CHECK: call void @__copy_constructor_8_8_t0w32_sv64(
+// CHECK: call void @__destructor_8_sv64(
+
+// CHECK: define linkonce_odr hidden void @__copy_constructor_8_8_t0w32_sv64(
+// CHECK: %[[V8:.*]] = load volatile i8*, i8** %{{.*}}, align 8
+// CHECK: %[[V9:.*]] = call i8* @objc_retain(i8* %[[V8]])
+// CHECK: store volatile i8* %[[V9]], i8** %{{.*}}, align 8
+
+void test_copy_constructor_StrongVolatile0(StrongVolatile *s) {
+ StrongVolatile t = *s;
+}
+
+// CHECK: define void @test_copy_constructor_StrongVolatile1(
+// CHECK: call void @__copy_constructor_8_8_tv0w128_sv128(
+
+void test_copy_constructor_StrongVolatile1(Strong *s) {
+ volatile Strong t = *s;
+}
+
+// CHECK: define void @test_block_capture_Strong()
+// CHECK: call void @__default_constructor_8_s128(
+// CHECK: call void @__copy_constructor_8_8_t0w128_s128(
+// CHECK: call void @__destructor_8_s128(
+// CHECK: call void @__destructor_8_s128(
+// CHECK: ret void
+
+// CHECK: define internal void @__copy_helper_block_.1(i8*, i8*)
+// CHECK: call void @__copy_constructor_8_8_t0w128_s128(
+// CHECK: ret void
+
+// CHECK: define internal void @__destroy_helper_block_.2(
+// CHECK: call void @__destructor_8_s128(
+// CHECK: ret void
+
+void test_block_capture_Strong(void) {
+ Strong t;
+ BlockTy b = ^(){ (void)t; };
+}
+
+// CHECK: define void @test_variable_length_array(i32 %[[N:.*]])
+// CHECK: %[[N_ADDR:.*]] = alloca i32, align 4
+// CHECK: store i32 %[[N]], i32* %[[N_ADDR]], align 4
+// CHECK: %[[V0:.*]] = load i32, i32* %[[N_ADDR]], align 4
+// CHECK: %[[V1:.*]] = zext i32 %[[V0]] to i64
+// CHECK: %[[VLA:.*]] = alloca %[[STRUCT_STRONG:.*]], i64 %[[V1]], align 8
+// CHECK: %[[V3:.*]] = bitcast %[[STRUCT_STRONG]]* %[[VLA]] to i8**
+// CHECK: %[[V4:.*]] = mul nuw i64 24, %[[V1]]
+// CHECK: %[[V5:.*]] = bitcast i8** %[[V3]] to i8*
+// CHECK: %[[V6:.*]] = getelementptr inbounds i8, i8* %[[V5]], i64 %[[V4]]
+// CHECK: %[[DSTARRAY_END:.*]] = bitcast i8* %[[V6]] to i8**
+// CHECK: br label
+
+// CHECK: %[[DSTADDR_CUR:.*]] = phi i8** [ %[[V3]], {{.*}} ], [ %[[V7:.*]], {{.*}} ]
+// CHECK: %[[DONE:.*]] = icmp eq i8** %[[DSTADDR_CUR]], %[[DSTARRAY_END]]
+// CHECK: br i1 %[[DONE]], label
+
+// CHECK: call void @__default_constructor_8_s128(i8** %[[DSTADDR_CUR]])
+// CHECK: %[[V8:.*]] = bitcast i8** %[[DSTADDR_CUR]] to i8*
+// CHECK: %[[V9:.*]] = getelementptr inbounds i8, i8* %[[V8]], i64 24
+// CHECK: %[[V7]] = bitcast i8* %[[V9]] to i8**
+// CHECK: br label
+
+// CHECK: call void @func(%[[STRUCT_STRONG]]* %[[VLA]])
+// CHECK: %[[V10:.*]] = getelementptr inbounds %[[STRUCT_STRONG]], %[[STRUCT_STRONG]]* %[[VLA]], i64 %[[V1]]
+// CHECK: %[[ARRAYDESTROY_ISEMPTY:.*]] = icmp eq %[[STRUCT_STRONG]]* %[[VLA]], %[[V10]]
+// CHECK: br i1 %[[ARRAYDESTROY_ISEMPTY]], label
+
+// CHECK: %[[ARRAYDESTROY_ELEMENTPAST:.*]] = phi %[[STRUCT_STRONG]]* [ %[[V10]], {{.*}} ], [ %[[ARRAYDESTROY_ELEMENT:.*]], {{.*}} ]
+// CHECK: %[[ARRAYDESTROY_ELEMENT]] = getelementptr inbounds %[[STRUCT_STRONG]], %[[STRUCT_STRONG]]* %[[ARRAYDESTROY_ELEMENTPAST]], i64 -1
+// CHECK: %[[V11:.*]] = bitcast %[[STRUCT_STRONG]]* %[[ARRAYDESTROY_ELEMENT]] to i8**
+// CHECK: call void @__destructor_8_s128(i8** %[[V11]])
+// CHECK: %[[ARRAYDESTROY_DONE:.*]] = icmp eq %[[STRUCT_STRONG]]* %[[ARRAYDESTROY_ELEMENT]], %[[VLA]]
+// CHECK: br i1 %[[ARRAYDESTROY_DONE]], label
+
+// CHECK: ret void
+
+void test_variable_length_array(int n) {
+ Strong a[n];
+ func(a);
+}
+
+// CHECK: define linkonce_odr hidden void @__default_constructor_8_AB64s64n4_s64_AE(
+void test_constructor_destructor_IDArray(void) {
+ IDArray t;
+}
+
+// CHECK: define linkonce_odr hidden void @__default_constructor_8_AB64s192n4_s192_AE(
+void test_constructor_destructor_StructArray(void) {
+ StructArray t;
+}
+
+// Check that IRGen copies the 9-bit bitfield emitting i16 load and store.
+
+// CHECK: define linkonce_odr hidden void @__copy_constructor_8_8_s0_t64w9(
+// CHECK: %[[V4:.*]] = bitcast i8** %{{.*}} to i8*
+// CHECK: %[[V5:.*]] = getelementptr inbounds i8, i8* %[[V4]], i64 8
+// CHECK: %[[V6:.*]] = bitcast i8* %[[V5]] to i8**
+// CHECK: %[[V7:.*]] = bitcast i8** %{{.*}} to i8*
+// CHECK: %[[V8:.*]] = getelementptr inbounds i8, i8* %[[V7]], i64 8
+// CHECK: %[[V9:.*]] = bitcast i8* %[[V8]] to i8**
+// CHECK: %[[V10:.*]] = bitcast i8** %[[V6]] to i16*
+// CHECK: %[[V11:.*]] = bitcast i8** %[[V9]] to i16*
+// CHECK: %[[V12:.*]] = load i16, i16* %[[V11]], align 8
+// CHECK: store i16 %[[V12]], i16* %[[V10]], align 8
+// CHECK: ret void
+
+void test_copy_constructor_Bitfield0(Bitfield0 *a) {
+ Bitfield0 t = *a;
+}
+
+// CHECK: define linkonce_odr hidden void @__copy_constructor_8_8_t0w8_t8w2_t10w4_s64_t128w31_t159w1_s192_t256w96_s384_t448w64_t512w1(
+// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* %{{.*}}, i8* %{{.*}}, i64 2, i32 8, i1 false)
+// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* %{{.*}}, i8* %{{.*}}, i64 4, i32 8, i1 false)
+// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* %{{.*}}, i8* %{{.*}}, i64 12, i32 8, i1 false)
+// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* %{{.*}}, i8* %{{.*}}, i64 9, i32 8, i1 false)
+
+void test_copy_constructor_Bitfield1(Bitfield1 *a) {
+ Bitfield1 t = *a;
+}
Index: test/CodeGenObjC/nontrivial-c-struct-exception.m
===================================================================
--- /dev/null
+++ test/CodeGenObjC/nontrivial-c-struct-exception.m
@@ -0,0 +1,33 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios11 -fobjc-arc -fblocks -fobjc-runtime=ios-11.0 -fobjc-exceptions -fexceptions -fobjc-arc-exceptions -emit-llvm -o - %s | FileCheck %s
+
+// CHECK: %[[STRUCT_STRONG:.*]] = type { i32, i8* }
+
+typedef struct {
+ int i;
+ id f1;
+} Strong;
+
+// CHECK: define void @testStrongException()
+// CHECK: %[[AGG_TMP:.*]] = alloca %[[STRUCT_STRONG]], align 8
+// CHECK: %[[AGG_TMP1:.*]] = alloca %[[STRUCT_STRONG]], align 8
+// CHECK: %[[CALL:.*]] = call [2 x i64] @genStrong()
+// CHECK: %[[V0:.*]] = bitcast %[[STRUCT_STRONG]]* %[[AGG_TMP]] to [2 x i64]*
+// CHECK: store [2 x i64] %[[CALL]], [2 x i64]* %[[V0]], align 8
+// CHECK: invoke [2 x i64] @genStrong()
+
+// CHECK: call void @calleeStrong([2 x i64] %{{.*}}, [2 x i64] %{{.*}})
+// CHECK-NEXT: ret void
+
+// CHECK: landingpad { i8*, i32 }
+// CHECK: %[[V9:.*]] = bitcast %[[STRUCT_STRONG]]* %[[AGG_TMP]] to i8**
+// CHECK: call void @__destructor_8_s64(i8** %[[V9]])
+// CHECK: br label
+
+// CHECK: resume
+
+Strong genStrong(void);
+void calleeStrong(Strong, Strong);
+
+void testStrongException(void) {
+ calleeStrong(genStrong(), genStrong());
+}
Index: test/ARCMT/checking.m
===================================================================
--- test/ARCMT/checking.m
+++ test/ARCMT/checking.m
@@ -116,7 +116,7 @@
}
struct S {
- A* a; // expected-error {{ARC forbids Objective-C objects in struct}}
+ A* a;
};
@interface B
Index: lib/Sema/SemaExpr.cpp
===================================================================
--- lib/Sema/SemaExpr.cpp
+++ lib/Sema/SemaExpr.cpp
@@ -776,6 +776,9 @@
return VAK_Valid;
}
+ if (Ty.isNonTrivialToPrimitiveDestroyStruct())
+ return VAK_Invalid;
+
if (Ty.isCXX98PODType(Context))
return VAK_Valid;
@@ -837,7 +840,10 @@
break;
case VAK_Invalid:
- if (Ty->isObjCObjectType())
+ if (Ty.isNonTrivialToPrimitiveDestroyStruct())
+ Diag(E->getLocStart(),
+ diag::err_cannot_pass_non_trivial_c_struct_to_vararg) << Ty << CT;
+ else if (Ty->isObjCObjectType())
DiagRuntimeBehavior(
E->getLocStart(), nullptr,
PDiag(diag::err_cannot_pass_objc_interface_to_vararg)
Index: lib/Sema/SemaDecl.cpp
===================================================================
--- lib/Sema/SemaDecl.cpp
+++ lib/Sema/SemaDecl.cpp
@@ -11340,6 +11340,9 @@
}
}
+ if (var->getType().isNonTrivialToPrimitiveDestroyStruct())
+ getCurFunction()->setHasBranchProtectedScope();
+
// Warn about externally-visible variables being defined without a
// prior declaration. We only want to do this for global
// declarations, but we also specifically need to avoid doing it for
@@ -15226,6 +15229,7 @@
// Get the type for the field.
const Type *FDTy = FD->getType().getTypePtr();
+ Qualifiers QS = FD->getType().getQualifiers();
if (!FD->isAnonymousStructOrUnion()) {
// Remember all fields written by the user.
@@ -15367,7 +15371,9 @@
FD->setType(T);
} else if (getLangOpts().allowsNonTrivialObjCLifetimeQualifiers() &&
Record && !ObjCFieldLifetimeErrReported &&
- (!getLangOpts().CPlusPlus || Record->isUnion())) {
+ ((!getLangOpts().CPlusPlus &&
+ QS.getObjCLifetime() == Qualifiers::OCL_Weak) ||
+ Record->isUnion())) {
// It's an error in ARC or Weak if a field has lifetime.
// We don't want to report this in a system header, though,
// so we just make the field unavailable.
@@ -15403,6 +15409,13 @@
Record->setHasObjectMember(true);
}
}
+
+ if (Record && !getLangOpts().CPlusPlus) {
+ QualType FT = FD->getType();
+ if (FT.hasObjCStrongPointer())
+ Record->setHasStrongObjCPointer(true);
+ }
+
if (Record && FD->getType().isVolatileQualified())
Record->setHasVolatileMember(true);
// Keep track of the number of named members.
Index: lib/Sema/JumpDiagnostics.cpp
===================================================================
--- lib/Sema/JumpDiagnostics.cpp
+++ lib/Sema/JumpDiagnostics.cpp
@@ -154,6 +154,10 @@
return ScopePair(diag::note_protected_by_objc_weak_init,
diag::note_exits_objc_weak);
+ case QualType::DK_c_struct_strong_field:
+ return ScopePair(diag::note_protected_by_non_trivial_c_struct_init,
+ diag::note_exits_dtor);
+
case QualType::DK_cxx_destructor:
OutDiag = diag::note_exits_dtor;
break;
@@ -254,6 +258,10 @@
Diags = ScopePair(diag::note_enters_block_captures_weak,
diag::note_exits_block_captures_weak);
break;
+ case QualType::DK_c_struct_strong_field:
+ Diags = ScopePair(diag::note_enters_block_captures_non_trivial_c_struct,
+ diag::note_exits_block_captures_non_trivial_c_struct);
+ break;
case QualType::DK_none:
llvm_unreachable("non-lifetime captured variable");
}
Index: lib/Lex/PPMacroExpansion.cpp
===================================================================
--- lib/Lex/PPMacroExpansion.cpp
+++ lib/Lex/PPMacroExpansion.cpp
@@ -1151,6 +1151,7 @@
// Objective-C features
.Case("objc_arr", LangOpts.ObjCAutoRefCount) // FIXME: REMOVE?
.Case("objc_arc", LangOpts.ObjCAutoRefCount)
+ .Case("objc_arc_fields", true)
.Case("objc_arc_weak", LangOpts.ObjCWeak)
.Case("objc_default_synthesize_properties", LangOpts.ObjC2)
.Case("objc_fixed_enum", LangOpts.ObjC2)
Index: lib/CodeGen/CodeGenFunction.h
===================================================================
--- lib/CodeGen/CodeGenFunction.h
+++ lib/CodeGen/CodeGenFunction.h
@@ -1538,6 +1538,7 @@
case QualType::DK_objc_weak_lifetime:
return getLangOpts().Exceptions;
case QualType::DK_objc_strong_lifetime:
+ case QualType::DK_c_struct_strong_field:
return getLangOpts().Exceptions &&
CGM.getCodeGenOpts().ObjCAutoRefCountExceptions;
}
@@ -3366,6 +3367,22 @@
CXXDtorType Type,
const CXXRecordDecl *RD);
+ // These functions emit calls to the special functions of non-trivial C
+ // structs.
+ void defaultInitNonTrivialCStructVar(Address DstPtr, QualType QT,
+ bool IsVolatile);
+ void callCStructDefaultConstructor(Address DstPtr, QualType QT,
+ bool IsVolatile);
+ void callCStructCopyConstructor(Address DstPtr, Address SrcPtr,
+ QualType QT, bool IsVolatile);
+ void callCStructMoveConstructor(Address DstPtr, Address SrcPtr,
+ QualType QT, bool IsVolatile);
+ void callCStructCopyAssignmentOperator(Address DstPtr, Address SrcPtr,
+ QualType QT, bool IsVolatile);
+ void callCStructMoveAssignmentOperator(Address DstPtr, Address SrcPtr,
+ QualType QT, bool IsVolatile);
+ void callCStructDestructor(Address DstPtr, QualType QT, bool IsVolatile);
+
RValue
EmitCXXMemberOrOperatorCall(const CXXMethodDecl *Method,
const CGCallee &Callee,
@@ -3540,6 +3557,7 @@
static Destroyer destroyARCStrongPrecise;
static Destroyer destroyARCWeak;
static Destroyer emitARCIntrinsicUse;
+ static Destroyer destroyNonTrivialCStruct;
void EmitObjCAutoreleasePoolPop(llvm::Value *Ptr);
llvm::Value *EmitObjCAutoreleasePoolPush();
Index: lib/CodeGen/CMakeLists.txt
===================================================================
--- lib/CodeGen/CMakeLists.txt
+++ lib/CodeGen/CMakeLists.txt
@@ -56,6 +56,7 @@
CGExprScalar.cpp
CGGPUBuiltin.cpp
CGLoopInfo.cpp
+ CGNonTrivialStruct.cpp
CGObjC.cpp
CGObjCGNU.cpp
CGObjCMac.cpp
Index: lib/CodeGen/CGNonTrivialStruct.cpp
===================================================================
--- /dev/null
+++ lib/CodeGen/CGNonTrivialStruct.cpp
@@ -0,0 +1,673 @@
+//===--- CGNonTrivialStruct.cpp - Emit Special Functions for C Structs ----===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines functions to generate various special functions for C
+// structs.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CodeGenFunction.h"
+#include "CodeGenModule.h"
+#include "llvm/Support/ScopedPrinter.h"
+
+using namespace clang;
+using namespace CodeGen;
+
+/// Return an address with the specified offset from the passed address.
+static Address getAddrWithOffset(Address Addr, unsigned Offset,
+ CodeGenFunction &CGF) {
+ if (!Addr.isValid() || Offset == 0)
+ return Addr;
+ Addr = CGF.Builder.CreateBitCast(Addr, CGF.CGM.Int8PtrTy);
+ Addr = CGF.Builder.CreateConstInBoundsGEP(Addr, Offset, CharUnits::One());
+ return CGF.Builder.CreateBitCast(Addr, CGF.CGM.Int8PtrPtrTy);
+}
+
+static const CGFunctionInfo &getFunctionInfo(CodeGenModule &CGM, bool IsBinary,
+ FunctionArgList &Args) {
+ ASTContext &Ctx = CGM.getContext();
+ llvm::SmallVector<ImplicitParamDecl *, 2> Params;
+ QualType ParamTy = Ctx.getPointerType(Ctx.VoidPtrTy);
+ Params.push_back(ImplicitParamDecl::Create(Ctx, nullptr, SourceLocation(),
+ &Ctx.Idents.get("dst"), ParamTy,
+ ImplicitParamDecl::Other));
+
+ if (IsBinary)
+ Params.push_back(ImplicitParamDecl::Create(Ctx, nullptr, SourceLocation(),
+ &Ctx.Idents.get("src"), ParamTy,
+ ImplicitParamDecl::Other));
+
+ for (auto &P : Params)
+ Args.push_back(P);
+
+ return CGM.getTypes().arrangeBuiltinFunctionDeclaration(Ctx.VoidTy, Args);
+}
+
+static std::string getVolatileOffsetStr(bool IsVolatile, uint64_t Offset) {
+ std::string S;
+ if (IsVolatile)
+ S = "v";
+ S += llvm::to_string(Offset);
+ return S;
+}
+
+static uint64_t getFieldSize(const FieldDecl *FD, ASTContext &Ctx) {
+ if (FD->isBitField())
+ return FD->getBitWidthValue(Ctx);
+ return Ctx.getTypeSize(FD->getType());
+}
+
+namespace {
+// This is a helper class that encodes the information about the fields of a
+// non-trivial C struct into the name of its special functions. The string has
+// the following structure:
+//
+// <struct-field-info> ::= <field-info>+
+// <field-info> ::= <struct-or-scalar-field-info> | <array-field-info> |
+// <struct-or-scalar-field-info> ::= <struct-field-info> | <strong-field-info> |
+// <trivial-field-info>
+// <array-field-info> ::= "_AB" <array-offset> "s" <element-size> "n"
+// <num-elements> <element-info> "_AE"
+// <element-info> ::= <struct-or-scalar-field-info>
+// <strong-field-info> ::= "_s" ["b"] ["v"] <field-offset>
+// <trivial-field-info> ::= "_t" ["v"] <field-offset> "_" <field-size>
+
+template <class T> class FieldInfoToString {
+public:
+ FieldInfoToString(QualType QT, bool IsVolatile, ASTContext &C) : Ctx(C) {
+ visitStruct(0, QT, IsVolatile);
+ }
+
+ void visitField(uint64_t OffsetInBits, QualType QT, bool IsVolatile,
+ const FieldDecl *FD = nullptr) {
+ QualType::PrimitiveCopyKind Kind = T::isInterestingType(QT);
+
+ if (Kind == QualType::PCK_Trivial) {
+ assert(FD && "non-null FieldDecl expected here");
+ // Ignore this trivial field if it is a zero-sized field or a unary
+ // special function is being emitted.
+ if (getFieldSize(FD, Ctx) == 0 || !T::isBinary())
+ return;
+ uint64_t SizeInBits = getFieldSize(FD, Ctx);
+ Str += "_t" + getVolatileOffsetStr(IsVolatile, OffsetInBits) + "w";
+ Str += llvm::to_string(SizeInBits);
+ return;
+ }
+
+ if (Ctx.getAsArrayType(QT)) {
+ visitArray(OffsetInBits, QT, IsVolatile);
+ return;
+ }
+
+ switch (Kind) {
+ case QualType::PCK_Struct:
+ visitStruct(OffsetInBits, QT, IsVolatile);
+ break;
+ case QualType::PCK_Strong:
+ Str += "_s";
+ if (QT->isBlockPointerType())
+ Str += "b";
+ Str += getVolatileOffsetStr(IsVolatile, OffsetInBits);
+ break;
+ case QualType::PCK_Trivial:
+ llvm_unreachable("trivial field not expected here");
+ break;
+ }
+ }
+
+ void visitStruct(uint64_t Offset, QualType QT, bool IsVolatile) {
+ const RecordDecl *RD = QT->castAs<RecordType>()->getDecl();
+ const ASTRecordLayout &RL = Ctx.getASTRecordLayout(RD);
+
+ // Iterate over the fields of the struct.
+ size_t FieldNo = 0;
+ for (const FieldDecl *FD : RD->fields()) {
+ QualType FT = FD->getType();
+ uint64_t FOffsetInBits = Offset + RL.getFieldOffset(FieldNo++);
+ visitField(FOffsetInBits, FT, FT.isVolatileQualified() || IsVolatile, FD);
+ }
+ }
+
+ void visitArray(uint64_t Offset, QualType QT, bool IsVolatile) {
+ const auto *AT = Ctx.getAsConstantArrayType(QT);
+ unsigned NumElts = Ctx.getConstantArrayElementCount(AT);
+ QualType EltTy = Ctx.getBaseElementType(AT);
+ unsigned EltSize = Ctx.getTypeSize(EltTy);
+ Str += "_AB" + llvm::to_string(Offset) + "s" + llvm::to_string(EltSize) +
+ "n" + llvm::to_string(NumElts);
+ visitField(Offset, EltTy, IsVolatile);
+ Str += "_AE";
+ }
+
+ operator std::string() const { return Str; }
+
+private:
+ ASTContext &Ctx;
+ std::string Str;
+};
+} // namespace
+
+// This function creates the mangled name of a special function of a non-trivial
+// C struct. Since there is no ODR in C, the function is mangled based on the
+// struct contents and not the name. The mangled name has the following
+// structure:
+//
+// <function-name> ::= <prefix> <alignment-info> "_" <struct-field-info>
+// <prefix> ::= "__destructor_" | "__default_constructor_" |
+// "__copy_constructor_" | "__move_constructor_" |
+// "__copy_assignment_" | "__move_assignment_"
+// <alignment-info> ::= <dst-alignment> ["_" <src-alignment>]
+
+template <class T>
+static std::string
+getSpecialFuncName(QualType QT, bool IsVolatile, CharUnits DstAlignment,
+ CharUnits SrcAlignment, bool IsBinary, CodeGenModule &CGM) {
+ StringRef Prefix = T::getFuncPrefix();
+ std::string S = FieldInfoToString<T>(QT, IsVolatile, CGM.getContext());
+ std::string A = llvm::to_string(DstAlignment.getQuantity());
+ if (IsBinary)
+ A += "_" + llvm::to_string(SrcAlignment.getQuantity());
+ return std::string(Prefix) + A + S;
+}
+
+// Helper function to create a null constant.
+static llvm::Constant *getNullForVariable(Address Addr) {
+ llvm::Type *Ty = Addr.getElementType();
+ return llvm::ConstantPointerNull::get(cast<llvm::PointerType>(Ty));
+}
+
+namespace {
+
+template <class T> class IRGen {
+public:
+ IRGen()
+ : CGF(nullptr), TrivialStartDst(Address::invalid()),
+ TrivialStartSrc(Address::invalid()) {}
+
+ void setFunction(CodeGenFunction &F) { CGF = &F; }
+
+ void visitField(Address DstBaseAddr, Address SrcBaseAddr, uint64_t Offset,
+ QualType QT, bool IsVolatile, const FieldDecl *FD = nullptr) {
+ ASTContext &Ctx = CGF->getContext();
+ QualType::PrimitiveCopyKind Kind = T::isInterestingType(QT);
+
+ if (Kind == QualType::PCK_Trivial) {
+ assert(FD && "non-null FieldDecl expected here");
+ // Ignore this trivial field if it is a zero-sized field or a unary
+ // special function is being emitted.
+ if (getFieldSize(FD, Ctx) != 0 && T::isBinary())
+ addTrivialField(DstBaseAddr, SrcBaseAddr, Offset, IsVolatile, FD);
+ return;
+ }
+
+ flushTrivialFields();
+ Address DstAddr = getAddrWithOffset(DstBaseAddr, Offset, *CGF);
+ Address SrcAddr = getAddrWithOffset(SrcBaseAddr, Offset, *CGF);
+
+ if (Ctx.getAsArrayType(QT)) {
+ visitArray(DstAddr, SrcAddr, QT, IsVolatile);
+ return;
+ }
+
+ switch (Kind) {
+ case QualType::PCK_Struct:
+ visitStruct(DstAddr, SrcAddr, QT, IsVolatile);
+ break;
+ case QualType::PCK_Strong:
+ if (IsVolatile)
+ QT = QT.withVolatile();
+ T::emitIRStrong(DstAddr, SrcAddr, QT, *CGF);
+ break;
+ case QualType::PCK_Trivial:
+ llvm_unreachable("trivial field not expected here");
+ break;
+ }
+ }
+
+ void visitStruct(Address DstAddr, Address SrcAddr, QualType QT,
+ bool IsVolatile, bool IsField = true) {
+ // Just call the special function if this is a struct field contained in
+ // another struct.
+ if (IsField) {
+ T::callSpecialFunction(DstAddr, SrcAddr, QT, IsVolatile, *CGF);
+ return;
+ }
+
+ const RecordDecl *RD = QT->castAs<RecordType>()->getDecl();
+ ASTContext &Ctx = CGF->getContext();
+ const ASTRecordLayout &RL = Ctx.getASTRecordLayout(RD);
+
+ // Iterate over the fields of the struct.
+ size_t FieldNo = 0;
+ for (const FieldDecl *FD : RD->fields()) {
+ QualType FT = FD->getType();
+ visitField(DstAddr, SrcAddr,
+ RL.getFieldOffset(FieldNo++) / Ctx.getCharWidth(), FT,
+ FT.isVolatileQualified() || IsVolatile, FD);
+ }
+
+ // Flush the trivial fields at the end of the struct.
+ flushTrivialFields();
+ }
+
+ void visitArray(Address DstAddr, Address SrcAddr, QualType QT,
+ bool IsVolatile) {
+ // Compute the end address.
+ ASTContext &Ctx = CGF->getContext();
+ QualType BaseEltQT;
+ Address TmpAddr = DstAddr;
+ llvm::Value *NumElts =
+ CGF->emitArrayLength(Ctx.getAsArrayType(QT), BaseEltQT, TmpAddr);
+ unsigned BaseEltSize = Ctx.getTypeSizeInChars(BaseEltQT).getQuantity();
+ llvm::Value *BaseEltSizeVal =
+ llvm::ConstantInt::get(NumElts->getType(), BaseEltSize);
+ llvm::Value *SizeInBytes =
+ CGF->Builder.CreateNUWMul(BaseEltSizeVal, NumElts);
+ Address BC = CGF->Builder.CreateBitCast(DstAddr, CGF->CGM.Int8PtrTy);
+ llvm::Value *DstArrayEnd =
+ CGF->Builder.CreateInBoundsGEP(BC.getPointer(), SizeInBytes);
+ DstArrayEnd = CGF->Builder.CreateBitCast(DstArrayEnd, CGF->CGM.Int8PtrPtrTy,
+ "dstarray.end");
+ llvm::BasicBlock *PreheaderBB = CGF->Builder.GetInsertBlock();
+
+ // Create the header block and insert the phi instructions.
+ llvm::BasicBlock *HeaderBB = CGF->createBasicBlock("loop.header");
+ CGF->EmitBlock(HeaderBB);
+ llvm::Type *Ty = DstAddr.getPointer()->getType();
+ llvm::PHINode *DstPHI = CGF->Builder.CreatePHI(Ty, 2, "dstaddr.cur");
+ DstPHI->addIncoming(DstAddr.getPointer(), PreheaderBB);
+ llvm::PHINode *SrcPHI = nullptr;
+ if (SrcAddr.isValid()) {
+ SrcPHI = CGF->Builder.CreatePHI(Ty, 2, "srcaddr.cur");
+ SrcPHI->addIncoming(SrcAddr.getPointer(), PreheaderBB);
+ }
+
+ // Create the exit and loop body blocks.
+ llvm::BasicBlock *ExitBB = CGF->createBasicBlock("loop.exit");
+ llvm::BasicBlock *LoopBB = CGF->createBasicBlock("loop.body");
+
+ // Emit the comparison and conditional branch instruction that jumps to
+ // either the exit or the loop body.
+ llvm::Value *Done = CGF->Builder.CreateICmpEQ(DstPHI, DstArrayEnd, "done");
+ CGF->Builder.CreateCondBr(Done, ExitBB, LoopBB);
+
+ // Visit the element of the array in the loop body.
+ CGF->EmitBlock(LoopBB);
+ QualType EltQT = Ctx.getAsArrayType(QT)->getElementType();
+ CharUnits EltSize = Ctx.getTypeSizeInChars(EltQT);
+ Address Dst =
+ Address(DstPHI, DstAddr.getAlignment().alignmentAtOffset(EltSize));
+ Address Src = Address::invalid();
+ if (SrcAddr.isValid())
+ Src = Address(SrcPHI, SrcAddr.getAlignment().alignmentAtOffset(EltSize));
+ visitField(Dst, Src, 0, EltQT, IsVolatile);
+
+ // Instrs to update the destination and source addresses.
+ Address NextDst = getAddrWithOffset(Dst, EltSize.getQuantity(), *CGF);
+ Address NextSrc = getAddrWithOffset(Src, EltSize.getQuantity(), *CGF);
+
+ // Update phi instructions.
+ LoopBB = CGF->Builder.GetInsertBlock();
+ DstPHI->addIncoming(NextDst.getPointer(), LoopBB);
+ if (NextSrc.isValid())
+ SrcPHI->addIncoming(NextSrc.getPointer(), LoopBB);
+
+ // Insert an unconditional branch to the header block.
+ CGF->Builder.CreateBr(HeaderBB);
+ CGF->EmitBlock(ExitBB);
+ }
+
+ void addTrivialField(Address DstAddr, Address SrcAddr, uint64_t Offset,
+ bool IsVolatile, const FieldDecl *FD) {
+ LastTrivialField = {FD, IsVolatile};
+ if (!FirstTrivialField.getPointer()) {
+ // Only the address of the first field of the sequence is needed.
+ TrivialStartDst = getAddrWithOffset(DstAddr, Offset, *CGF);
+ TrivialStartSrc = getAddrWithOffset(SrcAddr, Offset, *CGF);
+ FirstTrivialField = LastTrivialField;
+ }
+ }
+
+ void flushTrivialFields() {
+ if (!FirstTrivialField.getPointer())
+ return;
+
+ assert(T::isBinary() &&
+ "Unary special functions do not need to copy trivial fields");
+
+ // Emit a memcpy if there are multiple trivial fields.
+ bool UseMemCpy = FirstTrivialField != LastTrivialField;
+
+ ASTContext &Ctx = CGF->getContext();
+ const FieldDecl *FirstFD = FirstTrivialField.getPointer();
+ const FieldDecl *LastFD = LastTrivialField.getPointer();
+ uint64_t LastFieldSize = getFieldSize(LastFD, Ctx);
+ QualType FT = FirstFD->getType();
+ bool IsVolatile = FirstTrivialField.getInt();
+
+ // Emit a memcpy if the field is not a scalar.
+ if (CGF->getEvaluationKind(FT) != TEK_Scalar)
+ UseMemCpy = true;
+
+ if (UseMemCpy) {
+ // Emit memcpy.
+ const RecordDecl *RD = FirstFD->getParent();
+ const ASTRecordLayout &RL = Ctx.getASTRecordLayout(RD);
+
+ // Compute the byte offset of the first field.
+ uint64_t FirstOffsetInBits = RL.getFieldOffset(FirstFD->getFieldIndex());
+ CharUnits FirstOffsetInBytes = Ctx.toCharUnitsFromBits(FirstOffsetInBits);
+
+ // Compute the byte offset of the last byte of the last field.
+ uint64_t LastOffsetInBits = RL.getFieldOffset(LastFD->getFieldIndex());
+ CharUnits LastOffsetInBytes =
+ Ctx.toCharUnitsFromBits(LastOffsetInBits + LastFieldSize - 1);
+
+ // Compute the copy size and emit memcpy.
+ // FIXME: If the size is small enough, consider emitting a load/store pair
+ // instead of a memcpy.
+ CharUnits CopySize =
+ LastOffsetInBytes - FirstOffsetInBytes + CharUnits::One();
+ llvm::Value *SizeVal =
+ llvm::ConstantInt::get(CGF->SizeTy, CopySize.getQuantity());
+ TrivialStartDst =
+ CGF->Builder.CreateElementBitCast(TrivialStartDst, CGF->Int8Ty);
+ TrivialStartSrc =
+ CGF->Builder.CreateElementBitCast(TrivialStartSrc, CGF->Int8Ty);
+ CGF->Builder.CreateMemCpy(TrivialStartDst, TrivialStartSrc, SizeVal,
+ IsVolatile);
+ } else {
+ // Emit load/store to copy the single scalar field.
+ // Round up the field size to the size of char and create an integer type
+ // of that size.
+ llvm::Type *Ty = llvm::Type::getIntNTy(
+ CGF->getLLVMContext(),
+ llvm::alignTo(LastFieldSize, Ctx.getCharWidth()));
+ if (IsVolatile)
+ FT = FT.withVolatile();
+ llvm::Type *PtrTy = Ty->getPointerTo();
+ TrivialStartDst = CGF->Builder.CreateBitCast(TrivialStartDst, PtrTy);
+ TrivialStartSrc = CGF->Builder.CreateBitCast(TrivialStartSrc, PtrTy);
+ llvm::Value *SrcVal = CGF->EmitLoadOfScalar(TrivialStartSrc, IsVolatile,
+ FT, SourceLocation());
+ CGF->EmitStoreOfScalar(SrcVal, CGF->MakeAddrLValue(TrivialStartDst, FT),
+ T::isInit());
+ }
+
+ // Reset the pointers.
+ FirstTrivialField = LastTrivialField = decltype(FirstTrivialField)();
+ }
+
+ static bool isBinary() { return T::isBinary(); }
+
+ static const char *getFuncPrefix() { return T::getFuncPrefix(); }
+
+ static QualType::PrimitiveCopyKind isInterestingType(QualType QT) {
+ return T::isInterestingType(QT);
+ }
+
+private:
+ CodeGenFunction *CGF;
+ Address TrivialStartDst, TrivialStartSrc;
+ llvm::PointerIntPair<const FieldDecl *, 1, bool> FirstTrivialField,
+ LastTrivialField;
+};
+
+template <bool IsBinary, bool IsInit> struct SpecialFunc {
+ // Emit IR for objc __strong pointers.
+ static void emitIRStrong(Address DstAddr, Address SrcAddr, QualType QT,
+ CodeGenFunction &CGF) {}
+
+ // This function returns true if the subclass is used to generate special
+ // functions that need two arguments (source and destination arguments for
+ // copy/move constructors/assignment operators).
+ static bool isBinary() { return IsBinary; }
+
+ // This function returns true if the subclass generates a special function for
+ // a constructor.
+ static bool isInit() { return IsInit; }
+};
+
+// Various subclasses of SpecialFunc that are used to customize
+// the IR IRGen generates.
+struct Destructor : SpecialFunc<false, false> {
+ static void emitIRStrong(Address DstAddr, Address SrcAddr, QualType QT,
+ CodeGenFunction &CGF) {
+ CGF.destroyARCStrongImprecise(CGF, DstAddr, QT);
+ }
+ static const char *getFuncPrefix() { return "__destructor_"; }
+
+ static QualType::PrimitiveCopyKind isInterestingType(QualType T) {
+ return T.isNonTrivialToPrimitiveDestroy();
+ }
+ static void callSpecialFunction(Address DstAddr, Address SrcAddr, QualType QT,
+ bool IsVolatile, CodeGenFunction &CGF) {
+ CGF.callCStructDestructor(DstAddr, QT, IsVolatile);
+ }
+};
+
+struct DefaultConstructor : SpecialFunc<false, true> {
+ static void emitIRStrong(Address DstAddr, Address SrcAddr, QualType QT,
+ CodeGenFunction &CGF) {
+ CGF.EmitNullInitialization(DstAddr, QT);
+ }
+ static const char *getFuncPrefix() { return "__default_constructor_"; }
+
+ static QualType::PrimitiveCopyKind isInterestingType(QualType T) {
+ return T.isNonTrivialToPrimitiveDefaultInitialize();
+ }
+ static void callSpecialFunction(Address DstAddr, Address SrcAddr, QualType QT,
+ bool IsVolatile, CodeGenFunction &CGF) {
+ CGF.callCStructDefaultConstructor(DstAddr, QT, IsVolatile);
+ }
+};
+
+struct CopyConstructor : SpecialFunc<true, true> {
+ static void emitIRStrong(Address DstAddr, Address SrcAddr, QualType QT,
+ CodeGenFunction &CGF) {
+ llvm::Value *SrcVal = CGF.EmitLoadOfScalar(
+ SrcAddr, QT.isVolatileQualified(), QT, SourceLocation());
+ llvm::Value *Val = CGF.EmitARCRetain(QT, SrcVal);
+ CGF.EmitStoreOfScalar(Val, CGF.MakeAddrLValue(DstAddr, QT), true);
+ }
+ static const char *getFuncPrefix() { return "__copy_constructor_"; }
+
+ static QualType::PrimitiveCopyKind isInterestingType(QualType T) {
+ return T.isNonTrivialToPrimitiveCopy();
+ }
+ static void callSpecialFunction(Address DstAddr, Address SrcAddr, QualType QT,
+ bool IsVolatile, CodeGenFunction &CGF) {
+ CGF.callCStructCopyConstructor(DstAddr, SrcAddr, QT, IsVolatile);
+ }
+};
+
+struct MoveConstructor : SpecialFunc<true, true> {
+ static void emitIRStrong(Address DstAddr, Address SrcAddr, QualType QT,
+ CodeGenFunction &CGF) {
+ LValue SrcLV = CGF.MakeAddrLValue(SrcAddr, QT);
+ llvm::Value *SrcVal =
+ CGF.EmitLoadOfLValue(SrcLV, SourceLocation()).getScalarVal();
+ CGF.EmitStoreOfScalar(getNullForVariable(SrcLV.getAddress()), SrcLV);
+ CGF.EmitStoreOfScalar(SrcVal, CGF.MakeAddrLValue(DstAddr, QT),
+ /* isInitialization */ true);
+ }
+ static const char *getFuncPrefix() { return "__move_constructor_"; }
+
+ static QualType::PrimitiveCopyKind isInterestingType(QualType T) {
+ return T.isNonTrivialToPrimitiveDestructiveMove();
+ }
+ static void callSpecialFunction(Address DstAddr, Address SrcAddr, QualType QT,
+ bool IsVolatile, CodeGenFunction &CGF) {
+ CGF.callCStructMoveConstructor(DstAddr, SrcAddr, QT, IsVolatile);
+ }
+};
+
+struct CopyAssignment : SpecialFunc<true, false> {
+ static void emitIRStrong(Address DstAddr, Address SrcAddr, QualType QT,
+ CodeGenFunction &CGF) {
+ llvm::Value *SrcVal = CGF.EmitLoadOfScalar(
+ SrcAddr, QT.isVolatileQualified(), QT, SourceLocation());
+ CGF.EmitARCStoreStrong(CGF.MakeAddrLValue(DstAddr, QT), SrcVal, false);
+ }
+ static const char *getFuncPrefix() { return "__copy_assignment_"; }
+
+ static QualType::PrimitiveCopyKind isInterestingType(QualType T) {
+ return T.isNonTrivialToPrimitiveCopy();
+ }
+ static void callSpecialFunction(Address DstAddr, Address SrcAddr, QualType QT,
+ bool IsVolatile, CodeGenFunction &CGF) {
+ CGF.callCStructCopyAssignmentOperator(DstAddr, SrcAddr, QT, IsVolatile);
+ }
+};
+
+struct MoveAssignment : SpecialFunc<true, false> {
+ static void emitIRStrong(Address DstAddr, Address SrcAddr, QualType QT,
+ CodeGenFunction &CGF) {
+ LValue SrcLV = CGF.MakeAddrLValue(SrcAddr, QT);
+ llvm::Value *SrcVal =
+ CGF.EmitLoadOfLValue(SrcLV, SourceLocation()).getScalarVal();
+ CGF.EmitStoreOfScalar(getNullForVariable(SrcLV.getAddress()), SrcLV);
+ LValue DstLV = CGF.MakeAddrLValue(DstAddr, QT);
+ llvm::Value *DstVal =
+ CGF.EmitLoadOfLValue(DstLV, SourceLocation()).getScalarVal();
+ CGF.EmitStoreOfScalar(SrcVal, DstLV);
+ CGF.EmitARCRelease(DstVal, ARCImpreciseLifetime);
+ }
+ static const char *getFuncPrefix() { return "__move_assignment_"; }
+
+ static QualType::PrimitiveCopyKind isInterestingType(QualType T) {
+ return T.isNonTrivialToPrimitiveDestructiveMove();
+ }
+ static void callSpecialFunction(Address DstAddr, Address SrcAddr, QualType QT,
+ bool IsVolatile, CodeGenFunction &CGF) {
+ CGF.callCStructMoveAssignmentOperator(DstAddr, SrcAddr, QT, IsVolatile);
+ }
+};
+} // namespace
+
+void CodeGenFunction::destroyNonTrivialCStruct(CodeGenFunction &CGF,
+ Address Addr, QualType Type) {
+ CGF.callCStructDestructor(Addr, Type, Type.isVolatileQualified());
+}
+
+// A template function to create the special function for a non-trivial C
+// struct. Template parameter 'T' is an instantiation of IRGen.
+template <class T>
+static llvm::Function *
+getSpecialFunction(QualType QT, bool IsVolatile, T &Gen, CharUnits DstAlignment,
+ CharUnits SrcAlignment, CodeGenModule &CGM) {
+ bool IsBinary = T::isBinary();
+ std::string FuncName = getSpecialFuncName<T>(QT, IsVolatile, DstAlignment,
+ SrcAlignment, IsBinary, CGM);
+
+ // If the special function already exists in the module, return it here.
+ if (llvm::Function *F = CGM.getModule().getFunction(FuncName))
+ return F;
+
+ // Create the special function.
+ ASTContext &Ctx = CGM.getContext();
+ CodeGenFunction CGF(CGM);
+ FunctionArgList Args;
+ const CGFunctionInfo &FI = getFunctionInfo(CGM, IsBinary, Args);
+ llvm::FunctionType *FuncTy = CGM.getTypes().GetFunctionType(FI);
+ llvm::Function *F =
+ llvm::Function::Create(FuncTy, llvm::GlobalValue::LinkOnceODRLinkage,
+ FuncName, &CGM.getModule());
+ F->setVisibility(llvm::GlobalValue::HiddenVisibility);
+ CGF.CGM.SetLLVMFunctionAttributes(nullptr, FI, F);
+ CGF.CGM.SetLLVMFunctionAttributesForDefinition(nullptr, F);
+ IdentifierInfo *II = &Ctx.Idents.get(FuncName);
+ FunctionDecl *FD = FunctionDecl::Create(
+ Ctx, Ctx.getTranslationUnitDecl(), SourceLocation(), SourceLocation(), II,
+ Ctx.VoidTy, nullptr, SC_PrivateExtern, false, false);
+ CGF.StartFunction(FD, Ctx.VoidTy, F, FI, Args);
+ Gen.setFunction(CGF);
+ llvm::Value *D = CGF.Builder.CreateLoad(CGF.GetAddrOfLocalVar(Args[0]));
+ Address DstStart = Address(D, DstAlignment), SrcStart = Address::invalid();
+ if (T::isBinary()) {
+ assert(CGF.CurFn->arg_size() == 2 &&
+ "Binary functions expected to have two arguments");
+ llvm::Value *S = CGF.Builder.CreateLoad(CGF.GetAddrOfLocalVar(Args[1]));
+ SrcStart = Address(S, SrcAlignment);
+ }
+ Gen.visitStruct(DstStart, SrcStart, QT, IsVolatile, false);
+ CGF.FinishFunction();
+ return F;
+}
+
+// Default-initialize a non-trivial struct or an array.
+void CodeGenFunction::defaultInitNonTrivialCStructVar(Address DstPtr,
+ QualType QT,
+ bool IsVolatile) {
+ if (getContext().getAsArrayType(QT)) {
+ IRGen<DefaultConstructor> Gen;
+ Gen.setFunction(*this);
+ DstPtr = Builder.CreateBitCast(DstPtr, CGM.Int8PtrPtrTy);
+ Gen.visitArray(DstPtr, Address::invalid(), QT, IsVolatile);
+ } else {
+ callCStructDefaultConstructor(DstPtr, QT, IsVolatile);
+ }
+}
+
+template <class T>
+static void callUnaryFunction(Address DstPtr, QualType QT, bool IsVolatile,
+ CodeGenFunction &CGF) {
+ IRGen<T> Gen;
+ llvm::Function *Fn = getSpecialFunction(
+ QT, IsVolatile, Gen, DstPtr.getAlignment(), CharUnits::Zero(), CGF.CGM);
+ DstPtr = CGF.Builder.CreateBitCast(DstPtr, CGF.CGM.Int8PtrPtrTy);
+ CGF.EmitNounwindRuntimeCall(Fn, DstPtr.getPointer());
+}
+
+template <class T>
+static void callBinaryFunction(Address DstPtr, Address SrcPtr, QualType QT,
+ bool IsVolatile, CodeGenFunction &CGF) {
+ IRGen<T> Gen;
+ llvm::Function *Fn =
+ getSpecialFunction(QT, IsVolatile, Gen, DstPtr.getAlignment(),
+ SrcPtr.getAlignment(), CGF.CGM);
+ DstPtr = CGF.Builder.CreateBitCast(DstPtr, CGF.CGM.Int8PtrPtrTy);
+ SrcPtr = CGF.Builder.CreateBitCast(SrcPtr, CGF.CGM.Int8PtrPtrTy);
+ CGF.EmitNounwindRuntimeCall(Fn, {DstPtr.getPointer(), SrcPtr.getPointer()});
+}
+
+// Functions to emit calls to the special functions of a non-trivial C struct.
+void CodeGenFunction::callCStructDefaultConstructor(Address DstPtr, QualType QT,
+ bool IsVolatile) {
+ callUnaryFunction<DefaultConstructor>(DstPtr, QT, IsVolatile, *this);
+}
+
+void CodeGenFunction::callCStructDestructor(Address DstPtr, QualType QT,
+ bool IsVolatile) {
+ callUnaryFunction<Destructor>(DstPtr, QT, IsVolatile, *this);
+}
+
+void CodeGenFunction::callCStructCopyConstructor(Address DstPtr, Address SrcPtr,
+ QualType QT, bool IsVolatile) {
+ callBinaryFunction<CopyConstructor>(DstPtr, SrcPtr, QT, IsVolatile, *this);
+}
+
+void CodeGenFunction::callCStructCopyAssignmentOperator(Address DstPtr,
+ Address SrcPtr,
+ QualType QT,
+ bool IsVolatile) {
+ callBinaryFunction<CopyAssignment>(DstPtr, SrcPtr, QT, IsVolatile, *this);
+}
+
+void CodeGenFunction::callCStructMoveConstructor(Address DstPtr, Address SrcPtr,
+ QualType QT, bool IsVolatile) {
+ callBinaryFunction<MoveConstructor>(DstPtr, SrcPtr, QT, IsVolatile, *this);
+}
+
+void CodeGenFunction::callCStructMoveAssignmentOperator(Address DstPtr,
+ Address SrcPtr,
+ QualType QT,
+ bool IsVolatile) {
+ callBinaryFunction<MoveAssignment>(DstPtr, SrcPtr, QT, IsVolatile, *this);
+}
Index: lib/CodeGen/CGExprAgg.cpp
===================================================================
--- lib/CodeGen/CGExprAgg.cpp
+++ lib/CodeGen/CGExprAgg.cpp
@@ -77,7 +77,9 @@
void EmitAggLoadOfLValue(const Expr *E);
/// EmitFinalDestCopy - Perform the final copy to DestPtr, if desired.
- void EmitFinalDestCopy(QualType type, const LValue &src);
+ /// SrcIsRValue is true if source comes from an RValue.
+ void EmitFinalDestCopy(QualType type, const LValue &src,
+ bool SrcIsRValue = false);
void EmitFinalDestCopy(QualType type, RValue src);
void EmitCopy(QualType type, const AggValueSlot &dest,
const AggValueSlot &src);
@@ -245,6 +247,12 @@
/// directly into the return value slot. Otherwise, a final move
/// will be performed.
void AggExprEmitter::EmitMoveFromReturnSlot(const Expr *E, RValue src) {
+ // Push destructor if the result is ignored and the type is a C struct that
+ // is non-trivial to destroy.
+ QualType Ty = E->getType();
+ if (Dest.isIgnored() && Ty.isNonTrivialToPrimitiveDestroyStruct())
+ CGF.pushDestroy(Ty.isDestructedType(), src.getAggregateAddress(), Ty);
+
if (shouldUseDestForReturnSlot()) {
// Logically, Dest.getAddr() should equal Src.getAggregateAddr().
// The possibility of undef rvalues complicates that a lot,
@@ -261,18 +269,49 @@
void AggExprEmitter::EmitFinalDestCopy(QualType type, RValue src) {
assert(src.isAggregate() && "value must be aggregate value!");
LValue srcLV = CGF.MakeAddrLValue(src.getAggregateAddress(), type);
- EmitFinalDestCopy(type, srcLV);
+ EmitFinalDestCopy(type, srcLV, true);
}
/// EmitFinalDestCopy - Perform the final copy to DestPtr, if desired.
-void AggExprEmitter::EmitFinalDestCopy(QualType type, const LValue &src) {
+void AggExprEmitter::EmitFinalDestCopy(QualType type, const LValue &src,
+ bool SrcIsRValue) {
// If Dest is ignored, then we're evaluating an aggregate expression
// in a context that doesn't care about the result. Note that loads
// from volatile l-values force the existence of a non-ignored
// destination.
if (Dest.isIgnored())
return;
+ // Copy non-trivial C structs here.
+
+ // For simplicity, both the destination and the source are treated as being
+ // volatile if either is volatile.
+ bool IsVolatile = Dest.isVolatile() || src.isVolatile();
+
+ if (SrcIsRValue) {
+ if (type.isNonTrivialToPrimitiveDestructiveMoveStruct()) {
+ if (Dest.isPotentiallyAliased())
+ CGF.callCStructMoveAssignmentOperator(Dest.getAddress(),
+ src.getAddress(), type,
+ IsVolatile);
+ else
+ CGF.callCStructMoveConstructor(Dest.getAddress(), src.getAddress(),
+ type, IsVolatile);
+ return;
+ }
+ } else {
+ if (type.isNonTrivialToPrimitiveCopyStruct()) {
+ if (Dest.isPotentiallyAliased())
+ CGF.callCStructCopyAssignmentOperator(Dest.getAddress(),
+ src.getAddress(), type,
+ IsVolatile);
+ else
+ CGF.callCStructCopyConstructor(Dest.getAddress(), src.getAddress(),
+ type, IsVolatile);
+ return;
+ }
+ }
+
AggValueSlot srcAgg =
AggValueSlot::forLValue(src, AggValueSlot::IsDestructed,
needsGC(type), AggValueSlot::IsAliased);
Index: lib/CodeGen/CGDeclCXX.cpp
===================================================================
--- lib/CodeGen/CGDeclCXX.cpp
+++ lib/CodeGen/CGDeclCXX.cpp
@@ -79,6 +79,7 @@
case QualType::DK_objc_strong_lifetime:
case QualType::DK_objc_weak_lifetime:
+ case QualType::DK_c_struct_strong_field:
// We don't care about releasing objects during process teardown.
assert(!D.getTLSKind() && "should have rejected this");
return;
Index: lib/CodeGen/CGDecl.cpp
===================================================================
--- lib/CodeGen/CGDecl.cpp
+++ lib/CodeGen/CGDecl.cpp
@@ -1232,6 +1232,15 @@
if (emission.IsByRef)
emitByrefStructureInit(emission);
+ // Initialize the variable here if it doesn't have a initializer and it is a
+ // C struct that is non-trivial to initialize or an array containing such a
+ // struct.
+ if (!Init && type.isNonTrivialToPrimitiveDefaultInitializeStruct()) {
+ defaultInitNonTrivialCStructVar(emission.getAllocatedAddress(), type,
+ type.isVolatileQualified());
+ return;
+ }
+
if (isTrivialInitializer(Init))
return;
@@ -1406,6 +1415,11 @@
case QualType::DK_objc_weak_lifetime:
break;
+
+ case QualType::DK_c_struct_strong_field:
+ destroyer = CodeGenFunction::destroyNonTrivialCStruct;
+ cleanupKind = getARCCleanupKind();
+ break;
}
// If we haven't chosen a more specific destroyer, use the default.
@@ -1467,6 +1481,8 @@
return destroyARCStrongPrecise;
case QualType::DK_objc_weak_lifetime:
return destroyARCWeak;
+ case QualType::DK_c_struct_strong_field:
+ return destroyNonTrivialCStruct;
}
llvm_unreachable("Unknown DestructionKind");
}
@@ -1814,10 +1830,13 @@
// Don't push a cleanup in a thunk for a method that will also emit a
// cleanup.
if (!IsScalar && !CurFuncIsThunk &&
- getTarget().getCXXABI().areArgsDestroyedLeftToRightInCallee()) {
- const CXXRecordDecl *RD = Ty->getAsCXXRecordDecl();
- if (RD && RD->hasNonTrivialDestructor())
- pushDestroy(QualType::DK_cxx_destructor, DeclPtr, Ty);
+ getContext().isParamDestroyedInCallee(Ty)) {
+ if (QualType::DestructionKind DtorKind = Ty.isDestructedType()) {
+ assert((DtorKind == QualType::DK_cxx_destructor ||
+ DtorKind == QualType::DK_c_struct_strong_field) &&
+ "unexpected destructor type");
+ pushDestroy(DtorKind, DeclPtr, Ty);
+ }
}
} else {
// Otherwise, create a temporary to hold the value.
Index: lib/CodeGen/CGCall.cpp
===================================================================
--- lib/CodeGen/CGCall.cpp
+++ lib/CodeGen/CGCall.cpp
@@ -3143,7 +3143,8 @@
static void deactivateArgCleanupsBeforeCall(CodeGenFunction &CGF,
const CallArgList &CallArgs) {
- assert(CGF.getTarget().getCXXABI().areArgsDestroyedLeftToRightInCallee());
+ assert(!CGF.getLangOpts().CPlusPlus ||
+ CGF.getTarget().getCXXABI().areArgsDestroyedLeftToRightInCallee());
ArrayRef<CallArgList::CallArgCleanup> Cleanups =
CallArgs.getCleanupsToDeactivate();
// Iterate in reverse to increase the likelihood of popping the cleanup.
@@ -3456,10 +3457,15 @@
QualType Ty;
void Emit(CodeGenFunction &CGF, Flags flags) override {
- const CXXDestructorDecl *Dtor = Ty->getAsCXXRecordDecl()->getDestructor();
- assert(!Dtor->isTrivial());
- CGF.EmitCXXDestructorCall(Dtor, Dtor_Complete, /*for vbase*/ false,
- /*Delegating=*/false, Addr);
+ QualType::DestructionKind DtorKind = Ty.isDestructedType();
+ if (DtorKind == QualType::DK_cxx_destructor) {
+ const CXXDestructorDecl *Dtor = Ty->getAsCXXRecordDecl()->getDestructor();
+ assert(!Dtor->isTrivial());
+ CGF.EmitCXXDestructorCall(Dtor, Dtor_Complete, /*for vbase*/ false,
+ /*Delegating=*/false, Addr);
+ } else {
+ CGF.callCStructDestructor(Addr, Ty, Ty.isVolatileQualified());
+ }
}
};
@@ -3500,28 +3506,32 @@
// In the Microsoft C++ ABI, aggregate arguments are destructed by the callee.
// However, we still have to push an EH-only cleanup in case we unwind before
// we make it to the call.
- if (HasAggregateEvalKind &&
- CGM.getTarget().getCXXABI().areArgsDestroyedLeftToRightInCallee()) {
+ if (HasAggregateEvalKind && getContext().isParamDestroyedInCallee(type)) {
// If we're using inalloca, use the argument memory. Otherwise, use a
// temporary.
AggValueSlot Slot;
if (args.isUsingInAlloca())
Slot = createPlaceholderSlot(*this, type);
else
Slot = CreateAggTemp(type, "agg.tmp");
- const CXXRecordDecl *RD = type->getAsCXXRecordDecl();
- bool DestroyedInCallee =
- RD && RD->hasNonTrivialDestructor() &&
- CGM.getCXXABI().getRecordArgABI(RD) != CGCXXABI::RAA_Default;
+ bool DestroyedInCallee = true, NeedsEHCleanup = true;
+ if (const auto *RD = type->getAsCXXRecordDecl()) {
+ DestroyedInCallee =
+ RD && RD->hasNonTrivialDestructor() &&
+ CGM.getCXXABI().getRecordArgABI(RD) != CGCXXABI::RAA_Default;
+ } else {
+ NeedsEHCleanup = needsEHCleanup(type.isDestructedType());
+ }
+
if (DestroyedInCallee)
Slot.setExternallyDestructed();
EmitAggExpr(E, Slot);
RValue RV = Slot.asRValue();
args.add(RV, type);
- if (DestroyedInCallee) {
+ if (DestroyedInCallee && NeedsEHCleanup) {
// Create a no-op GEP between the placeholder and the cleanup so we can
// RAUW it successfully. It also serves as a marker of the first
// instruction where the cleanup is active.
Index: lib/CodeGen/CGBlocks.cpp
===================================================================
--- lib/CodeGen/CGBlocks.cpp
+++ lib/CodeGen/CGBlocks.cpp
@@ -477,6 +477,12 @@
info.NeedsCopyDispose = true;
info.HasCXXObject = true;
+ // So do C structs that require non-trivial copy construction or
+ // destruction.
+ } else if (variable->getType().isNonTrivialToPrimitiveCopyStruct() ||
+ variable->getType().isNonTrivialToPrimitiveDestroyStruct()) {
+ info.NeedsCopyDispose = true;
+
// And so do types with destructors.
} else if (CGM.getLangOpts().CPlusPlus) {
if (const CXXRecordDecl *record =
@@ -1513,6 +1519,7 @@
CXXRecord, // Copy or destroy
ARCWeak,
ARCStrong,
+ NonTrivialCStruct,
BlockObject, // Assign or release
None
};
@@ -1548,6 +1555,9 @@
Flags |= BLOCK_FIELD_IS_WEAK;
return std::make_pair(BlockCaptureEntityKind::BlockObject, Flags);
}
+ if (T.isNonTrivialToPrimitiveCopyStruct())
+ return std::make_pair(BlockCaptureEntityKind::NonTrivialCStruct,
+ BlockFieldFlags());
if (!T->isObjCRetainableType())
// For all other types, the memcpy is fine.
return std::make_pair(BlockCaptureEntityKind::None, Flags);
@@ -1677,6 +1687,13 @@
EmitSynthesizedCXXCopyCtor(dstField, srcField, CI.getCopyExpr());
} else if (CopiedCapture.Kind == BlockCaptureEntityKind::ARCWeak) {
EmitARCCopyWeak(dstField, srcField);
+ // If this is a C struct that requires non-trivial copy construction, emit a
+ // call to its copy constructor.
+ } else if (CopiedCapture.Kind ==
+ BlockCaptureEntityKind::NonTrivialCStruct) {
+ QualType varType = CI.getVariable()->getType();
+ callCStructCopyConstructor(dstField, srcField, varType,
+ varType.isVolatileQualified());
} else {
llvm::Value *srcValue = Builder.CreateLoad(srcField, "blockcopy.src");
if (CopiedCapture.Kind == BlockCaptureEntityKind::ARCStrong) {
@@ -1749,6 +1766,10 @@
return std::make_pair(BlockCaptureEntityKind::CXXRecord, BlockFieldFlags());
}
+ if (T.isNonTrivialToPrimitiveDestroyStruct())
+ return std::make_pair(BlockCaptureEntityKind::NonTrivialCStruct,
+ BlockFieldFlags());
+
// Other types don't need to be destroy explicitly.
if (!T->isObjCRetainableType())
return std::make_pair(BlockCaptureEntityKind::None, Flags);
@@ -1853,6 +1874,13 @@
} else if (DestroyedCapture.Kind == BlockCaptureEntityKind::ARCStrong) {
EmitARCDestroyStrong(srcField, ARCImpreciseLifetime);
+ // If this is a C struct that requires non-trivial destruction, emit a call
+ // to its destructor.
+ } else if (DestroyedCapture.Kind ==
+ BlockCaptureEntityKind::NonTrivialCStruct) {
+ QualType varType = CI.getVariable()->getType();
+ pushDestroy(varType.isDestructedType(), srcField, varType);
+
// Otherwise we call _Block_object_dispose. It wouldn't be too
// hard to just emit this as a cleanup if we wanted to make sure
// that things were done in reverse.
@@ -2020,6 +2048,36 @@
id.AddPointer(VarType.getCanonicalType().getAsOpaquePtr());
}
};
+
+/// Emits the copy/dispose helpers for a __block variable that is a non-trivial
+/// C struct.
+class NonTrivialCStructByrefHelpers final : public BlockByrefHelpers {
+ QualType VarType;
+
+public:
+ NonTrivialCStructByrefHelpers(CharUnits alignment, QualType type)
+ : BlockByrefHelpers(alignment), VarType(type) {}
+
+ void emitCopy(CodeGenFunction &CGF, Address destField,
+ Address srcField) override {
+ CGF.callCStructMoveConstructor(destField, srcField, VarType,
+ VarType.isVolatileQualified());
+ }
+
+ bool needsDispose() const override {
+ return VarType.isNonTrivialToPrimitiveDestroyStruct();
+ }
+
+ void emitDispose(CodeGenFunction &CGF, Address field) override {
+ EHScopeStack::stable_iterator cleanupDepth = CGF.EHStack.stable_begin();
+ CGF.pushDestroy(VarType.isDestructedType(), field, VarType);
+ CGF.PopCleanupBlocks(cleanupDepth);
+ }
+
+ void profileImpl(llvm::FoldingSetNodeID &id) const override {
+ id.AddPointer(VarType.getCanonicalType().getAsOpaquePtr());
+ }
+};
} // end anonymous namespace
static llvm::Constant *
@@ -2205,6 +2263,13 @@
CGM, byrefInfo, CXXByrefHelpers(valueAlignment, type, copyExpr));
}
+ // If type is a non-trivial C struct type that is non-trivial to
+ // destructly move or destroy, build the copy and dispose helpers.
+ if (type.isNonTrivialToPrimitiveDestructiveMoveStruct() ||
+ type.isNonTrivialToPrimitiveDestroyStruct())
+ return ::buildByrefHelpers(
+ CGM, byrefInfo, NonTrivialCStructByrefHelpers(valueAlignment, type));
+
// Otherwise, if we don't have a retainable type, there's nothing to do.
// that the runtime does extra copies.
if (!type->isObjCRetainableType()) return nullptr;
Index: lib/AST/Type.cpp
===================================================================
--- lib/AST/Type.cpp
+++ lib/AST/Type.cpp
@@ -2207,6 +2207,66 @@
getObjCLifetime() != Qualifiers::OCL_Weak;
}
+static bool hasStructWithObjCStrongField(QualType QT) {
+ if (const auto *RT = QT->getBaseElementTypeUnsafe()->getAs<RecordType>())
+ return RT->getDecl()->hasStrongObjCPointer();
+ return false;
+}
+
+QualType::PrimitiveCopyKind
+QualType::isNonTrivialToPrimitiveDefaultInitialize() const {
+ if (hasStructWithObjCStrongField(*this))
+ return PCK_Struct;
+
+ Qualifiers::ObjCLifetime Lifetime = getQualifiers().getObjCLifetime();
+ if (Lifetime == Qualifiers::OCL_Strong)
+ return PCK_Strong;
+
+ return PCK_Trivial;
+}
+
+QualType::PrimitiveCopyKind QualType::isNonTrivialToPrimitiveCopy() const {
+ if (hasStructWithObjCStrongField(*this))
+ return PCK_Struct;
+
+ Qualifiers::ObjCLifetime Lifetime = getQualifiers().getObjCLifetime();
+ if (Lifetime == Qualifiers::OCL_Strong)
+ return PCK_Strong;
+
+ return PCK_Trivial;
+}
+
+QualType::PrimitiveCopyKind
+QualType::isNonTrivialToPrimitiveDestructiveMove() const {
+ if (hasStructWithObjCStrongField(*this))
+ return PCK_Struct;
+
+ Qualifiers::ObjCLifetime Lifetime = getQualifiers().getObjCLifetime();
+ if (Lifetime == Qualifiers::OCL_Strong)
+ return PCK_Strong;
+
+ return PCK_Trivial;
+}
+
+QualType::PrimitiveCopyKind QualType::isNonTrivialToPrimitiveDestroy() const {
+ if (hasStructWithObjCStrongField(*this))
+ return PCK_Struct;
+
+ Qualifiers::ObjCLifetime Lifetime = getQualifiers().getObjCLifetime();
+ if (Lifetime == Qualifiers::OCL_Strong)
+ return PCK_Strong;
+
+ return PCK_Trivial;
+}
+
+bool QualType::hasObjCStrongPointer() const {
+ if (hasStructWithObjCStrongField(*this))
+ return true;
+
+ Qualifiers::ObjCLifetime Lifetime = getQualifiers().getObjCLifetime();
+ return Lifetime == Qualifiers::OCL_Strong;
+}
+
bool Type::isLiteralType(const ASTContext &Ctx) const {
if (isDependentType())
return false;
@@ -3895,6 +3955,11 @@
return DK_objc_weak_lifetime;
}
+ // See if this is a C struct that is non-trivial to destroy or an array that
+ // contains such a struct.
+ if (hasStructWithObjCStrongField(type))
+ return DK_c_struct_strong_field;
+
/// Currently, the only destruction kind we recognize is C++ objects
/// with non-trivial destructors.
const CXXRecordDecl *record =
Index: lib/AST/Decl.cpp
===================================================================
--- lib/AST/Decl.cpp
+++ lib/AST/Decl.cpp
@@ -3915,7 +3915,7 @@
: TagDecl(DK, TK, C, DC, IdLoc, Id, PrevDecl, StartLoc),
HasFlexibleArrayMember(false), AnonymousStructOrUnion(false),
HasObjectMember(false), HasVolatileMember(false),
- LoadedFieldsFromExternalStorage(false) {
+ LoadedFieldsFromExternalStorage(false), HasStrongObjCPointer(false) {
assert(classof(static_cast<Decl*>(this)) && "Invalid Kind!");
}
Index: lib/AST/ASTContext.cpp
===================================================================
--- lib/AST/ASTContext.cpp
+++ lib/AST/ASTContext.cpp
@@ -2640,6 +2640,12 @@
}
}
+bool ASTContext::isParamDestroyedInCallee(QualType T) const {
+ QualType::DestructionKind DtorKind = T.isDestructedType();
+ return getTargetInfo().getCXXABI().areArgsDestroyedLeftToRightInCallee() ||
+ DtorKind == QualType::DK_c_struct_strong_field;
+}
+
/// getComplexType - Return the uniqued reference to the type for a complex
/// number with the specified element type.
QualType ASTContext::getComplexType(QualType T) const {
@@ -5766,6 +5772,12 @@
return true;
}
+ // The block needs copy/destroy helpers if Ty is a C struct type that is
+ // non-trivial to destructly move or destroy.
+ if (Ty.isNonTrivialToPrimitiveDestructiveMoveStruct() ||
+ Ty.isNonTrivialToPrimitiveDestroyStruct())
+ return true;
+
if (!Ty->isObjCRetainableType()) return false;
Qualifiers qs = Ty.getQualifiers();
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -5116,12 +5116,17 @@
"jump bypasses initialization of __strong variable">;
def note_protected_by_objc_weak_init : Note<
"jump bypasses initialization of __weak variable">;
+def note_protected_by_non_trivial_c_struct_init : Note<
+ "jump bypasses initialization of variable of non-trivial C struct type">;
def note_enters_block_captures_cxx_obj : Note<
"jump enters lifetime of block which captures a destructible C++ object">;
def note_enters_block_captures_strong : Note<
"jump enters lifetime of block which strongly captures a variable">;
def note_enters_block_captures_weak : Note<
"jump enters lifetime of block which weakly captures a variable">;
+def note_enters_block_captures_non_trivial_c_struct : Note<
+ "jump enters lifetime of block which captures a C struct that is non-trivial "
+ "to destroy">;
def note_exits_cleanup : Note<
"jump exits scope of variable with __attribute__((cleanup))">;
@@ -5162,6 +5167,9 @@
"jump exits lifetime of block which strongly captures a variable">;
def note_exits_block_captures_weak : Note<
"jump exits lifetime of block which weakly captures a variable">;
+def note_exits_block_captures_non_trivial_c_struct : Note<
+ "jump exits lifetime of block which captures a C struct that is non-trivial "
+ "to destroy">;
def err_func_returning_qualified_void : ExtWarn<
"function cannot return qualified void type %0">,
@@ -7180,6 +7188,10 @@
"cannot pass object with interface type %1 by value to variadic "
"%select{function|block|method|constructor}2; expected type from format "
"string was %3">;
+def err_cannot_pass_non_trivial_c_struct_to_vararg : Error<
+ "cannot pass non-trivial C object of type %0 by value to variadic "
+ "%select{function|block|method|constructor}1">;
+
def err_cannot_pass_objc_interface_to_vararg : Error<
"cannot pass object with interface type %0 by value through variadic "
Index: include/clang/AST/Type.h
===================================================================
--- include/clang/AST/Type.h
+++ include/clang/AST/Type.h
@@ -1082,11 +1082,70 @@
// true when Type is objc's weak and weak is enabled but ARC isn't.
bool isNonWeakInMRRWithObjCWeak(const ASTContext &Context) const;
+ // Enum used to distinguish the different kinds of fields of non-trivial C
+ // structs.
+ enum PrimitiveCopyKind {
+ PCK_Trivial, // a field of a trivial type.
+ PCK_Strong, // objc strong pointer.
+ PCK_Struct // non-trivial C struct.
+ };
+
+ /// Functions to query basic properties of non-trivial C struct types.
+
+ /// Check if this is a non-trivial type that would cause a C struct
+ /// transitively containing this type to be non-trivial to default initialize
+ /// and return the kind.
+ PrimitiveCopyKind isNonTrivialToPrimitiveDefaultInitialize() const;
+
+ /// Check if this is a non-trivial type that would cause a C struct
+ /// transitively containing this type to be non-trivial to copy and return the
+ /// kind.
+ PrimitiveCopyKind isNonTrivialToPrimitiveCopy() const;
+
+ /// Check if this is a non-trivial type that would cause a C struct
+ /// transitively containing this type to be non-trivial to destructively
+ /// move and return the kind.
+ PrimitiveCopyKind isNonTrivialToPrimitiveDestructiveMove() const;
+
+ /// Check if this is a non-trivial type that would cause a C struct
+ /// transitively containing this type to be non-trivial to destroy and return
+ /// the kind.
+ PrimitiveCopyKind isNonTrivialToPrimitiveDestroy() const;
+
+ /// Returns true if this type is a C struct that is non-trivial to
+ /// default-initialize or an array that contains such a struct.
+ bool isNonTrivialToPrimitiveDefaultInitializeStruct() const {
+ return isNonTrivialToPrimitiveDefaultInitialize() == PCK_Struct;
+ }
+
+ /// Returns true if this type is a C struct that is non-trivial to copy or an
+ /// array that contains such a struct.
+ bool isNonTrivialToPrimitiveCopyStruct() const {
+ return isNonTrivialToPrimitiveCopy() == PCK_Struct;
+ }
+
+ /// Returns true if this type is a C struct that is non-trivial to
+ /// destructively move or an array that contains such a struct.
+ bool isNonTrivialToPrimitiveDestructiveMoveStruct() const {
+ return isNonTrivialToPrimitiveDestructiveMove() == PCK_Struct;
+ }
+
+ /// Returns true if this type is a C struct that is non-trivial to destroy or
+ /// an array that contains such a struct.
+ bool isNonTrivialToPrimitiveDestroyStruct() const {
+ return isNonTrivialToPrimitiveDestroy() == PCK_Struct;
+ }
+
+ /// Returns true if this is an objc __strong pointer or a type containing a
+ /// struct that has an objc __strong pointer field.
+ bool hasObjCStrongPointer() const;
+
enum DestructionKind {
DK_none,
DK_cxx_destructor,
DK_objc_strong_lifetime,
- DK_objc_weak_lifetime
+ DK_objc_weak_lifetime,
+ DK_c_struct_strong_field
};
/// Returns a nonzero value if objects of this type require
Index: include/clang/AST/Decl.h
===================================================================
--- include/clang/AST/Decl.h
+++ include/clang/AST/Decl.h
@@ -3525,6 +3525,9 @@
/// when needed.
mutable bool LoadedFieldsFromExternalStorage : 1;
+ /// Basic properties of non-trivial C structs.
+ bool HasStrongObjCPointer : 1;
+
protected:
RecordDecl(Kind DK, TagKind TK, const ASTContext &C, DeclContext *DC,
SourceLocation StartLoc, SourceLocation IdLoc,
@@ -3584,6 +3587,31 @@
LoadedFieldsFromExternalStorage = val;
}
+ /// Functions to query basic properties of non-trivial C structs.
+ bool isNonTrivialToPrimitiveDefaultInitialize() const {
+ return HasStrongObjCPointer;
+ }
+
+ bool isNonTrivialToPrimitiveCopy() const {
+ return HasStrongObjCPointer;
+ }
+
+ bool isNonTrivialToPrimitiveDestructiveMove() const {
+ return HasStrongObjCPointer;
+ }
+
+ bool isNonTrivialToPrimitiveDestroy() const {
+ return HasStrongObjCPointer;
+ }
+
+ bool hasStrongObjCPointer() const {
+ return HasStrongObjCPointer;
+ }
+
+ void setHasStrongObjCPointer(bool val) {
+ HasStrongObjCPointer = val;
+ }
+
/// \brief Determines whether this declaration represents the
/// injected class name.
///
Index: include/clang/AST/ASTContext.h
===================================================================
--- include/clang/AST/ASTContext.h
+++ include/clang/AST/ASTContext.h
@@ -1179,6 +1179,9 @@
const FunctionProtoType::ExceptionSpecInfo &ESI,
bool AsWritten = false);
+ /// Determine whether a type should be detructed in the callee function.
+ bool isParamDestroyedInCallee(QualType T) const;
+
/// \brief Return the uniqued reference to the type for a complex
/// number with the specified element type.
QualType getComplexType(QualType T) const;
Index: docs/LanguageExtensions.rst
===================================================================
--- docs/LanguageExtensions.rst
+++ docs/LanguageExtensions.rst
@@ -1186,12 +1186,14 @@
Clang provides support for :doc:`automated reference counting
<AutomaticReferenceCounting>` in Objective-C, which eliminates the need
-for manual ``retain``/``release``/``autorelease`` message sends. There are two
+for manual ``retain``/``release``/``autorelease`` message sends. There are three
feature macros associated with automatic reference counting:
``__has_feature(objc_arc)`` indicates the availability of automated reference
counting in general, while ``__has_feature(objc_arc_weak)`` indicates that
automated reference counting also includes support for ``__weak`` pointers to
-Objective-C objects.
+Objective-C objects. ``__has_feature(objc_arc_fields)`` indicates that C structs
+are allowed to have fields that are pointers to Objective-C objects managed by
+automatic reference counting.
.. _objc-fixed-enum:
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits