ahatanak created this revision.
ahatanak added reviewers: rjmccall, doug.gregor, rsmith.
Herald added a subscriber: mgorny.

ObjectiveC ARC in C mode currently disallows having __strong and __weak 
pointers in structs. This patch takes the first step towards lifting that 
restriction. In order to properly manage the lifetimes of the objects owned by 
the structs, this patch modifies IRGen to synthesize special functions for 
structs with __strong pointers and call them when those structs have to be 
initialized, copied, and destructed similarly to what C++ special member 
functions of non-trivial classes do.

I plan to send a patch that allows __weak pointers in structs after this patch 
is committed.


https://reviews.llvm.org/D41228

Files:
  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/CMakeLists.txt
  lib/CodeGen/CodeGenCStruct.cpp
  lib/CodeGen/CodeGenFunction.cpp
  lib/CodeGen/CodeGenFunction.h
  lib/Sema/JumpDiagnostics.cpp
  lib/Sema/SemaDecl.cpp
  lib/Sema/SemaExpr.cpp
  test/ARCMT/checking.m
  test/CodeGenObjC/strong-in-c-struct.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}}
-                // expected-note@arc-system-header.h: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}}
-                         // expected-note@arc-system-header.h: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/CodeGenObjC/strong-in-c-struct.m
===================================================================
--- /dev/null
+++ test/CodeGenObjC/strong-in-c-struct.m
@@ -0,0 +1,483 @@
+// 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 f0;
+  id f1;
+} Strong;
+
+typedef struct {
+  Strong f0;
+  id f1;
+} StrongOuter;
+
+typedef struct {
+  int f0;
+  volatile id f1;
+} StrongVolatile;
+
+typedef struct {
+  BlockTy f0;
+} StrongBlock;
+
+typedef struct {
+  id f0[2][2];
+} IDArray;
+
+typedef struct {
+  Strong f0[2][2];
+} StructArray;
+
+Strong getStrong(void);
+StrongOuter getStrongOuter(void);
+void calleeStrong(Strong);
+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_s8_s16(i8** %[[V0]])
+// CHECK: ret void
+
+// CHECK: define linkonce_odr hidden void @__default_constructor_8_s8_s16(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_s8(i8** %[[V0]])
+// 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 @__default_constructor_8_s8(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 8
+// 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_s8_s16(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_s8(i8** %[[V0]])
+// 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
+
+// CHECK: define linkonce_odr hidden void @__destructor_8_s8(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 8
+// 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 @llvm.memcpy.p0i8.p0i8.i64(i8* %[[V1]], i8* %[[V2]], i64 24, i32 8, i1 false)
+// CHECK: %[[V3:.*]] = bitcast %[[STRUCT_STRONGOUTER]]* %[[T]] to i8**
+// CHECK: %[[V4:.*]] = bitcast %[[STRUCT_STRONGOUTER]]* %[[V0]] to i8**
+// CHECK: call void @__copy_constructor_8_8_s8_s16(i8** %[[V3]], i8** %[[V4]])
+// CHECK: %[[V5:.*]] = bitcast %[[STRUCT_STRONGOUTER]]* %[[T]] to i8**
+// CHECK: call void @__destructor_8_s8_s16(i8** %[[V5]])
+// CHECK: ret void
+
+// CHECK: define linkonce_odr hidden void @__copy_constructor_8_8_s8_s16(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_s8(i8** %[[V0]], i8** %[[V1]])
+// CHECK: %[[V2:.*]] = bitcast i8** %[[V0]] to i8*
+// CHECK: %[[V3:.*]] = getelementptr inbounds i8, i8* %[[V2]], i64 16
+// CHECK: %[[V4:.*]] = bitcast i8* %[[V3]] to i8**
+// CHECK: %[[V5:.*]] = bitcast i8** %[[V1]] to i8*
+// CHECK: %[[V6:.*]] = getelementptr inbounds i8, i8* %[[V5]], i64 16
+// 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: ret void
+
+// CHECK: define linkonce_odr hidden void @__copy_constructor_8_8_s8(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 8
+// CHECK: %[[V4:.*]] = bitcast i8* %[[V3]] to i8**
+// CHECK: %[[V5:.*]] = bitcast i8** %[[V1]] to i8*
+// CHECK: %[[V6:.*]] = getelementptr inbounds i8, i8* %[[V5]], i64 8
+// 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: ret void
+
+void test_copy_constructor_StrongOuter(StrongOuter *s) {
+  StrongOuter t = *s;
+}
+
+// CHECK: define void @test_copy_assignment_StrongOuter(%[[STRUCT_STRONGOUTER:.*]]* %[[D:.*]], %[[STRUCT_STRONGOUTER]]* %[[S:.*]])
+// CHECK: %[[D_ADDR:.*]] = alloca %[[STRUCT_STRONGOUTER]]*, align 8
+// CHECK: %[[S_ADDR:.*]] = alloca %[[STRUCT_STRONGOUTER]]*, align 8
+// CHECK: store %[[STRUCT_STRONGOUTER]]* %[[D]], %[[STRUCT_STRONGOUTER]]** %[[D_ADDR]], align 8
+// CHECK: store %[[STRUCT_STRONGOUTER]]* %[[S]], %[[STRUCT_STRONGOUTER]]** %[[S_ADDR]], align 8
+// CHECK: %[[V0:.*]] = load %[[STRUCT_STRONGOUTER]]*, %[[STRUCT_STRONGOUTER]]** %[[D_ADDR]], align 8
+// CHECK: %[[V1:.*]] = load %[[STRUCT_STRONGOUTER]]*, %[[STRUCT_STRONGOUTER]]** %[[S_ADDR]], align 8
+// CHECK: %[[V2:.*]] = bitcast %[[STRUCT_STRONGOUTER]]* %[[V0]] to i8*
+// CHECK: %[[V3:.*]] = bitcast %[[STRUCT_STRONGOUTER]]* %[[V1]] to i8*
+// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* %[[V2]], i8* %[[V3]], i64 24, i32 8, i1 false)
+// CHECK: %[[V4:.*]] = bitcast %[[STRUCT_STRONGOUTER]]* %[[V0]] to i8**
+// CHECK: %[[V5:.*]] = bitcast %[[STRUCT_STRONGOUTER]]* %[[V1]] to i8**
+// CHECK: call void @__copy_assignment_8_8_s8_s16(i8** %[[V4]], i8** %[[V5]])
+// CHECK: ret void
+
+// CHECK: define linkonce_odr hidden void @__copy_assignment_8_8_s8_s16(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_assignment_8_8_s8(i8** %[[V0]], i8** %[[V1]])
+// CHECK: %[[V2:.*]] = bitcast i8** %[[V0]] to i8*
+// CHECK: %[[V3:.*]] = getelementptr inbounds i8, i8* %[[V2]], i64 16
+// CHECK: %[[V4:.*]] = bitcast i8* %[[V3]] to i8**
+// CHECK: %[[V5:.*]] = bitcast i8** %[[V1]] to i8*
+// CHECK: %[[V6:.*]] = getelementptr inbounds i8, i8* %[[V5]], i64 16
+// CHECK: %[[V7:.*]] = bitcast i8* %[[V6]] to i8**
+// CHECK: %[[V8:.*]] = load i8*, i8** %[[V7]], align 8
+// CHECK: call void @objc_storeStrong(i8** %[[V4]], i8* %[[V8]])
+// CHECK: ret void
+
+// CHECK: define linkonce_odr hidden void @__copy_assignment_8_8_s8(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 8
+// CHECK: %[[V4:.*]] = bitcast i8* %[[V3]] to i8**
+// CHECK: %[[V5:.*]] = bitcast i8** %[[V1]] to i8*
+// CHECK: %[[V6:.*]] = getelementptr inbounds i8, i8* %[[V5]], i64 8
+// CHECK: %[[V7:.*]] = bitcast i8* %[[V6]] to i8**
+// CHECK: %[[V8:.*]] = load i8*, i8** %[[V7]], align 8
+// CHECK: call void @objc_storeStrong(i8** %[[V4]], i8* %[[V8]])
+// CHECK: ret void
+
+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_s8_s16(
+
+// CHECK: define linkonce_odr hidden void @__move_constructor_8_8_s8_s16(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_s8(i8** %[[V0]], i8** %[[V1]])
+// CHECK: %[[V2:.*]] = bitcast i8** %[[V0]] to i8*
+// CHECK: %[[V3:.*]] = getelementptr inbounds i8, i8* %[[V2]], i64 16
+// CHECK: %[[V4:.*]] = bitcast i8* %[[V3]] to i8**
+// CHECK: %[[V5:.*]] = bitcast i8** %[[V1]] to i8*
+// CHECK: %[[V6:.*]] = getelementptr inbounds i8, i8* %[[V5]], i64 16
+// 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: ret void
+
+// CHECK: define linkonce_odr hidden void @__move_constructor_8_8_s8(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 8
+// CHECK: %[[V4:.*]] = bitcast i8* %[[V3]] to i8**
+// CHECK: %[[V5:.*]] = bitcast i8** %[[V1]] to i8*
+// CHECK: %[[V6:.*]] = getelementptr inbounds i8, i8* %[[V5]], i64 8
+// 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: ret void
+
+// CHECK: define internal void @__Block_byref_object_dispose_(i8*)
+// CHECK: call void @__destructor_8_s8_s16(
+// CHECK: ret void
+
+void test_move_constructor_StrongOuter(void) {
+  __block StrongOuter t;
+  BlockTy b = ^{ (void)t; };
+}
+
+// CHECK: define void @test_move_assignment_StrongOuter(%[[STRUCT_STRONGOUTER:.*]]* %[[P:.*]])
+// CHECK: %[[P_ADDR:.*]] = alloca %[[STRUCT_STRONGOUTER]]*, align 8
+// CHECK: %[[TMP:.*]] = alloca %[[STRUCT_STRONGOUTER]], align 8
+// CHECK: store %[[STRUCT_STRONGOUTER]]* %[[P]], %[[STRUCT_STRONGOUTER]]** %[[P_ADDR]], align 8
+// CHECK: %[[V0:.*]] = load %[[STRUCT_STRONGOUTER]]*, %[[STRUCT_STRONGOUTER]]** %[[P_ADDR]], align 8
+// CHECK: call void @getStrongOuter(%[[STRUCT_STRONGOUTER]]* sret %[[TMP]])
+// CHECK: %[[V1:.*]] = bitcast %[[STRUCT_STRONGOUTER]]* %[[V0]] to i8*
+// CHECK: %[[V2:.*]] = bitcast %[[STRUCT_STRONGOUTER]]* %[[TMP]] to i8*
+// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* %[[V1]], i8* %[[V2]], i64 24, i32 8, i1 false)
+// CHECK: %[[V3:.*]] = bitcast %[[STRUCT_STRONGOUTER]]* %[[V0]] to i8**
+// CHECK: %[[V4:.*]] = bitcast %[[STRUCT_STRONGOUTER]]* %[[TMP]] to i8**
+// CHECK: call void @__move_assignment_8_8_s8_s16(i8** %[[V3]], i8** %[[V4]])
+// CHECK: ret void
+
+// CHECK: define linkonce_odr hidden void @__move_assignment_8_8_s8_s16(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_s8(i8** %[[V0]], i8** %[[V1]])
+// CHECK: %[[V2:.*]] = bitcast i8** %[[V0]] to i8*
+// CHECK: %[[V3:.*]] = getelementptr inbounds i8, i8* %[[V2]], i64 16
+// CHECK: %[[V4:.*]] = bitcast i8* %[[V3]] to i8**
+// CHECK: %[[V5:.*]] = bitcast i8** %[[V1]] to i8*
+// CHECK: %[[V6:.*]] = getelementptr inbounds i8, i8* %[[V5]], i64 16
+// 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]])
+// CHECK: ret void
+
+// CHECK: define linkonce_odr hidden void @__move_assignment_8_8_s8(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 8
+// CHECK: %[[V4:.*]] = bitcast i8* %[[V3]] to i8**
+// CHECK: %[[V5:.*]] = bitcast i8** %[[V1]] to i8*
+// CHECK: %[[V6:.*]] = getelementptr inbounds i8, i8* %[[V5]], i64 8
+// 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]])
+// CHECK: ret void
+
+void test_move_assignment_StrongOuter(StrongOuter *p) {
+  *p = getStrongOuter();
+}
+
+// CHECK: define void @test_parameter_Strong([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_s8(i8** %[[V1]])
+// CHECK: ret void
+
+void test_parameter_Strong(Strong a) {
+}
+
+// CHECK: define void @test_argument_Strong([2 x i64] %[[A_COERCE:.*]])
+// CHECK: %[[A:.*]] = alloca %[[STRUCT_STRONG:.*]], align 8
+// CHECK-NEXT: %[[TEMP_LVALUE:.*]] = alloca %[[STRUCT_STRONG]], align 8
+// CHECK-NEXT: %[[V0:.*]] = bitcast %[[STRUCT_STRONG]]* %[[A]] to [2 x i64]*
+// CHECK-NEXT: store [2 x i64] %[[A_COERCE]], [2 x i64]* %[[V0]], align 8
+// CHECK-NEXT: %[[V1:.*]] = bitcast %[[STRUCT_STRONG]]* %[[TEMP_LVALUE]] to i8*
+// CHECK-NEXT: %[[V2:.*]] = bitcast %[[STRUCT_STRONG]]* %[[A]] to i8*
+// CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* %[[V1]], i8* %[[V2]], i64 16, i32 8, i1 false)
+// CHECK-NEXT: %[[V3:.*]] = bitcast %[[STRUCT_STRONG]]* %[[TEMP_LVALUE]] to i8**
+// CHECK-NEXT: %[[V4:.*]] = bitcast %[[STRUCT_STRONG]]* %[[A]] to i8**
+// CHECK-NEXT: call void @__copy_constructor_8_8_s8(i8** %[[V3]], i8** %[[V4]])
+// CHECK-NEXT: %[[V5:.*]] = bitcast %[[STRUCT_STRONG]]* %[[TEMP_LVALUE]] to [2 x i64]*
+// CHECK-NEXT: %[[V6:.*]] = load [2 x i64], [2 x i64]* %[[V5]], align 8
+// CHECK-NEXT: call void @calleeStrong([2 x i64] %[[V6]])
+// CHECK-NEXT: %[[V7:.*]] = bitcast %[[STRUCT_STRONG]]* %[[A]] to i8**
+// CHECK-NEXT: call void @__destructor_8_s8(i8** %[[V7]])
+// CHECK-NEXT: ret void
+
+void test_argument_Strong(Strong a) {
+  calleeStrong(a);
+}
+
+// CHECK: define [2 x i64] @test_return_Strong([2 x i64] %[[A_COERCE:.*]])
+// CHECK: %[[RETVAL:.*]] = alloca %[[STRUCT_STRONG:.*]], align 8
+// CHECK-NEXT: %[[A:.*]] = alloca %[[STRUCT_STRONG]], align 8
+// CHECK-NEXT: %[[V0:.*]] = bitcast %[[STRUCT_STRONG]]* %[[A]] to [2 x i64]*
+// CHECK-NEXT: store [2 x i64] %[[A_COERCE]], [2 x i64]* %[[V0]], align 8
+// CHECK-NEXT: %[[V1:.*]] = bitcast %[[STRUCT_STRONG]]* %[[RETVAL]] to i8*
+// CHECK-NEXT: %[[V2:.*]] = bitcast %[[STRUCT_STRONG]]* %[[A]] to i8*
+// CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* %[[V1]], i8* %[[V2]], i64 16, i32 8, i1 false)
+// CHECK-NEXT: %[[V3:.*]] = bitcast %[[STRUCT_STRONG]]* %[[RETVAL]] to i8**
+// CHECK-NEXT: %[[V4:.*]] = bitcast %[[STRUCT_STRONG]]* %[[A]] to i8**
+// CHECK-NEXT: call void @__copy_constructor_8_8_s8(i8** %[[V3]], i8** %[[V4]])
+// CHECK-NEXT: %[[V5:.*]] = bitcast %[[STRUCT_STRONG]]* %[[A]] to i8**
+// CHECK-NEXT: call void @__destructor_8_s8(i8** %[[V5]])
+// CHECK-NEXT: %[[V6:.*]] = bitcast %[[STRUCT_STRONG]]* %[[RETVAL]] to [2 x i64]*
+// CHECK-NEXT: %[[V7:.*]] = load [2 x i64], [2 x i64]* %[[V6]], align 8
+// CHECK-NEXT: ret [2 x i64] %[[V7]]
+
+Strong test_return_Strong(Strong a) {
+  return a;
+}
+
+// CHECK: define void @test_destructor_ignored_result()
+// CHECK: %[[COERCE:.*]] = alloca %[[STRUCT_STRONG:.*]], align 8
+// CHECK: %[[CALL:.*]] = call [2 x i64] @getStrong()
+// CHECK: %[[V0:.*]] = bitcast %[[STRUCT_STRONG]]* %[[COERCE]] to [2 x i64]*
+// CHECK: store [2 x i64] %[[CALL]], [2 x i64]* %[[V0]], align 8
+// CHECK: %[[V1:.*]] = bitcast %[[STRUCT_STRONG]]* %[[COERCE]] to i8**
+// CHECK: call void @__destructor_8_s8(i8** %[[V1]])
+// CHECK: ret void
+
+void test_destructor_ignored_result(void) {
+  getStrong();
+}
+
+// CHECK: define void @test_copy_constructor_StrongBlock(
+// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(
+// 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 @llvm.memcpy.p0i8.p0i8.i64(
+// 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 @llvm.memcpy.p0i8.p0i8.i64(
+// CHECK: call void @__copy_constructor_8_8_sv8(
+// CHECK: call void @__destructor_8_sv8(
+
+// CHECK: define linkonce_odr hidden void @__copy_constructor_8_8_sv8(
+// 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 @llvm.memcpy.p0i8.p0i8.i64(
+// CHECK: call void @__copy_constructor_8_8_sv8(
+
+void test_copy_constructor_StrongVolatile1(Strong *s) {
+  volatile Strong t = *s;
+}
+
+// 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 16, %[[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_s8(i8** %[[DSTADDR_CUR]])
+// CHECK: %[[V8:.*]] = bitcast i8** %[[DSTADDR_CUR]] to i8*
+// CHECK: %[[V9:.*]] = getelementptr inbounds i8, i8* %[[V8]], i64 16
+// 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_s8(i8** %[[V11]]) #5
+// 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);
+}
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.nonTrivialToDestroy())
+    return VAK_Invalid;
+
   if (Ty.isCXX98PODType(Context))
     return VAK_Valid;
 
@@ -837,7 +840,10 @@
     break;
 
   case VAK_Invalid:
-    if (Ty->isObjCObjectType())
+    if (Ty.hasNonTrivialToDestroyStruct())
+      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
@@ -10941,6 +10941,9 @@
     }
   }
 
+  if (var->getType().hasNonTrivialToDestroyStruct())
+    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
@@ -14823,6 +14826,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.
@@ -14964,7 +14968,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.
@@ -15000,6 +15006,19 @@
                Record->setHasObjectMember(true);
       }
     }
+
+    if (Record && !getLangOpts().CPlusPlus) {
+      QualType FT = FD->getType();
+      if (FT.nonTrivialToDefaultInitialize())
+        Record->setNonTrivialToDefaultInitialize(true);
+      if (FT.nonTrivialToCopy())
+        Record->setNonTrivialToCopy(true);
+      if (FT.nonTrivialToDestructiveMove())
+        Record->setNonTrivialToDestructiveMove(true);
+      if (FT.nonTrivialToDestroy())
+        Record->setNonTrivialToDestroy(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_non_trivial_c_struct:
+        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_non_trivial_c_struct:
+        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/CodeGen/CodeGenFunction.h
===================================================================
--- lib/CodeGen/CodeGenFunction.h
+++ lib/CodeGen/CodeGenFunction.h
@@ -1532,6 +1532,7 @@
       return false;
     case QualType::DK_cxx_destructor:
     case QualType::DK_objc_weak_lifetime:
+    case QualType::DK_non_trivial_c_struct:
       return getLangOpts().Exceptions;
     case QualType::DK_objc_strong_lifetime:
       return getLangOpts().Exceptions &&
@@ -3330,6 +3331,21 @@
                                                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 callDefaultConstructor(Address DstPtr, QualType QT, bool IsVolatile);
+  void callCopyConstructor(Address DstPtr, Address SrcPtr,
+                           QualType QT, bool IsVolatile);
+  void callMoveConstructor(Address DstPtr, Address SrcPtr,
+                           QualType QT, bool IsVolatile);
+  void callCopyAssignmentOperator(Address DstPtr, Address SrcPtr,
+                                  QualType QT, bool IsVolatile);
+  void callMoveAssignmentOperator(Address DstPtr, Address SrcPtr,
+                                  QualType QT, bool IsVolatile);
+  void callDestructor(Address DstPtr, QualType QT, bool IsVolatile);
+
   RValue
   EmitCXXMemberOrOperatorCall(const CXXMethodDecl *Method,
                               const CGCallee &Callee,
@@ -3501,6 +3517,7 @@
   static Destroyer destroyARCStrongPrecise;
   static Destroyer destroyARCWeak;
   static Destroyer emitARCIntrinsicUse;
+  static Destroyer destroyNonTrivialCStruct;
 
   void EmitObjCAutoreleasePoolPop(llvm::Value *Ptr);
   llvm::Value *EmitObjCAutoreleasePoolPush();
Index: lib/CodeGen/CodeGenFunction.cpp
===================================================================
--- lib/CodeGen/CodeGenFunction.cpp
+++ lib/CodeGen/CodeGenFunction.cpp
@@ -1138,6 +1138,15 @@
 
     if (Ty->isVariablyModifiedType())
       EmitVariablyModifiedType(Ty);
+
+    // If this arg is a type that is non-trivial to destruct and is not passed
+    // indirectly, the callee is responsible for destructing the argument.
+    if (Ty.hasNonTrivialToDestroyStruct()) {
+      AutoVarEmission Emission(*VD);
+      Emission.Addr = GetAddrOfLocalVar(VD);
+      emitAutoVarTypeCleanup(Emission,
+                             QualType::DK_non_trivial_c_struct);
+    }
   }
   // Emit a location at the end of the prologue.
   if (CGDebugInfo *DI = getDebugInfo())
Index: lib/CodeGen/CodeGenCStruct.cpp
===================================================================
--- /dev/null
+++ lib/CodeGen/CodeGenCStruct.cpp
@@ -0,0 +1,554 @@
+//===--- CodeGenCStruct.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;
+
+namespace {
+enum FieldKind {
+  FK_Strong,   // objc strong pointer.
+  FK_Struct,   // non-trivial C struct.
+  FK_Array     // array that has non-trivial elements.
+};
+}
+
+/// 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);
+}
+
+// Get the kind of a C struct field. Returns an empty Optional if the field
+// type is trivial.
+template <class T>
+static llvm::Optional<FieldKind> getFieldKind(QualType QT, ASTContext &Ctx) {
+  if (!T::isInterestingType(QT))
+    return Optional<FieldKind>();
+
+  if (QT->getAs<RecordType>())
+    return FK_Struct;
+  if (Ctx.getAsArrayType(QT))
+    return FK_Array;
+  if (QT.getQualifiers().getObjCLifetime() == Qualifiers::OCL_Strong)
+    return FK_Strong;
+
+  return Optional<FieldKind>();
+}
+
+static std::string getVolatileOffsetStr(bool IsVolatile, CharUnits Offset) {
+  std::string S;
+  if (IsVolatile)
+    S = "v";
+  S += llvm::to_string(Offset.getQuantity());
+  return S;
+}
+
+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.
+template <class T> class FieldInfoToString {
+public:
+  FieldInfoToString(ASTContext &C) : Ctx(C) {}
+
+  std::string doit(QualType QT, bool IsVolatile) {
+    if (Ctx.getAsArrayType(QT))
+      visitArray(CharUnits::Zero(), QT, IsVolatile);
+    else
+      visitStruct(CharUnits::Zero(), QT, IsVolatile);
+    return Str;
+  }
+
+  void visitField(CharUnits Offset, QualType QT, bool IsVolatile) {
+    switch (*getFieldKind<T>(QT, Ctx)) {
+    case FK_Struct:
+      visitStruct(Offset, QT, IsVolatile);
+      break;
+    case FK_Array:
+      visitArray(Offset, QT, IsVolatile);
+      break;
+    case FK_Strong:
+      Str += "_s";
+      if (QT->isBlockPointerType())
+        Str += "b";
+      Str += getVolatileOffsetStr(IsVolatile, Offset);
+      break;
+    }
+  }
+
+  void visitStruct(CharUnits 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();
+      unsigned FOffsetVal = RL.getFieldOffset(FieldNo++) / Ctx.getCharWidth();
+      if (getFieldKind<T>(FT, Ctx)) {
+        CharUnits FOffset = Offset + CharUnits::fromQuantity(FOffsetVal);
+        visitField(FOffset, FT, FT.isVolatileQualified() || IsVolatile);
+      }
+    }
+  }
+
+  void visitArray(CharUnits Offset, QualType QT, bool IsVolatile) {
+    const auto *AT = Ctx.getAsConstantArrayType(QT);
+    unsigned NumElts = Ctx.getConstantArrayElementCount(AT);
+    QualType EltTy = Ctx.getBaseElementType(AT);
+    unsigned EltSize = Ctx.getTypeSizeInChars(EltTy).getQuantity();
+    Str += "_AB" + llvm::to_string(EltSize) + "_" + llvm::to_string(NumElts);
+    visitField(Offset, QT, IsVolatile);
+    Str += "_AE";
+  }
+
+private:
+  ASTContext &Ctx;
+  std::string Str;
+};
+} // namespace
+
+// This function creates the mangled name of a special function of a non-trivial
+// C struct.
+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>(CGM.getContext()).doit(QT, IsVolatile);
+  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), DstStart(Address::invalid()),
+        SrcStart(Address::invalid()) {}
+
+  IRGen(CodeGenFunction &F)
+      : CGF(&F), DstStart(Address::invalid()), SrcStart(Address::invalid()) {}
+
+  void setFunction(CodeGenFunction &F, FunctionArgList &Args,
+                   CharUnits DstAlignment, CharUnits SrcAlignment) {
+    CGF = &F;
+    llvm::Value *D = CGF->Builder.CreateLoad(CGF->GetAddrOfLocalVar(Args[0]));
+    DstStart = Address(D, DstAlignment);
+    if (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);
+    }
+  }
+
+  void visitMostOuterStruct(QualType QT, bool IsVolatile) {
+    visitStruct(DstStart, SrcStart, QT, IsVolatile, false);
+  }
+
+  void visitField(Address DstAddr, Address SrcAddr, QualType QT,
+                  bool IsVolatile) {
+    switch (*getFieldKind<T>(QT, CGF->getContext())) {
+    case FK_Struct:
+      visitStruct(DstAddr, SrcAddr, QT, IsVolatile);
+      break;
+    case FK_Array:
+      visitArray(DstAddr, SrcAddr, QT, IsVolatile);
+      break;
+    case FK_Strong:
+      if (IsVolatile)
+        QT = QT.withVolatile();
+      T::emitIRStrong(DstAddr, SrcAddr, QT, *CGF);
+      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()) {
+      unsigned FOffset = RL.getFieldOffset(FieldNo++) / Ctx.getCharWidth();
+      Address FieldDstAddr = getAddrWithOffset(DstAddr, FOffset, *CGF);
+      Address FieldSrcAddr = getAddrWithOffset(SrcAddr, FOffset, *CGF);
+      QualType FT = FD->getType();
+      if (getFieldKind<T>(FT, CGF->getContext()))
+        visitField(FieldDstAddr, FieldSrcAddr, FT,
+                   FT.isVolatileQualified() || IsVolatile);
+    }
+  }
+
+  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, 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);
+  }
+
+  static bool isBinary() { return T::isBinary(); }
+
+  static const char *getFuncPrefix() { return T::getFuncPrefix(); }
+
+  static bool isInterestingType(QualType QT) {
+    return T::isInterestingType(QT);
+  }
+
+private:
+  CodeGenFunction *CGF;
+  Address DstStart, SrcStart;
+};
+
+template <bool IsBinary> 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; }
+};
+
+// Various subclasses of SpecialFunc that are used to customize
+// the IR IRGen generates.
+struct Destructor : SpecialFunc<false> {
+  static void emitIRStrong(Address DstAddr, Address SrcAddr, QualType QT,
+                           CodeGenFunction &CGF) {
+    CGF.destroyARCStrongImprecise(CGF, DstAddr, QT);
+  }
+  static const char *getFuncPrefix() { return "__destructor_"; }
+
+  static bool isInterestingType(QualType T) { return T.nonTrivialToDestroy(); }
+  static void callSpecialFunction(Address DstAddr, Address SrcAddr, QualType QT,
+                                  bool IsVolatile, CodeGenFunction &CGF) {
+    CGF.callDestructor(DstAddr, QT, IsVolatile);
+  }
+};
+
+struct DefaultConstructor : SpecialFunc<false> {
+  static void emitIRStrong(Address DstAddr, Address SrcAddr, QualType QT,
+                           CodeGenFunction &CGF) {
+    CGF.EmitNullInitialization(DstAddr, QT);
+  }
+  static const char *getFuncPrefix() { return "__default_constructor_"; }
+
+  static bool isInterestingType(QualType T) {
+    return T.nonTrivialToDefaultInitialize();
+  }
+  static void callSpecialFunction(Address DstAddr, Address SrcAddr, QualType QT,
+                                  bool IsVolatile, CodeGenFunction &CGF) {
+    CGF.callDefaultConstructor(DstAddr, QT, IsVolatile);
+  }
+};
+
+struct CopyConstructor : SpecialFunc<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 bool isInterestingType(QualType T) { return T.nonTrivialToCopy(); }
+  static void callSpecialFunction(Address DstAddr, Address SrcAddr, QualType QT,
+                                  bool IsVolatile, CodeGenFunction &CGF) {
+    CGF.callCopyConstructor(DstAddr, SrcAddr, QT, IsVolatile);
+  }
+};
+
+struct MoveConstructor : SpecialFunc<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 bool isInterestingType(QualType T) {
+    return T.nonTrivialToDestructiveMove();
+  }
+  static void callSpecialFunction(Address DstAddr, Address SrcAddr, QualType QT,
+                                  bool IsVolatile, CodeGenFunction &CGF) {
+    CGF.callMoveConstructor(DstAddr, SrcAddr, QT, IsVolatile);
+  }
+};
+
+struct CopyAssignment : SpecialFunc<true> {
+  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 bool isInterestingType(QualType T) { return T.nonTrivialToCopy(); }
+  static void callSpecialFunction(Address DstAddr, Address SrcAddr, QualType QT,
+                                  bool IsVolatile, CodeGenFunction &CGF) {
+    CGF.callCopyAssignmentOperator(DstAddr, SrcAddr, QT, IsVolatile);
+  }
+};
+
+struct MoveAssignment : SpecialFunc<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);
+    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 bool isInterestingType(QualType T) {
+    return T.nonTrivialToDestructiveMove();
+  }
+  static void callSpecialFunction(Address DstAddr, Address SrcAddr, QualType QT,
+                                  bool IsVolatile, CodeGenFunction &CGF) {
+    CGF.callMoveAssignmentOperator(DstAddr, SrcAddr, QT, IsVolatile);
+  }
+};
+} // namespace
+
+void CodeGenFunction::destroyNonTrivialCStruct(CodeGenFunction &CGF,
+                                               Address Addr, QualType Type) {
+  CGF.callDestructor(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, Args, DstAlignment, SrcAlignment);
+  Gen.visitMostOuterStruct(QT, IsVolatile);
+  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(*this);
+    DstPtr = Builder.CreateBitCast(DstPtr, CGM.Int8PtrPtrTy);
+    Gen.visitArray(DstPtr, Address::invalid(), QT, IsVolatile);
+  } else {
+    callDefaultConstructor(DstPtr, QT, IsVolatile);
+  }
+}
+
+// Functions to emit calls to the special functions of a non-trivial C struct.
+void CodeGenFunction::callDefaultConstructor(Address DstPtr, QualType QT,
+                                             bool IsVolatile) {
+  IRGen<DefaultConstructor> Gen;
+  llvm::Function *Fn = getSpecialFunction(
+      QT, IsVolatile, Gen, DstPtr.getAlignment(), CharUnits::Zero(), CGM);
+  DstPtr = Builder.CreateBitCast(DstPtr, CGM.Int8PtrPtrTy);
+  EmitNounwindRuntimeCall(Fn, DstPtr.getPointer());
+}
+
+void CodeGenFunction::callDestructor(Address DstPtr, QualType QT,
+                                     bool IsVolatile) {
+  IRGen<Destructor> Gen;
+  llvm::Function *Fn = getSpecialFunction(
+      QT, IsVolatile, Gen, DstPtr.getAlignment(), CharUnits::Zero(), CGM);
+  DstPtr = Builder.CreateBitCast(DstPtr, CGM.Int8PtrPtrTy);
+  EmitNounwindRuntimeCall(Fn, DstPtr.getPointer());
+}
+
+void CodeGenFunction::callCopyConstructor(Address DstPtr, Address SrcPtr,
+                                          QualType QT, bool IsVolatile) {
+  IRGen<CopyConstructor> Gen;
+  llvm::Function *Fn = getSpecialFunction(
+      QT, IsVolatile, Gen, DstPtr.getAlignment(), SrcPtr.getAlignment(), CGM);
+  DstPtr = Builder.CreateBitCast(DstPtr, CGM.Int8PtrPtrTy);
+  SrcPtr = Builder.CreateBitCast(SrcPtr, CGM.Int8PtrPtrTy);
+  EmitNounwindRuntimeCall(Fn, {DstPtr.getPointer(), SrcPtr.getPointer()});
+}
+
+void CodeGenFunction::callCopyAssignmentOperator(Address DstPtr, Address SrcPtr,
+                                                 QualType QT, bool IsVolatile) {
+  IRGen<CopyAssignment> Gen;
+  llvm::Function *Fn = getSpecialFunction(
+      QT, IsVolatile, Gen, DstPtr.getAlignment(), SrcPtr.getAlignment(), CGM);
+  DstPtr = Builder.CreateBitCast(DstPtr, CGM.Int8PtrPtrTy);
+  SrcPtr = Builder.CreateBitCast(SrcPtr, CGM.Int8PtrPtrTy);
+  EmitNounwindRuntimeCall(Fn, {DstPtr.getPointer(), SrcPtr.getPointer()});
+}
+
+void CodeGenFunction::callMoveConstructor(Address DstPtr, Address SrcPtr,
+                                          QualType QT, bool IsVolatile) {
+  IRGen<MoveConstructor> Gen;
+  llvm::Function *Fn = getSpecialFunction(
+      QT, IsVolatile, Gen, DstPtr.getAlignment(), SrcPtr.getAlignment(), CGM);
+  DstPtr = Builder.CreateBitCast(DstPtr, CGM.Int8PtrPtrTy);
+  SrcPtr = Builder.CreateBitCast(SrcPtr, CGM.Int8PtrPtrTy);
+  EmitNounwindRuntimeCall(Fn, {DstPtr.getPointer(), SrcPtr.getPointer()});
+}
+
+void CodeGenFunction::callMoveAssignmentOperator(Address DstPtr, Address SrcPtr,
+                                                 QualType QT, bool IsVolatile) {
+  IRGen<MoveAssignment> Gen;
+  llvm::Function *Fn = getSpecialFunction(
+      QT, IsVolatile, Gen, DstPtr.getAlignment(), SrcPtr.getAlignment(), CGM);
+  DstPtr = Builder.CreateBitCast(DstPtr, CGM.Int8PtrPtrTy);
+  SrcPtr = Builder.CreateBitCast(SrcPtr, CGM.Int8PtrPtrTy);
+  EmitNounwindRuntimeCall(Fn, {DstPtr.getPointer(), SrcPtr.getPointer()});
+}
Index: lib/CodeGen/CMakeLists.txt
===================================================================
--- lib/CodeGen/CMakeLists.txt
+++ lib/CodeGen/CMakeLists.txt
@@ -70,6 +70,7 @@
   CGVTables.cpp
   CodeGenABITypes.cpp
   CodeGenAction.cpp
+  CodeGenCStruct.cpp
   CodeGenFunction.cpp
   CodeGenModule.cpp
   CodeGenPGO.cpp
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,13 @@
 /// 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.hasNonTrivialToDestroyStruct())
+    CGF.pushDestroy(QualType::DK_non_trivial_c_struct,
+                    src.getAggregateAddress(), Ty);
+
   if (shouldUseDestForReturnSlot()) {
     // Logically, Dest.getAddr() should equal Src.getAggregateAddr().
     // The possibility of undef rvalues complicates that a lot,
@@ -261,11 +270,12 @@
 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
@@ -277,6 +287,32 @@
     AggValueSlot::forLValue(src, AggValueSlot::IsDestructed,
                             needsGC(type), AggValueSlot::IsAliased);
   EmitCopy(type, Dest, srcAgg);
+
+  // 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.hasNonTrivialToDestructiveMoveStruct()) {
+      if (Dest.isPotentiallyAliased())
+        CGF.callMoveAssignmentOperator(Dest.getAddress(), src.getAddress(),
+                                       type, IsVolatile);
+      else
+        CGF.callMoveConstructor(Dest.getAddress(), src.getAddress(), type,
+                                IsVolatile);
+    }
+  } else {
+    if (type.hasNonTrivialToCopyStruct()) {
+      if (Dest.isPotentiallyAliased())
+        CGF.callCopyAssignmentOperator(Dest.getAddress(), src.getAddress(),
+                                       type, IsVolatile);
+      else
+        CGF.callCopyConstructor(Dest.getAddress(), src.getAddress(), type,
+                                IsVolatile);
+    }
+  }
 }
 
 /// Perform a copy from the source into the destination.
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_non_trivial_c_struct:
     // 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.hasNonTrivialToDefaultInitializeStruct()) {
+    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_non_trivial_c_struct:
+    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_non_trivial_c_struct:
+    return destroyNonTrivialCStruct;
   }
   llvm_unreachable("Unknown DestructionKind");
 }
Index: lib/CodeGen/CGCall.cpp
===================================================================
--- lib/CodeGen/CGCall.cpp
+++ lib/CodeGen/CGCall.cpp
@@ -3532,6 +3532,16 @@
       cast<CastExpr>(E)->getCastKind() == CK_LValueToRValue) {
     LValue L = EmitLValue(cast<CastExpr>(E)->getSubExpr());
     assert(L.isSimple());
+
+    // If type is a C struct that is non-trivial to copy, emit a call to its
+    // copy constructor to build the argument in the temporary slot.
+    if (type.hasNonTrivialToCopyStruct()) {
+      AggValueSlot Slot = CreateAggTemp(E->getType(), "temp.lvalue");
+      EmitAggExpr(E, Slot);
+      args.add(Slot.asRValue(), type);
+      return;
+    }
+
     if (L.getAlignment() >= getContext().getTypeAlignInChars(type)) {
       args.add(L.asAggregateRValue(), type, /*NeedsCopy*/true);
     } else {
Index: lib/CodeGen/CGBlocks.cpp
===================================================================
--- lib/CodeGen/CGBlocks.cpp
+++ lib/CodeGen/CGBlocks.cpp
@@ -2020,6 +2020,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.callMoveConstructor(destField, srcField, VarType,
+                            VarType.isVolatileQualified());
+  }
+
+  bool needsDispose() const override {
+    return VarType.hasNonTrivialToDestroyStruct();
+  }
+
+  void emitDispose(CodeGenFunction &CGF, Address field) override {
+    EHScopeStack::stable_iterator cleanupDepth = CGF.EHStack.stable_begin();
+    CGF.pushDestroy(QualType::DK_non_trivial_c_struct, 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 +2235,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.hasNonTrivialToDestructiveMoveStruct() ||
+      type.hasNonTrivialToDestroyStruct())
+    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;
 }
 
+bool QualType::hasNonTrivialToDefaultInitializeStruct() const {
+  if (const auto *RT =
+          getTypePtr()->getBaseElementTypeUnsafe()->getAs<RecordType>())
+    return RT->getDecl()->nonTrivialToDefaultInitialize();
+  return false;
+}
+
+bool QualType::hasNonTrivialToCopyStruct() const {
+  if (const auto *RT =
+          getTypePtr()->getBaseElementTypeUnsafe()->getAs<RecordType>())
+    return RT->getDecl()->nonTrivialToCopy();
+  return false;
+}
+
+bool QualType::hasNonTrivialToDestructiveMoveStruct() const {
+  if (const auto *RT =
+          getTypePtr()->getBaseElementTypeUnsafe()->getAs<RecordType>())
+    return RT->getDecl()->nonTrivialToDestructiveMove();
+  return false;
+}
+
+bool QualType::hasNonTrivialToDestroyStruct() const {
+  if (const auto *RT =
+          getTypePtr()->getBaseElementTypeUnsafe()->getAs<RecordType>())
+    return RT->getDecl()->nonTrivialToDestroy();
+  return false;
+}
+
+bool QualType::nonTrivialToDefaultInitialize() const {
+  if (hasNonTrivialToDefaultInitializeStruct())
+    return true;
+
+  Qualifiers::ObjCLifetime Lifetime = getQualifiers().getObjCLifetime();
+  return Lifetime == Qualifiers::OCL_Strong;
+}
+
+bool QualType::nonTrivialToCopy() const {
+  if (hasNonTrivialToCopyStruct())
+    return true;
+
+  Qualifiers::ObjCLifetime Lifetime = getQualifiers().getObjCLifetime();
+  return Lifetime == Qualifiers::OCL_Strong;
+}
+
+bool QualType::nonTrivialToDestructiveMove() const {
+  if (hasNonTrivialToDestructiveMoveStruct())
+    return true;
+
+  Qualifiers::ObjCLifetime Lifetime = getQualifiers().getObjCLifetime();
+  return Lifetime == Qualifiers::OCL_Strong;
+}
+
+bool QualType::nonTrivialToDestroy() const {
+  if (hasNonTrivialToDestroyStruct())
+    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 (type.hasNonTrivialToDestroyStruct())
+    return DK_non_trivial_c_struct;
+
   /// 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
@@ -3892,7 +3892,9 @@
     : TagDecl(DK, TK, C, DC, IdLoc, Id, PrevDecl, StartLoc),
       HasFlexibleArrayMember(false), AnonymousStructOrUnion(false),
       HasObjectMember(false), HasVolatileMember(false),
-      LoadedFieldsFromExternalStorage(false) {
+      LoadedFieldsFromExternalStorage(false),
+      NonTrivialToDefaultInitialize(false), NonTrivialToCopy(false),
+      NonTrivialToDestructiveMove(false), NonTrivialToDestroy(false) {
   assert(classof(static_cast<Decl*>(this)) && "Invalid Kind!");
 }
 
Index: lib/AST/ASTContext.cpp
===================================================================
--- lib/AST/ASTContext.cpp
+++ lib/AST/ASTContext.cpp
@@ -5776,6 +5776,12 @@
     return true;
   }
   
+  // Return true if Ty is a non-trivial C struct type that is non-trivial to
+  // destructly move or destroy.
+  if (Ty.hasNonTrivialToDestructiveMoveStruct() ||
+      Ty.hasNonTrivialToDestroyStruct())
+    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
@@ -5109,12 +5109,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))">;
@@ -5155,6 +5160,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">,
@@ -7173,6 +7181,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
@@ -1084,11 +1084,47 @@
   // true when Type is objc's weak and weak is enabled but ARC isn't.
   bool isNonWeakInMRRWithObjCWeak(const ASTContext &Context) const;
 
+  /// Functions to query basic properties of non-trivial C struct types.
+
+  /// 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 hasNonTrivialToDefaultInitializeStruct() const;
+
+  /// Returns true if this type is a C struct that is non-trivial to copy or an
+  /// array that contains such a struct.
+  bool hasNonTrivialToCopyStruct() const;
+
+  /// 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 hasNonTrivialToDestructiveMoveStruct() const;
+
+  /// Returns true if this type is a C struct that is non-trivial to destroy or
+  /// an array that contains such a struct.
+  bool hasNonTrivialToDestroyStruct() const;
+
+  /// Returns true if this is a non-trivial type that would cause a struct
+  /// transitively containing this type to be non-trivial to default initialize.
+  bool nonTrivialToDefaultInitialize() const;
+
+  /// Returns true if this is a non-trivial type that would cause a struct
+  /// transitively containing this type to be non-trivial to copy.
+  bool nonTrivialToCopy() const;
+
+  /// Returns true if this is a non-trivial type that would cause a struct
+  /// transitively containing this type to be non-trivial to destructively
+  /// move.
+  bool nonTrivialToDestructiveMove() const;
+
+  /// Returns true if this is a non-trivial type that would cause a struct
+  /// transitively containing this type to be non-trivial to destroy.
+  bool nonTrivialToDestroy() const;
+
   enum DestructionKind {
     DK_none,
     DK_cxx_destructor,
     DK_objc_strong_lifetime,
-    DK_objc_weak_lifetime
+    DK_objc_weak_lifetime,
+    DK_non_trivial_c_struct
   };
 
   /// 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
@@ -3506,6 +3506,12 @@
   /// when needed.
   mutable bool LoadedFieldsFromExternalStorage : 1;
 
+  /// Basic properties of non-trivial C structs.
+  bool NonTrivialToDefaultInitialize : 1;
+  bool NonTrivialToCopy : 1;
+  bool NonTrivialToDestructiveMove : 1;
+  bool NonTrivialToDestroy : 1;
+
 protected:
   RecordDecl(Kind DK, TagKind TK, const ASTContext &C, DeclContext *DC,
              SourceLocation StartLoc, SourceLocation IdLoc,
@@ -3565,6 +3571,39 @@
     LoadedFieldsFromExternalStorage = val;
   }
 
+  /// Functions to query basic properties of non-trivial C structs.
+  bool nonTrivialToDefaultInitialize() const {
+    return NonTrivialToDefaultInitialize;
+  }
+
+  void setNonTrivialToDefaultInitialize(bool val) {
+    NonTrivialToDefaultInitialize = val;
+  }
+
+  bool nonTrivialToCopy() const {
+    return NonTrivialToCopy;
+  }
+
+  void setNonTrivialToCopy(bool val) {
+    NonTrivialToCopy = val;
+  }
+
+  bool nonTrivialToDestructiveMove() const {
+    return NonTrivialToDestructiveMove;
+  }
+
+  void setNonTrivialToDestructiveMove(bool val) {
+    NonTrivialToDestructiveMove = val;
+  }
+
+  bool nonTrivialToDestroy() const {
+    return NonTrivialToDestroy;
+  }
+
+  void setNonTrivialToDestroy(bool val) {
+    NonTrivialToDestroy = val;
+  }
+
   /// \brief Determines whether this declaration represents the
   /// injected class name.
   ///
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to