ahatanak created this revision.
ahatanak added reviewers: rjmccall, erik.pilkington.
ahatanak added a project: clang.
Herald added subscribers: ributzka, dexonsmith, jkorous, martong.
Herald added a reviewer: shafik.

This patch fixes several shortcomings of the way we currently extend the 
lifetime of objects passed to calls to __builtin_os_log_format:

- Sema doesn't diagnose code that jumps into or out of the scope of a 
lifetime-extended object.
- Lifetime of objects in comma operators or expressions for ObjC property 
access isn't extended.
- We extend the lifetime of an object by emitting an extra pair of retain and 
release calls instead of delaying the release call.
- Calls to clang.arc.use are emitted on exception paths even though they are 
needed only on normal paths.

rdar://problem/62610583


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D83997

Files:
  clang/include/clang/AST/ExprCXX.h
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/include/clang/Serialization/ASTBitCodes.h
  clang/lib/AST/ASTImporter.cpp
  clang/lib/AST/JSONNodeDumper.cpp
  clang/lib/AST/TextNodeDumper.cpp
  clang/lib/CodeGen/CGBuiltin.cpp
  clang/lib/CodeGen/CGExprAgg.cpp
  clang/lib/CodeGen/CGExprComplex.cpp
  clang/lib/CodeGen/CGExprScalar.cpp
  clang/lib/CodeGen/CGObjC.cpp
  clang/lib/CodeGen/CodeGenFunction.cpp
  clang/lib/CodeGen/CodeGenFunction.h
  clang/lib/Sema/JumpDiagnostics.cpp
  clang/lib/Sema/SemaChecking.cpp
  clang/lib/Serialization/ASTReaderStmt.cpp
  clang/lib/Serialization/ASTWriterStmt.cpp
  clang/test/AST/ast-dump-objc-arc-json.m
  clang/test/AST/ast-dump-stmt.m
  clang/test/CodeGenObjC/os_log.m
  clang/test/CodeGenObjCXX/os_log.mm
  clang/test/PCH/Inputs/arc.h
  clang/test/PCH/arc.m
  clang/test/SemaObjC/format-strings-oslog.m

Index: clang/test/SemaObjC/format-strings-oslog.m
===================================================================
--- clang/test/SemaObjC/format-strings-oslog.m
+++ clang/test/SemaObjC/format-strings-oslog.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fsyntax-only -fobjc-arc -verify %s
 
 #include <stdarg.h>
 #include <stddef.h>
@@ -6,6 +6,7 @@
 #include <stddef.h> // For wint_t and wchar_t
 
 int printf(const char *restrict, ...);
+id getObj(void);
 
 @interface NSString
 @end
@@ -67,3 +68,38 @@
   MyOSLog("%d");    // expected-warning {{more '%' conversions than data arguments}}
   MyOSLog("%P", p); // expected-warning {{using '%P' format specifier without precision}}
 }
+
+void test_arc_extended_lifetime0(int cond, void *buf) {
+  switch (cond) {
+  case 0:
+    __builtin_os_log_format(buf, @"%@", getObj()); // expected-note {{jump enters lifetime of an object}}
+    break;
+  default: // expected-error {{cannot jump from switch statement to this case label}}
+    break;
+  }
+}
+
+void test_arc_extended_lifetime1(int cond, void *buf, id a) {
+  switch (cond) {
+  case 0:
+    __builtin_os_log_format(buf, @"%@", a);
+    break;
+  default:
+    break;
+  }
+}
+
+void test_arc_extended_lifetime2(int cond, void *buf) {
+  static void *ips[] = {&&L0};
+L0: // expected-note {{possible target of indirect goto}}
+    ;
+  __builtin_os_log_format(buf, @"%@", getObj()); // expected-note {{jump exits lifetime of an object}}
+  goto *ips;                                     // expected-error {{cannot jump}}
+}
+
+void test_arc_extended_lifetime3(int cond, void *buf, id a) {
+  static void *ips[] = {&&L0};
+L0:;
+  __builtin_os_log_format(buf, @"%@", a);
+  goto *ips;
+}
Index: clang/test/PCH/arc.m
===================================================================
--- clang/test/PCH/arc.m
+++ clang/test/PCH/arc.m
@@ -4,7 +4,7 @@
 
 // Test with pch.
 // RUN: %clang_cc1 -emit-pch -fblocks -triple x86_64-apple-darwin11 -fobjc-arc -x objective-c-header -o %t %S/Inputs/arc.h
-// RUN: %clang_cc1 -fblocks -triple x86_64-apple-darwin11 -fobjc-arc -include-pch %t -fsyntax-only -emit-llvm-only %s 
+// RUN: %clang_cc1 -fblocks -triple x86_64-apple-darwin11 -fobjc-arc -include-pch %t -emit-llvm -o - %s | FileCheck %s
 
 // Test error when pch's -fobjc-arc state is different.
 // RUN: not %clang_cc1 -fblocks -triple x86_64-apple-darwin11 -include-pch %t -fsyntax-only -emit-llvm-only %s 2>&1 | FileCheck -check-prefix=CHECK-ERR1 %s 
@@ -16,3 +16,15 @@
 
 // CHECK-ERR1: Objective-C automated reference counting was enabled in PCH file but is currently disabled
 // CHECK-ERR2: Objective-C automated reference counting was disabled in PCH file but is currently enabled
+
+// CHECK: define internal void @callBuiltinOSLogFormat(
+// CHECK: %[[CALL:.*]] = call i8* @getObj()
+// CHECK: %[[V1:.*]] = notail call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %[[CALL]])
+// CHECK: %[[V2:.*]] = ptrtoint i8* %[[V1]] to i64
+// CHECK: call void @__os_log_helper_1_2_1_8_64(i8* %{{.*}}, i64 %[[V2]])
+// CHECK: call void @os_log_pack_send(
+// CHECK: call void @llvm.objc.release(i8* %[[V1]])
+
+void testBuiltinOSLogFormat(void) {
+  callBuiltinOSLogFormat(0);
+}
Index: clang/test/PCH/Inputs/arc.h
===================================================================
--- clang/test/PCH/Inputs/arc.h
+++ clang/test/PCH/Inputs/arc.h
@@ -23,4 +23,9 @@
 typedef int array0[sizeof((BRIDGE id)CFCreateSomething())];
 typedef int array1[sizeof((BRIDGE CFTypeRef)CreateSomething())];
 
-
+static inline void callBuiltinOSLogFormat(void *buf) {
+  extern id getObj(void);
+  extern void os_log_pack_send(void *);
+  __builtin_os_log_format(buf, "%@", getObj());
+  os_log_pack_send(buf);
+}
Index: clang/test/CodeGenObjCXX/os_log.mm
===================================================================
--- clang/test/CodeGenObjCXX/os_log.mm
+++ clang/test/CodeGenObjCXX/os_log.mm
@@ -1,5 +1,8 @@
 // RUN: %clang_cc1 %s -emit-llvm -o - -triple x86_64-darwin-apple -fobjc-arc \
-// RUN:   -fexceptions -fcxx-exceptions | FileCheck %s
+// RUN:   -fexceptions -fcxx-exceptions -fobjc-arc-exceptions -O2 \
+// RUN:   -disable-llvm-passes | FileCheck %s
+
+@class Class0;
 
 // Check that no EH cleanup is emitted around the call to __os_log_helper.
 namespace no_eh_cleanup {
@@ -17,4 +20,124 @@
   // CHECK: define {{.*}} @__os_log_helper_1_2_2_4_0_8_34({{.*}} [[NUW:#[0-9]+]]
 }
 
+namespace extend_lifetime {
+void os_log_pack_send(void *);
+id getObj(int);
+id g0, g1;
+
+// CHECK-LABEL: define void @_ZN15extend_lifetime5test0EPvb(
+// CHECK: alloca i8*, align 8
+// CHECK: alloca i8, align 1
+// CHECK: %[[COND_CLEANUP_SAVE:.*]] = alloca i8*, align 8
+// CHECK: %[[CLEANUP_COND:.*]] = alloca i1, align 1
+// CHECK: %[[CLEANUP_COND1:.*]] = alloca i1, align 1
+// CHECK: %[[COND_CLEANUP_SAVE2:.*]] = alloca i8*, align 8
+// CHECK: %[[CLEANUP_COND3:.*]] = alloca i1, align 1
+// CHECK: %[[COND_CLEANUP_SAVE4:.*]] = alloca i8*, align 8
+// CHECK: alloca i8*, align 8
+// CHECK: %[[COND_CLEANUP_SAVE6:.*]] = alloca i8*, align 8
+// CHECK: %[[CLEANUP_COND7:.*]] = alloca i1, align 1
+// CHECK: %[[CLEANUP_COND8:.*]] = alloca i1, align 1
+// CHECK: %[[COND_CLEANUP_SAVE9:.*]] = alloca i8*, align 8
+// CHECK: %[[CLEANUP_COND10:.*]] = alloca i1, align 1
+// CHECK: %[[COND_CLEANUP_SAVE11:.*]] = alloca i8*, align 8
+// CHECK: store i1 false, i1* %[[CLEANUP_COND]], align 1
+// CHECK: store i1 false, i1* %[[CLEANUP_COND1]], align 1
+// CHECK: store i1 false, i1* %[[CLEANUP_COND3]], align 1
+// CHECK: store i1 false, i1* %[[CLEANUP_COND7]], align 1
+// CHECK: store i1 false, i1* %[[CLEANUP_COND8]], align 1
+// CHECK: store i1 false, i1* %[[CLEANUP_COND10]], align 1
+
+// CHECK: %[[CALL:.*]] = call i8* @_ZN15extend_lifetime6getObjEi(i32 0)
+// CHECK: %[[V2:.*]] = notail call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %[[CALL]])
+// CHECK: store i8* %[[V2]], i8** %[[COND_CLEANUP_SAVE]], align 8
+// CHECK: store i1 true, i1* %[[CLEANUP_COND]], align 1
+// CHECK: store i1 true, i1* %[[CLEANUP_COND1]], align 1
+// CHECK: store i8* %[[V2]], i8** %[[COND_CLEANUP_SAVE2]], align 8
+// CHECK: store i1 true, i1* %[[CLEANUP_COND3]], align 1
+// CHECK: store i8* %[[V2]], i8** %[[COND_CLEANUP_SAVE4]], align 8
+
+// CHECK: %[[CALL5:.*]] = invoke i8* @_ZN15extend_lifetime6getObjEi(i32 1)
+
+// CHECK: %[[V3:.*]] = notail call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %[[CALL5]])
+// CHECK: store i8* %[[V3]], i8** %[[COND_CLEANUP_SAVE6]], align 8
+// CHECK: store i1 true, i1* %[[CLEANUP_COND7]], align 1
+// CHECK: store i1 true, i1* %[[CLEANUP_COND8]], align 1
+// CHECK: store i8* %[[V3]], i8** %[[COND_CLEANUP_SAVE9]], align 8
+// CHECK: store i1 true, i1* %[[CLEANUP_COND10]], align 1
+// CHECK: store i8* %[[V3]], i8** %[[COND_CLEANUP_SAVE11]], align 8
+
+// CHECK: %[[COND:.*]] = phi i8* [ %[[V2]], %{{.*}} ], [ %[[V3]], %{{.*}} ]
+// CHECK: %[[V4:.*]] = ptrtoint i8* %[[COND]] to i64
+// CHECK: %[[CALL14:.*]] = invoke i8* @_ZN15extend_lifetime6getObjEi(i32 2)
+
+// CHECK: %[[V5:.*]] = notail call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %[[CALL14]])
+// CHECK: %[[V6:.*]] = ptrtoint i8* %[[V5]] to i64
+// CHECK: call void @__os_log_helper_1_2_2_8_64_8_64(i8* %{{.*}}, i64 %[[V4]], i64 %[[V6]])
+// CHECK: invoke void @_ZN15extend_lifetime16os_log_pack_sendEPv(
+
+// CHECK: call void (...) @llvm.objc.clang.arc.use(i8* %[[V5]]) #4
+// CHECK: call void @llvm.objc.release(i8* %[[V5]])
+// CHECK: %[[CLEANUP_IS_ACTIVE21:.*]] = load i1, i1* %[[CLEANUP_COND10]], align 1
+// CHECK: br i1 %[[CLEANUP_IS_ACTIVE21]],
+
+// CHECK: %[[V8:.*]] = load i8*, i8** %[[COND_CLEANUP_SAVE11]], align 8
+// CHECK: call void (...) @llvm.objc.clang.arc.use(i8* %[[V8]])
+
+// CHECK: %[[CLEANUP_IS_ACTIVE24:.*]] = load i1, i1* %[[CLEANUP_COND8]], align 1
+// CHECK: br i1 %[[CLEANUP_IS_ACTIVE24]],
+
+// CHECK: %[[V9:.*]] = load i8*, i8** %[[COND_CLEANUP_SAVE9]], align 8
+// CHECK: call void @llvm.objc.release(i8* %[[V9]])
+
+// CHECK: %[[CLEANUP_IS_ACTIVE31:.*]] = load i1, i1* %[[CLEANUP_COND3]], align 1
+// CHECK: br i1 %[[CLEANUP_IS_ACTIVE31]],
+
+// CHECK: %[[V10:.*]] = load i8*, i8** %[[COND_CLEANUP_SAVE4]], align 8
+// CHECK: call void (...) @llvm.objc.clang.arc.use(i8* %[[V10]])
+
+// CHECK: %[[CLEANUP_IS_ACTIVE34:.*]] = load i1, i1* %[[CLEANUP_COND1]], align 1
+// CHECK: br i1 %[[CLEANUP_IS_ACTIVE34]],
+
+// CHECK: %[[V11:.*]] = load i8*, i8** %[[COND_CLEANUP_SAVE2]], align 8
+// CHECK: call void @llvm.objc.release(i8* %[[V11]])
+
+// CHECK: ret void
+
+// CHECK: landingpad { i8*, i32 }
+
+// CHECK: landingpad { i8*, i32 }
+// CHECK: %[[CLEANUP_IS_ACTIVE:.*]] = load i1, i1* %[[CLEANUP_COND7]], align 1
+// CHECK: br i1 %[[CLEANUP_IS_ACTIVE]],
+
+// CHECK: %[[V18:.*]] = load i8*, i8** %[[COND_CLEANUP_SAVE6]], align 8
+// CHECK: call void @llvm.objc.release(i8* %[[V18]])
+
+// CHECK: %[[CLEANUP_IS_ACTIVE15:.*]] = load i1, i1* %[[CLEANUP_COND]], align 1
+// CHECK: br i1 %[[CLEANUP_IS_ACTIVE15]],
+
+// CHECK: %[[V19:.*]] = load i8*, i8** %[[COND_CLEANUP_SAVE]], align 8
+// CHECK: call void @llvm.objc.release(i8* %[[V19]])
+
+// CHECK: landingpad { i8*, i32 }
+// CHECK: call void @llvm.objc.release(i8* %[[V5]])
+// CHECK: %[[CLEANUP_IS_ACTIVE28:.*]] = load i1, i1* %[[CLEANUP_COND8]], align 1
+// CHECK: br i1 %[[CLEANUP_IS_ACTIVE28]],
+
+// CHECK: %[[V23:.*]] = load i8*, i8** %[[COND_CLEANUP_SAVE9]], align 8
+// CHECK: call void @llvm.objc.release(i8* %[[V23]])
+
+// CHECK: %[[CLEANUP_IS_ACTIVE38:.*]] = load i1, i1* %[[CLEANUP_COND1]], align 1
+// CHECK: br i1 %[[CLEANUP_IS_ACTIVE38]],
+
+// CHECK: %[[V24:.*]] = load i8*, i8** %[[COND_CLEANUP_SAVE2]], align 8
+// CHECK: call void @llvm.objc.release(i8* %[[V24]])
+
+void test0(void *buf, bool c) {
+  __builtin_os_log_format(buf, "%@ %@", c ? getObj(0) : getObj(1), getObj(2));
+  os_log_pack_send(buf);
+}
+
+}
+
 // CHECK: attributes [[NUW]] = { {{.*}}nounwind
Index: clang/test/CodeGenObjC/os_log.m
===================================================================
--- clang/test/CodeGenObjC/os_log.m
+++ clang/test/CodeGenObjC/os_log.m
@@ -11,9 +11,11 @@
 @interface C
 - (id)m0;
 + (id)m1;
+@property id prop0;
 @end
 
 C *c;
+id g0, g1;
 
 @class NSString;
 extern __attribute__((visibility("default"))) NSString *GenString();
@@ -22,7 +24,6 @@
 // CHECK-LABEL: define void @test_builtin_os_log1(
 // CHECK: alloca i8*, align 8
 // CHECK: %[[A_ADDR:.*]] = alloca i8*, align 8
-// CHECK: %[[OS_LOG_ARG:.*]] = alloca %{{.*}}*, align 8
 // CHECK-O2: %[[V0:.*]] = call i8* @llvm.objc.retain(
 // CHECK-O2: store i8* %[[V0]], i8** %[[A_ADDR]], align 8,
 // CHECK-O0: call void @llvm.objc.storeStrong(i8** %[[A_ADDR]], i8* %{{.*}})
@@ -30,25 +31,16 @@
 // CHECK: %[[V2:.*]] = bitcast %{{.*}}* %[[CALL]] to i8*
 // CHECK: %[[V3:.*]] = notail call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %[[V2]])
 // CHECK: %[[V4:.*]] = bitcast i8* %[[V3]] to %{{.*}}*
-// CHECK: %[[V5:.*]] = bitcast %{{.*}}* %[[V4]] to i8*
-// CHECK: %[[V6:.*]] = call i8* @llvm.objc.retain(i8* %[[V5]])
-// CHECK: %[[V7:.*]] = bitcast i8* %[[V6]] to %{{.*}}*
-// CHECK: store %{{.*}}* %[[V7]], %{{.*}}** %[[OS_LOG_ARG]],
-// CHECK: %[[V8:.*]] = ptrtoint %{{.*}}* %[[V7]] to i64
-// CHECK: %[[V9:.*]] = load i8*, i8** %[[A_ADDR]], align 8
-// CHECK: %[[V10:.*]] = ptrtoint i8* %[[V9]] to i64
-// CHECK: call void @__os_log_helper_1_2_2_8_64_8_64(i8* %{{.*}}, i64 %[[V8]], i64 %[[V10]])
-// CHECK: %[[V11:.*]] = bitcast %{{.*}}* %[[V4]] to i8*
-// CHECK: call void @llvm.objc.release(i8* %[[V11]])
-// CHECK: call void @os_log_pack_send(i8* %{{.*}})
-// CHECK-O2: call void (...) @llvm.objc.clang.arc.use(%{{.*}}* %[[V7]])
-// CHECK-O2: %[[V13:.*]] = load %{{.*}}*, %{{.*}}** %[[OS_LOG_ARG]], align 8
-// CHECK-O2: %[[V14:.*]] = bitcast %{{.*}}* %[[V13]] to i8*
-// CHECK-O2: call void @llvm.objc.release(i8* %[[V14]])
-// CHECK-O2: %[[V15:.*]] = load i8*, i8** %[[A_ADDR]], align 8
-// CHECK-O2: call void @llvm.objc.release(i8* %[[V15]])
-// CHECK-O0: %[[V12:.*]] = bitcast %{{.*}}** %[[OS_LOG_ARG]] to i8**
-// CHECK-O0: call void @llvm.objc.storeStrong(i8** %[[V12]], i8* null)
+// CHECK: %[[V5:.*]] = ptrtoint %{{.*}}* %[[V4]] to i64
+// CHECK: %[[V6:.*]] = load i8*, i8** %[[A_ADDR]], align 8
+// CHECK: %[[V7:.*]] = ptrtoint i8* %[[V6]] to i64
+// CHECK: call void @__os_log_helper_1_2_2_8_64_8_64(i8* %{{.*}}, i64 %[[V5]], i64 %[[V7]])
+// CHECK: call void @os_log_pack_send(
+// CHECK-O2: call void (...) @llvm.objc.clang.arc.use(%{{.*}}* %[[V4]])
+// CHECK: %[[V9:.*]] = bitcast %{{.*}}* %[[V4]] to i8*
+// CHECK: call void @llvm.objc.release(i8* %[[V9]])
+// CHECK-O2: %[[V10:.*]] = load i8*, i8** %[[A_ADDR]], align 8
+// CHECK-O2: call void @llvm.objc.release(i8* %[[V10]])
 // CHECK-O0: call void @llvm.objc.storeStrong(i8** %[[A_ADDR]], i8* null)
 
 // CHECK-MRR-LABEL: define void @test_builtin_os_log1(
@@ -69,24 +61,15 @@
 }
 
 // CHECK-LABEL: define void @test_builtin_os_log3(
-// CHECK: alloca i8*, align 8
-// CHECK: %[[OS_LOG_ARG:.*]] = alloca i8*, align 8
 // CHECK: %[[CALL:.*]] = call %{{.*}}* (...) @GenString()
 // CHECK: %[[V1:.*]] = bitcast %{{.*}}* %[[CALL]] to i8*
 // CHECK: %[[V2:.*]] = notail call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %[[V1]])
 // CHECK: %[[V3:.*]] = bitcast i8* %[[V2]] to %{{.*}}*
-// CHECK: %[[V4:.*]] = bitcast %{{.*}}* %[[V3]] to i8*
-// CHECK: %[[V5:.*]] = call i8* @llvm.objc.retain(i8* %[[V4]])
-// CHECK: store i8* %[[V5]], i8** %[[OS_LOG_ARG]], align 8
-// CHECK: %[[V6:.*]] = ptrtoint i8* %[[V5]] to i64
-// CHECK: call void @__os_log_helper_1_2_1_8_64(i8* %{{.*}}, i64 %[[V6]])
+// CHECK: call void @__os_log_helper_1_2_1_8_64(
+// CHECK: call void @os_log_pack_send(
+// CHECK-O2: call void (...) @llvm.objc.clang.arc.use(%[[V1]]* %[[V3]])
 // CHECK: %[[V7:.*]] = bitcast %{{.*}}* %[[V3]] to i8*
 // CHECK: call void @llvm.objc.release(i8* %[[V7]])
-// CHECK: call void @os_log_pack_send(i8* %{{.*}})
-// CHECK-O2: call void (...) @llvm.objc.clang.arc.use(i8* %[[V5]])
-// CHECK-O2: %[[V9:.*]] = load i8*, i8** %[[OS_LOG_ARG]], align 8
-// CHECK-O2: call void @llvm.objc.release(i8* %[[V9]])
-// CHECK-O0: call void @llvm.objc.storeStrong(i8** %[[OS_LOG_ARG]], i8* null)
 
 void test_builtin_os_log3(void *buf) {
   __builtin_os_log_format(buf, "capabilities: %@", (id)GenString());
@@ -94,41 +77,214 @@
 }
 
 // CHECK-LABEL: define void @test_builtin_os_log4(
-// CHECK: alloca i8*, align 8
-// CHECK: %[[OS_LOG_ARG:.*]] = alloca i8*, align 8
-// CHECK: %[[OS_LOG_ARG2:.*]] = alloca i8*, align 8
-// CHECK: %[[CALL:.*]] = call {{.*}} @objc_msgSend
+// CHECK: %[[CALL:.*]] = call i8* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to i8* (i8*, i8*)*)(
 // CHECK: %[[V4:.*]] = notail call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %[[CALL]])
-// CHECK: %[[V5:.*]] = call i8* @llvm.objc.retain(i8* %[[V4]])
-// CHECK: store i8* %[[V5]], i8** %[[OS_LOG_ARG]], align 8
-// CHECK: %[[V6:.*]] = ptrtoint i8* %[[V5]] to i64
-// CHECK: %[[CALL1:.*]] = call {{.*}} @objc_msgSend
-// CHECK: %[[V10:.*]] = notail call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %[[CALL1]])
-// CHECK: %[[V11:.*]] = call i8* @llvm.objc.retain(i8* %[[V10]])
-// CHECK: store i8* %[[V11]], i8** %[[OS_LOG_ARG2]], align 8
-// CHECK: %[[V12:.*]] = ptrtoint i8* %[[V11]] to i64
-// CHECK: call void @__os_log_helper_1_2_2_8_64_8_64(i8* %{{.*}}, i64 %[[V6]], i64 %[[V12]])
-// CHECK: call void @llvm.objc.release(i8* %[[V10]])
+// CHECK: %[[CALL1:.*]] = call i8* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to i8* (
+// CHECK: %[[V9:.*]] = notail call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %[[CALL1]])
+// CHECK: call void @__os_log_helper_1_2_2_8_64_8_64(
+// CHECK: call void @os_log_pack_send(
+// CHECK-O2: call void (...) @llvm.objc.clang.arc.use(i8* %[[V9]])
+// CHECK: call void @llvm.objc.release(i8* %[[V9]])
+// CHECK-O2: call void (...) @llvm.objc.clang.arc.use(i8* %[[V4]])
 // CHECK: call void @llvm.objc.release(i8* %[[V4]])
-// CHECK: call void @os_log_pack_send(i8* %{{.*}})
-// CHECK-O2: call void (...) @llvm.objc.clang.arc.use(i8* %[[V11]])
-// CHECK-O2: %[[V14:.*]] = load i8*, i8** %[[OS_LOG_ARG2]], align 8
-// CHECK-O2: call void @llvm.objc.release(i8* %[[V14]])
-// CHECK-O2: call void (...) @llvm.objc.clang.arc.use(i8* %[[V5]])
-// CHECK-O2: %[[V15:.*]] = load i8*, i8** %[[OS_LOG_ARG]], align 8
-// CHECK-O2: call void @llvm.objc.release(i8* %[[V15]])
 
 void test_builtin_os_log4(void *buf) {
   __builtin_os_log_format(buf, "capabilities: %@ %@", [c m0], [C m1]);
   os_log_pack_send(buf);
 }
 
-// FIXME: Lifetime of GenString's return should be extended in this case too.
 // CHECK-LABEL: define void @test_builtin_os_log5(
+// CHECK: %[[CALL:.*]] = call %{{.*}}* (...) @GenString()
+// CHECK: %[[V1:.*]] = bitcast %{{.}}* %[[CALL]] to i8*
+// CHECK: %[[V2:.*]] = notail call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %[[V1]])
+// CHECK: %[[V3:.*]] = bitcast i8* %[[V2]] to %{{.*}}*
+// CHECK: call void @__os_log_helper_1_2_1_8_64(
 // CHECK: call void @os_log_pack_send(
-// CHECK-NOT: call void @llvm.objc.release(
+// CHECK-O2: call void (...) @llvm.objc.clang.arc.use(%{{.*}}* %[[V3]])
+// CHECK: %[[V6:.*]] = bitcast %{{.*}}* %[[V3]] to i8*
+// CHECK: call void @llvm.objc.release(i8* %[[V6]])
 
 void test_builtin_os_log5(void *buf) {
   __builtin_os_log_format(buf, "capabilities: %@", (0, GenString()));
   os_log_pack_send(buf);
 }
+
+// CHECK-LABEL: define void @test_builtin_os_log6(
+// CHECK: call void @os_log_pack_send(
+// CHECK-NOT: call
+
+void test_builtin_os_log6(void *buf) {
+  __builtin_os_log_format(buf, "%@", (GenString(), g0));
+  os_log_pack_send(buf);
+}
+
+// CHECK-LABEL: define void @test_builtin_os_log7(
+// CHECK: alloca i8*, align 8
+// CHECK: %[[TMP:.*]] = alloca %{{.*}}*, align 8
+// CHECK: %[[CALL:.*]] = call %{{.*}}* (...) @GenString()
+// CHECK: %[[V1:.*]] = bitcast %{{.}}* %[[CALL]] to i8*
+// CHECK: %[[V2:.*]] = notail call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %[[V1]])
+// CHECK: %[[V3:.*]] = bitcast i8* %[[V2]] to %{{.*}}*
+// CHECK: %[[V4:.*]] = bitcast %{{.*}}* %[[V3]] to i8*
+// CHECK: call void @llvm.objc.release(i8* %[[V4]])
+// CHECK: %[[CALL1:.*]] = call %{{.*}}* (...) @GenString()
+// CHECK: %[[V5:.*]] = bitcast %{{.}}* %[[CALL1]] to i8*
+// CHECK: %[[V6:.*]] = notail call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %[[V5]])
+// CHECK: %[[V7:.*]] = bitcast i8* %[[V6]] to %{{.*}}*
+// CHECK: store %{{.}}* %[[V7]], %{{.}}** %[[TMP]], align 8
+// CHECK: %[[V8:.*]] = load %{{.}}*, %{{.}}** %[[TMP]], align 8
+// CHECK: call void @__os_log_helper_1_2_1_8_64(
+// CHECK: call void @os_log_pack_send(
+// CHECK-O2: call void (...) @llvm.objc.clang.arc.use(%{{.*}}* %[[V8]])
+// CHECK: %[[V11:.*]] = bitcast %{{.*}}* %[[V8]] to i8*
+// CHECK: call void @llvm.objc.release(i8* %[[V11]])
+
+void test_builtin_os_log7(void *buf) {
+  __builtin_os_log_format(buf, "%@", ({ GenString(); GenString(); }));
+  os_log_pack_send(buf);
+}
+
+// CHECK-LABEL: define void @test_builtin_os_log8(
+// CHECK: %[[CALL:.*]] = call %{{.*}}* (...) @GenString()
+// CHECK: %[[V1:.*]] = bitcast %{{.}}* %[[CALL]] to i8*
+// CHECK: %[[V2:.*]] = notail call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %[[V1]])
+// CHECK: %[[V3:.*]] = bitcast i8* %[[V2]] to %{{.*}}*
+// CHECK: call void @__os_log_helper_1_2_1_8_64(
+// CHECK: call void @os_log_pack_send(
+// CHECK-O2: call void (...) @llvm.objc.clang.arc.use(%{{.*}}* %[[V3]])
+// CHECK: %[[V6:.*]] = bitcast %{{.*}}* %[[V3]] to i8*
+// CHECK: call void @llvm.objc.release(i8* %[[V6]])
+
+void test_builtin_os_log8(void *buf) {
+  __builtin_os_log_format(buf, "%@", (g0, (g0, GenString())));
+  os_log_pack_send(buf);
+}
+
+// CHECK-O2-LABEL: define void @test_builtin_os_log9(
+// CHECK-O2: %[[CLEANUP_COND:.*]] = alloca i1, align 1
+// CHECK-O2: %[[COND_CLEANUP_SAVE:.*]] = alloca %{{.*}}*, align 8
+// CHECK-O2: %[[CLEANUP_COND3:.*]] = alloca i1, align 1
+// CHECK-O2: %[[COND_CLEANUP_SAVE4:.*]] = alloca %{{.*}}*, align 8
+// CHECK-O2: %[[CLEANUP_COND10:.*]] = alloca i1, align 1
+// CHECK-O2: %[[COND_CLEANUP_SAVE11:.*]] = alloca %{{.*}}*, align 8
+// CHECK-O2: %[[CLEANUP_COND12:.*]] = alloca i1, align 1
+// CHECK-O2: %[[COND_CLEANUP_SAVE13:.*]] = alloca %{{.*}}*, align 8
+// CHECK-O2: store i1 false, i1* %[[CLEANUP_COND]], align 1
+// CHECK-O2: store i1 false, i1* %[[CLEANUP_COND3]], align 1
+// CHECK-O2: store i1 false, i1* %[[CLEANUP_COND10]], align 1
+// CHECK-O2: store i1 false, i1* %[[CLEANUP_COND12]], align 1
+
+// CHECK-O2: %[[CALL:.*]] = call %{{.*}}* (...) @GenString()
+// CHECK-O2: %[[V3:.*]] = bitcast %{{.*}}* %[[CALL]] to i8*
+// CHECK-O2: %[[V4:.*]] = notail call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %[[V3]])
+// CHECK-O2: %[[V5:.*]] = bitcast i8* %[[V4]] to %{{.*}}*
+// CHECK-O2: store i1 true, i1* %[[CLEANUP_COND]], align 1
+// CHECK-O2: store %{{.*}}* %[[V5]], %{{.*}}** %[[COND_CLEANUP_SAVE]], align 8
+// CHECK-O2: store i1 true, i1* %[[CLEANUP_COND3]], align 1
+// CHECK-O2: store %{{.*}}* %[[V5]], %{{.*}}** %[[COND_CLEANUP_SAVE4]], align 8
+// CHECK-O2: %[[V6:.*]] = bitcast %{{.*}}* %[[V5]] to i8*
+
+// CHECK-O2: %[[V7:.*]] = load i8*, i8** @g1, align 8
+
+// CHECK-O2: %[[COND:.*]] = phi i8* [ %[[V6]], %{{.*}} ], [ %[[V7]], %{{.*}} ]
+
+// CHECK-O2: %[[V9:.*]] = load i8*, i8** @g0, align 8
+
+// CHECK-O2: %[[CALL9:.*]] = call %{{.*}}* (...) @GenString()
+// CHECK-O2: %[[V10:.*]] = bitcast %{{.*}}* %[[CALL9]] to i8*
+// CHECK-O2: %[[V11:.*]] = notail call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %[[V10]])
+// CHECK-O2: %[[V12:.*]] = bitcast i8* %[[V11]] to %{{.*}}*
+// CHECK-O2: store i1 true, i1* %[[CLEANUP_COND10]], align 1
+// CHECK-O2: store %{{.*}}* %[[V12]], %{{.*}}** %[[COND_CLEANUP_SAVE11]], align 8
+// CHECK-O2: store i1 true, i1* %[[CLEANUP_COND12]], align 1
+// CHECK-O2: store %{{.*}}* %[[V12]], %{{.*}}** %[[COND_CLEANUP_SAVE13]], align 8
+// CHECK-O2: %[[V13:.*]] = bitcast %{{.*}}* %[[V12]] to i8*
+
+// CHECK-O2: %[[COND15:.*]] = phi i8* [ %[[V9]], %{{.*}} ], [ %[[V13]], %{{.*}} ]
+
+// CHECK-O2: %[[COND17:.*]] = phi i8* [ %[[COND]], %{{.*}} ], [ %[[COND15]], %{{.*}} ]
+// CHECK-O2: %[[V14:.*]] = ptrtoint i8* %[[COND17]] to i64
+// CHECK-O2: call void @__os_log_helper_1_2_1_8_64(i8* %{{.*}}, i64 %[[V14]])
+// CHECK-O2: call void @os_log_pack_send(
+// CHECK-O2: %[[CLEANUP_IS_ACTIVE:.*]] = load i1, i1* %[[CLEANUP_COND12]], align 1
+// CHECK-O2: br i1 %[[CLEANUP_IS_ACTIVE]],
+
+// CHECK-O2: %[[V16:.*]] = load %{{.*}}*, %{{.*}}** %[[COND_CLEANUP_SAVE13]], align 8
+// CHECK-O2: call void (...) @llvm.objc.clang.arc.use(%{{.*}}* %[[V16]])
+
+// CHECK-O2: %[[CLEANUP_IS_ACTIVE18:.*]] = load i1, i1* %[[CLEANUP_COND10]], align 1
+// CHECK-O2: br i1 %[[CLEANUP_IS_ACTIVE18]],
+
+// CHECK-O2: %[[V17:.*]] = load %{{.*}}*, %{{.*}}** %[[COND_CLEANUP_SAVE11]], align 8
+// CHECK-O2: %[[V18:.*]] = bitcast %{{.*}}* %[[V17]] to i8*
+// CHECK-O2: call void @llvm.objc.release(i8* %[[V18]])
+
+// CHECK-O2: %[[CLEANUP_IS_ACTIVE21:.*]] = load i1, i1* %[[CLEANUP_COND3]], align 1
+// CHECK-O2: br i1 %[[CLEANUP_IS_ACTIVE21]],
+
+// CHECK-O2: %[[V19:.*]] = load %{{.*}}*, %{{.*}}** %[[COND_CLEANUP_SAVE4]], align 8
+// CHECK-O2: call void (...) @llvm.objc.clang.arc.use(%{{.*}}* %[[V19]])
+
+// CHECK-O2: %[[CLEANUP_IS_ACTIVE24:.*]] = load i1, i1* %[[CLEANUP_COND]], align 1
+// CHECK-O2: br i1 %[[CLEANUP_IS_ACTIVE24]],
+
+// CHECK-O2: %[[V20:.*]] = load %{{.*}}*, %{{.*}}** %[[COND_CLEANUP_SAVE]], align 8
+// CHECK-O2: %[[V21:.*]] = bitcast %{{.*}}* %[[V20]] to i8*
+// CHECK-O2: call void @llvm.objc.release(i8* %[[V21]])
+
+void test_builtin_os_log9(void *buf, int c) {
+  __builtin_os_log_format(buf, "%@", c > 11 ? (c > 20 ? GenString() : g1) : (0, (c < 0 ? g0 : GenString())));
+  os_log_pack_send(buf);
+}
+
+// CHECK-O2-LABEL: define void @test_builtin_os_log10(
+// CHECK-O2: %[[CALL:.*]] = call i8* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to i8* (i8*, i8*)*)(
+// CHECK-O2: %[[V7:.*]] = notail call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %[[CALL]])
+// CHECK-O2: call void @__os_log_helper_1_2_1_8_64(
+// CHECK-O2: call void @os_log_pack_send(
+// CHECK-O2: call void (...) @llvm.objc.clang.arc.use(i8* %[[V7]])
+// CHECK-O2: call void @llvm.objc.release(i8* %[[V7]])
+
+void test_builtin_os_log10(void *buf, C *c) {
+  __builtin_os_log_format(buf, "%@", c.prop0);
+  os_log_pack_send(buf);
+}
+
+// CHECK-O2-LABEL: define void @test_builtin_os_log11(
+// CHECK-O2: alloca i8*, align 8
+// CHECK-O2: %[[TMP:.*]] = alloca i8*, align 8
+// CHECK-O2: %[[CALL:.*]] = call %{{.*}}* (...) @GenString()
+// CHECK-O2: %[[V2:.*]] = bitcast %{{.*}}* %[[CALL]] to i8*
+// CHECK-O2: %[[V3:.*]] = notail call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %[[V2]])
+// CHECK-O2: %[[V4:.*]] = bitcast i8* %[[V3]] to %{{.*}}*
+// CHECK-O2: call void @__os_log_helper_1_2_1_8_64(
+// CHECK-O2: call void @os_log_pack_send(
+// CHECK-O2: %[[V7:.*]] = load i8*, i8** @g0, align 8
+// CHECK-O2: %[[V8:.*]] = call i8* @llvm.objc.retain(i8* %[[V7]])
+// CHECK-O2: store i8* %[[V8]], i8** %[[TMP]], align 8
+// CHECK-O2: call void (...) @llvm.objc.clang.arc.use(%{{.*}}* %[[V4]])
+// CHECK-O2: %[[V9:.*]] = bitcast %{{.*}}* %[[V4]] to i8*
+// CHECK-O2: call void @llvm.objc.release(i8* %[[V9]])
+// CHECK-O2: %[[V10:.*]] = load i8*, i8** %[[TMP]], align 8
+// CHECK-O2: %[[CALL1:.*]] = call %{{.*}}* (...) @GenString()
+// CHECK-O2: %[[V12:.*]] = bitcast %{{.*}}* %[[CALL1]] to i8*
+// CHECK-O2: %[[V13:.*]] = notail call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %[[V12]])
+// CHECK-O2: %[[V14:.*]] = bitcast i8* %[[V13]] to %{{.*}}*
+// CHECK-O2: call void @__os_log_helper_1_2_2_8_64_8_64(
+// CHECK-O2: call void @os_log_pack_send(
+// CHECK-O2: call void (...) @llvm.objc.clang.arc.use(%{{.*}}* %[[V14]])
+// CHECK-O2: %[[V17:.*]] = bitcast %{{.*}}* %[[V14]] to i8*
+// CHECK-O2: call void @llvm.objc.release(i8* %[[V17]])
+// CHECK-O2: call void (...) @llvm.objc.clang.arc.use(i8* %[[V10]])
+// CHECK-O2: call void @llvm.objc.release(i8* %[[V10]])
+
+void test_builtin_os_log11(void *buf) {
+  __builtin_os_log_format(buf, "%@ %@",
+                          ({
+                            __builtin_os_log_format(buf, "%@", GenString());
+                            os_log_pack_send(buf);
+                            g0;
+                          }),
+                          GenString());
+  os_log_pack_send(buf);
+}
Index: clang/test/AST/ast-dump-stmt.m
===================================================================
--- clang/test/AST/ast-dump-stmt.m
+++ clang/test/AST/ast-dump-stmt.m
@@ -56,3 +56,16 @@
 // CHECK:       ExprWithCleanups
 // CHECK-NEXT:    cleanup CompoundLiteralExpr
 // CHECK:           CompoundLiteralExpr{{.*}}'S':'S' lvalue
+
+void TestBuiltinOSLog(void *buf) {
+  extern id getObj(void);
+  __builtin_os_log_format(buf, "%@", getObj());
+}
+
+// CHECK:       FunctionDecl{{.*}}TestBuiltinOSLog
+// CHECK:         ExprWithCleanups
+// CHECK-NEXT:      cleanup ImplicitCastExpr [[EXPR_ADDR:.*]]
+// CHECK:             ImplicitCastExpr [[EXPR_ADDR]]
+// CHECK-NEXT:          CallExpr
+// CHECK-NEXT:            ImplicitCastExpr
+// CHECK-NEXT:              DeclRefExpr{{.*}}'getObj' 'id (void)'
Index: clang/test/AST/ast-dump-objc-arc-json.m
===================================================================
--- clang/test/AST/ast-dump-objc-arc-json.m
+++ clang/test/AST/ast-dump-objc-arc-json.m
@@ -34,3 +34,16 @@
 // CHECK-NEXT:    "kind": "CompoundLiteralExpr"
 // CHECK-NEXT:   }
 // CHECK-NEXT:  ],
+
+id GetObj(void);
+
+// CHECK:       "cleanups": [
+// CHECK-NEXT:    {
+// CHECK-NEXT:      "id": "0x{{.*}}",
+// CHECK-NEXT:      "kind": "ImplicitCastExpr"
+// CHECK-NEXT:    }
+// CHECK-NEXT:  ],
+
+void TestBuiltinOSLogFormat(void *buf) {
+  __builtin_os_log_format(buf, "%@", GetObj());
+}
Index: clang/lib/Serialization/ASTWriterStmt.cpp
===================================================================
--- clang/lib/Serialization/ASTWriterStmt.cpp
+++ clang/lib/Serialization/ASTWriterStmt.cpp
@@ -1824,6 +1824,11 @@
     } else if (auto *CLE = Obj.dyn_cast<CompoundLiteralExpr *>()) {
       Record.push_back(serialization::COK_CompoundLiteral);
       Record.AddStmt(CLE);
+    } else if (auto *ICE = Obj.dyn_cast<ImplicitCastExpr *>()) {
+      Record.push_back(serialization::COK_ImplicitCastExpr);
+      Record.AddStmt(ICE);
+    } else {
+      llvm_unreachable("unexpected cleanup object type");
     }
   }
 
Index: clang/lib/Serialization/ASTReaderStmt.cpp
===================================================================
--- clang/lib/Serialization/ASTReaderStmt.cpp
+++ clang/lib/Serialization/ASTReaderStmt.cpp
@@ -1916,6 +1916,8 @@
       Obj = readDeclAs<BlockDecl>();
     else if (CleanupKind == COK_CompoundLiteral)
       Obj = cast<CompoundLiteralExpr>(Record.readSubExpr());
+    else if (CleanupKind == COK_ImplicitCastExpr)
+      Obj = cast<ImplicitCastExpr>(Record.readSubExpr());
     else
       llvm_unreachable("unexpected cleanup object type");
     E->getTrailingObjects<ExprWithCleanups::CleanupObject>()[i] = Obj;
Index: clang/lib/Sema/SemaChecking.cpp
===================================================================
--- clang/lib/Sema/SemaChecking.cpp
+++ clang/lib/Sema/SemaChecking.cpp
@@ -6077,6 +6077,68 @@
   return false;
 }
 
+/// Extend the lifetime of ObjC temporary objects under ARC that are passed to
+/// __builtin_os_log_format to the end of the enclosing scope.
+///
+/// For example, the lifetime of the object returned by the call to getObj is
+/// extended and doesn't end at the end of the full expression in the code
+/// below:
+///
+///  id getObj(void);
+///  // the pointer to the object is stored to 'buf' and must be kept alive
+///  // until 'os_log_pack_send' is called.
+///  __builtin_os_log_format(buf, "%@", getObj());
+///  os_log_pack_send(buf);
+
+static void lifetimeExtendOSLogFormatArg(Expr *E, Sema &S) {
+  while (true) {
+    E = E->IgnoreParens();
+
+    switch (E->getStmtClass()) {
+    case Expr::ConditionalOperatorClass:
+    case Expr::BinaryConditionalOperatorClass: {
+      auto *ACO = cast<AbstractConditionalOperator>(E);
+      lifetimeExtendOSLogFormatArg(ACO->getTrueExpr(), S);
+      lifetimeExtendOSLogFormatArg(ACO->getFalseExpr(), S);
+      return;
+    }
+    case Expr::BinaryOperatorClass: {
+      auto *BO = cast<BinaryOperator>(E);
+      // Only the RHS of a comma operator needs lifetime-extension.
+      if (BO->getOpcode() == BO_Comma) {
+        E = BO->getRHS();
+        continue;
+      }
+      return;
+    }
+    case Expr::PseudoObjectExprClass: {
+      // Visit the result expression of expressions for ObjC property accesses.
+      E = cast<PseudoObjectExpr>(E)->getResultExpr();
+      continue;
+    }
+    default: {
+      if (auto *CE = dyn_cast<CastExpr>(E)) {
+        switch (CE->getCastKind()) {
+        case CK_ARCConsumeObject:
+        case CK_ARCReclaimReturnedObject: {
+          S.Cleanup.setExprNeedsCleanups(true);
+          S.ExprCleanupObjects.push_back(cast<ImplicitCastExpr>(E));
+          S.getCurFunction()->setHasBranchProtectedScope();
+          return;
+        }
+        default: {
+          // Ignore any other cast expressions.
+          E = CE->getSubExpr();
+          continue;
+        }
+        }
+      }
+      return;
+    }
+    }
+  }
+}
+
 bool Sema::SemaBuiltinOSLogFormat(CallExpr *TheCall) {
   unsigned BuiltinID =
       cast<FunctionDecl>(TheCall->getCalleeDecl())->getBuiltinID();
@@ -6126,6 +6188,9 @@
         TheCall->getArg(i), VariadicFunction, nullptr);
     if (Arg.isInvalid())
       return true;
+    if (getLangOpts().ObjCAutoRefCount &&
+        Arg.get()->getType()->isObjCRetainableType())
+      lifetimeExtendOSLogFormatArg(Arg.get(), *this);
     CharUnits ArgSize = Context.getTypeSizeInChars(Arg.get()->getType());
     if (ArgSize.getQuantity() >= 0x100) {
       return Diag(Arg.get()->getEndLoc(), diag::err_os_log_argument_too_big)
Index: clang/lib/Sema/JumpDiagnostics.cpp
===================================================================
--- clang/lib/Sema/JumpDiagnostics.cpp
+++ clang/lib/Sema/JumpDiagnostics.cpp
@@ -76,6 +76,8 @@
   void BuildScopeInformation(VarDecl *D, const BlockDecl *BDecl,
                              unsigned &ParentScope);
   void BuildScopeInformation(CompoundLiteralExpr *CLE, unsigned &ParentScope);
+  void BuildLifetimeExtendedARCObjScopeInformation(ImplicitCastExpr *ICE,
+                                                   unsigned &ParentScope);
   void BuildScopeInformation(Stmt *S, unsigned &origParentScope);
 
   void VerifyJumps();
@@ -287,6 +289,14 @@
   ParentScope = Scopes.size() - 1;
 }
 
+void JumpScopeChecker::BuildLifetimeExtendedARCObjScopeInformation(
+    ImplicitCastExpr *ICE, unsigned &ParentScope) {
+  unsigned InDiag = diag::note_enters_lifetime_extended_arc_obj_scope;
+  unsigned OutDiag = diag::note_exits_lifetime_extended_arc_obj_scope;
+  Scopes.push_back(GotoScope(ParentScope, InDiag, OutDiag, ICE->getExprLoc()));
+  ParentScope = Scopes.size() - 1;
+}
+
 /// BuildScopeInformation - The statements from CI to CE are known to form a
 /// coherent VLA scope with a specified parent node.  Walk through the
 /// statements, adding any labels or gotos to LabelAndGotoScopes and recursively
@@ -547,6 +557,8 @@
         }
       else if (auto *CLE = EWC->getObject(i).dyn_cast<CompoundLiteralExpr *>())
         BuildScopeInformation(CLE, origParentScope);
+      else if (auto *ICE = EWC->getObject(i).dyn_cast<ImplicitCastExpr *>())
+        BuildLifetimeExtendedARCObjScopeInformation(ICE, origParentScope);
       else
         llvm_unreachable("unexpected cleanup object type");
     }
Index: clang/lib/CodeGen/CodeGenFunction.h
===================================================================
--- clang/lib/CodeGen/CodeGenFunction.h
+++ clang/lib/CodeGen/CodeGenFunction.h
@@ -276,6 +276,27 @@
   // because of jumps.
   VarBypassDetector Bypasses;
 
+  // The current ExprWithCleanups expression being visited.
+  const ExprWithCleanups *CurExprWithCleanups = nullptr;
+
+  // RAII for saving the current CurExprWithCleanups and restoring it later.
+  struct SaveExprWithCleanupsRAII {
+    SaveExprWithCleanupsRAII(const ExprWithCleanups *E, CodeGenFunction &CGF)
+        : OldExprWithCleanups(CGF.CurExprWithCleanups), CGF(CGF) {
+      CGF.CurExprWithCleanups = E;
+    }
+    ~SaveExprWithCleanupsRAII() {
+      CGF.CurExprWithCleanups = OldExprWithCleanups;
+    }
+    const ExprWithCleanups *OldExprWithCleanups;
+    CodeGenFunction &CGF;
+  };
+
+  // Indicates whether the expression is in the cleanup list of
+  // CurExprWithCleanups and represents an object whose lifetime is extended
+  // under ARC.
+  bool isLifetimeExtendedARCObject(const Expr *E) const;
+
   // CodeGen lambda for loops and support for ordered clause
   typedef llvm::function_ref<void(CodeGenFunction &, const OMPLoopDirective &,
                                   JumpDest)>
@@ -4150,12 +4171,14 @@
   llvm::Value *EmitObjCAllocInit(llvm::Value *value, llvm::Type *resultType);
 
   llvm::Value *EmitObjCThrowOperand(const Expr *expr);
-  llvm::Value *EmitObjCConsumeObject(QualType T, llvm::Value *Ptr);
+  llvm::Value *EmitObjCConsumeObject(QualType T, llvm::Value *Ptr,
+                                     bool extendLifetime = false);
   llvm::Value *EmitObjCExtendObjectLifetime(QualType T, llvm::Value *Ptr);
 
   llvm::Value *EmitARCExtendBlockObject(const Expr *expr);
   llvm::Value *EmitARCReclaimReturnedObject(const Expr *e,
-                                            bool allowUnsafeClaim);
+                                            bool allowUnsafeClaim,
+                                            bool extendLifetime = false);
   llvm::Value *EmitARCRetainScalarExpr(const Expr *expr);
   llvm::Value *EmitARCRetainAutoreleaseScalarExpr(const Expr *expr);
   llvm::Value *EmitARCUnsafeUnretainedScalarExpr(const Expr *expr);
Index: clang/lib/CodeGen/CodeGenFunction.cpp
===================================================================
--- clang/lib/CodeGen/CodeGenFunction.cpp
+++ clang/lib/CodeGen/CodeGenFunction.cpp
@@ -2233,6 +2233,14 @@
   CGF->IsSanitizerScope = false;
 }
 
+bool CodeGenFunction::isLifetimeExtendedARCObject(const Expr *E) const {
+  return CurExprWithCleanups &&
+         llvm::any_of(CurExprWithCleanups->getObjects(),
+                      [&](const ExprWithCleanups::CleanupObject &C) {
+                        return C.dyn_cast<ImplicitCastExpr *>() == E;
+                      });
+}
+
 void CodeGenFunction::InsertHelper(llvm::Instruction *I,
                                    const llvm::Twine &Name,
                                    llvm::BasicBlock *BB,
Index: clang/lib/CodeGen/CGObjC.cpp
===================================================================
--- clang/lib/CodeGen/CGObjC.cpp
+++ clang/lib/CodeGen/CGObjC.cpp
@@ -1981,13 +1981,36 @@
   };
 }
 
+namespace {
+// ARC cleanup for __builtin_os_log_format
+struct CallObjCArcUse final : EHScopeStack::Cleanup {
+  CallObjCArcUse(llvm::Value *object) : object(object) {}
+  llvm::Value *object;
+
+  void Emit(CodeGenFunction &CGF, Flags flags) override {
+    CGF.EmitARCIntrinsicUse(object);
+  }
+};
+} // namespace
+
 /// Produce the code for a CK_ARCConsumeObject.  Does a primitive
 /// release at the end of the full-expression.
 llvm::Value *CodeGenFunction::EmitObjCConsumeObject(QualType type,
-                                                    llvm::Value *object) {
+                                                    llvm::Value *object,
+                                                    bool extendLifetime) {
   // If we're in a conditional branch, we need to make the cleanup
   // conditional.
-  pushFullExprCleanup<CallObjCRelease>(getARCCleanupKind(), object);
+  CleanupKind cleanupKind = getARCCleanupKind();
+  if (extendLifetime) {
+    if (cleanupKind & EHCleanup)
+      pushFullExprCleanup<CallObjCRelease>(EHCleanup, object);
+    pushCleanupAfterFullExpr<CallObjCRelease>(cleanupKind, object);
+    // Push a clang.arc.use call to prevent ARC optimizer from moving the
+    // release call upward on normal paths.
+    if (CGM.getCodeGenOpts().OptimizationLevel != 0)
+      pushCleanupAfterFullExpr<CallObjCArcUse>(NormalCleanup, object);
+  } else
+    pushFullExprCleanup<CallObjCRelease>(cleanupKind, object);
   return object;
 }
 
@@ -2883,14 +2906,14 @@
            });
 }
 
-llvm::Value *CodeGenFunction::EmitARCReclaimReturnedObject(const Expr *E,
-                                                      bool allowUnsafeClaim) {
+llvm::Value *CodeGenFunction::EmitARCReclaimReturnedObject(
+    const Expr *E, bool allowUnsafeClaim, bool extendLifetime) {
   if (allowUnsafeClaim &&
       CGM.getLangOpts().ObjCRuntime.hasARCUnsafeClaimAutoreleasedReturnValue()) {
     return emitARCUnsafeClaimCallResult(*this, E);
   } else {
     llvm::Value *value = emitARCRetainCallResult(*this, E);
-    return EmitObjCConsumeObject(E->getType(), value);
+    return EmitObjCConsumeObject(E->getType(), value, extendLifetime);
   }
 }
 
Index: clang/lib/CodeGen/CGExprScalar.cpp
===================================================================
--- clang/lib/CodeGen/CGExprScalar.cpp
+++ clang/lib/CodeGen/CGExprScalar.cpp
@@ -2179,9 +2179,11 @@
   case CK_ARCProduceObject:
     return CGF.EmitARCRetainScalarExpr(E);
   case CK_ARCConsumeObject:
-    return CGF.EmitObjCConsumeObject(E->getType(), Visit(E));
+    return CGF.EmitObjCConsumeObject(E->getType(), Visit(E),
+                                     CGF.isLifetimeExtendedARCObject(CE));
   case CK_ARCReclaimReturnedObject:
-    return CGF.EmitARCReclaimReturnedObject(E, /*allowUnsafe*/ Ignored);
+    return CGF.EmitARCReclaimReturnedObject(
+        E, /*allowUnsafe*/ Ignored, CGF.isLifetimeExtendedARCObject(CE));
   case CK_ARCExtendBlockObject:
     return CGF.EmitARCExtendBlockObject(E);
 
@@ -2348,6 +2350,7 @@
 }
 
 Value *ScalarExprEmitter::VisitExprWithCleanups(ExprWithCleanups *E) {
+  CodeGenFunction::SaveExprWithCleanupsRAII SaveExprWithCleanups(E, CGF);
   CodeGenFunction::RunCleanupsScope Scope(CGF);
   Value *V = Visit(E->getSubExpr());
   // Defend against dominance problems caused by jumps out of expression
Index: clang/lib/CodeGen/CGExprComplex.cpp
===================================================================
--- clang/lib/CodeGen/CGExprComplex.cpp
+++ clang/lib/CodeGen/CGExprComplex.cpp
@@ -226,6 +226,7 @@
     return Visit(DIE->getExpr());
   }
   ComplexPairTy VisitExprWithCleanups(ExprWithCleanups *E) {
+    CodeGenFunction::SaveExprWithCleanupsRAII SaveExprWithCleanups(E, CGF);
     CodeGenFunction::RunCleanupsScope Scope(CGF);
     ComplexPairTy Vals = Visit(E->getSubExpr());
     // Defend against dominance problems caused by jumps out of expression
Index: clang/lib/CodeGen/CGExprAgg.cpp
===================================================================
--- clang/lib/CodeGen/CGExprAgg.cpp
+++ clang/lib/CodeGen/CGExprAgg.cpp
@@ -1354,6 +1354,7 @@
 }
 
 void AggExprEmitter::VisitExprWithCleanups(ExprWithCleanups *E) {
+  CodeGenFunction::SaveExprWithCleanupsRAII SaveExprWithCleanups(E, CGF);
   CodeGenFunction::RunCleanupsScope cleanups(CGF);
   Visit(E->getSubExpr());
 }
Index: clang/lib/CodeGen/CGBuiltin.cpp
===================================================================
--- clang/lib/CodeGen/CGBuiltin.cpp
+++ clang/lib/CodeGen/CGBuiltin.cpp
@@ -1174,18 +1174,6 @@
   llvm_unreachable("Incorrect MSVC intrinsic!");
 }
 
-namespace {
-// ARC cleanup for __builtin_os_log_format
-struct CallObjCArcUse final : EHScopeStack::Cleanup {
-  CallObjCArcUse(llvm::Value *object) : object(object) {}
-  llvm::Value *object;
-
-  void Emit(CodeGenFunction &CGF, Flags flags) override {
-    CGF.EmitARCIntrinsicUse(object);
-  }
-};
-}
-
 Value *CodeGenFunction::EmitCheckedArgForBuiltin(const Expr *E,
                                                  BuiltinCheckKind Kind) {
   assert((Kind == BCK_CLZPassedZero || Kind == BCK_CTZPassedZero)
@@ -1347,44 +1335,6 @@
       ArgVal = llvm::Constant::getIntegerValue(Int64Ty, llvm::APInt(64, Val));
     } else if (const Expr *TheExpr = Item.getExpr()) {
       ArgVal = EmitScalarExpr(TheExpr, /*Ignore*/ false);
-
-      // If a temporary object that requires destruction after the full
-      // expression is passed, push a lifetime-extended cleanup to extend its
-      // lifetime to the end of the enclosing block scope.
-      auto LifetimeExtendObject = [&](const Expr *E) {
-        E = E->IgnoreParenCasts();
-        // Extend lifetimes of objects returned by function calls and message
-        // sends.
-
-        // FIXME: We should do this in other cases in which temporaries are
-        //        created including arguments of non-ARC types (e.g., C++
-        //        temporaries).
-        if (isa<CallExpr>(E) || isa<ObjCMessageExpr>(E))
-          return true;
-        return false;
-      };
-
-      if (TheExpr->getType()->isObjCRetainableType() &&
-          getLangOpts().ObjCAutoRefCount && LifetimeExtendObject(TheExpr)) {
-        assert(getEvaluationKind(TheExpr->getType()) == TEK_Scalar &&
-               "Only scalar can be a ObjC retainable type");
-        if (!isa<Constant>(ArgVal)) {
-          CleanupKind Cleanup = getARCCleanupKind();
-          QualType Ty = TheExpr->getType();
-          Address Alloca = Address::invalid();
-          Address Addr = CreateMemTemp(Ty, "os.log.arg", &Alloca);
-          ArgVal = EmitARCRetain(Ty, ArgVal);
-          Builder.CreateStore(ArgVal, Addr);
-          pushLifetimeExtendedDestroy(Cleanup, Alloca, Ty,
-                                      CodeGenFunction::destroyARCStrongPrecise,
-                                      Cleanup & EHCleanup);
-
-          // Push a clang.arc.use call to ensure ARC optimizer knows that the
-          // argument has to be alive.
-          if (CGM.getCodeGenOpts().OptimizationLevel != 0)
-            pushCleanupAfterFullExpr<CallObjCArcUse>(Cleanup, ArgVal);
-        }
-      }
     } else {
       ArgVal = Builder.getInt32(Item.getConstValue().getQuantity());
     }
Index: clang/lib/AST/TextNodeDumper.cpp
===================================================================
--- clang/lib/AST/TextNodeDumper.cpp
+++ clang/lib/AST/TextNodeDumper.cpp
@@ -685,6 +685,15 @@
       }
       dumpPointer(CLE);
     });
+  else if (auto *ICE = C.dyn_cast<ImplicitCastExpr *>())
+    AddChild([=] {
+      OS << "cleanup ";
+      {
+        ColorScope Color(OS, ShowColors, StmtColor);
+        OS << ICE->getStmtClassName();
+      }
+      dumpPointer(ICE);
+    });
   else
     llvm_unreachable("unexpected cleanup type");
 }
Index: clang/lib/AST/JSONNodeDumper.cpp
===================================================================
--- clang/lib/AST/JSONNodeDumper.cpp
+++ clang/lib/AST/JSONNodeDumper.cpp
@@ -1338,6 +1338,11 @@
           Obj["id"] = createPointerRepresentation(CLE);
           Obj["kind"] = CLE->getStmtClassName();
           JOS.value(std::move(Obj));
+        } else if (auto *ICE = CO.dyn_cast<ImplicitCastExpr *>()) {
+          llvm::json::Object Obj;
+          Obj["id"] = createPointerRepresentation(ICE);
+          Obj["kind"] = ICE->getStmtClassName();
+          JOS.value(std::move(Obj));
         } else {
           llvm_unreachable("unexpected cleanup object type");
         }
Index: clang/lib/AST/ASTImporter.cpp
===================================================================
--- clang/lib/AST/ASTImporter.cpp
+++ clang/lib/AST/ASTImporter.cpp
@@ -7997,6 +7997,9 @@
   if (auto *CLE = From.dyn_cast<CompoundLiteralExpr *>()) {
     if (Expected<Expr *> R = Import(CLE))
       return ExprWithCleanups::CleanupObject(cast<CompoundLiteralExpr>(*R));
+  } else if (auto *ICE = From.dyn_cast<ImplicitCastExpr *>()) {
+    if (Expected<Expr *> R = Import(ICE))
+      return ExprWithCleanups::CleanupObject(cast<ImplicitCastExpr>(*R));
   }
 
   // FIXME: Handle BlockDecl when we implement importing BlockExpr in
Index: clang/include/clang/Serialization/ASTBitCodes.h
===================================================================
--- clang/include/clang/Serialization/ASTBitCodes.h
+++ clang/include/clang/Serialization/ASTBitCodes.h
@@ -1981,7 +1981,11 @@
     };
 
     /// Kinds of cleanup objects owned by ExprWithCleanups.
-    enum CleanupObjectKind { COK_Block, COK_CompoundLiteral };
+    enum CleanupObjectKind {
+      COK_Block,
+      COK_CompoundLiteral,
+      COK_ImplicitCastExpr
+    };
 
     /// Describes the redeclarations of a declaration.
     struct LocalRedeclarationsInfo {
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -5641,6 +5641,8 @@
   "to destroy">;
 def note_enters_compound_literal_scope : Note<
   "jump enters lifetime of a compound literal that is non-trivial to destruct">;
+def note_enters_lifetime_extended_arc_obj_scope : Note<
+  "jump enters lifetime of an object whose lifetime is extended under ARC">;
 
 def note_exits_cleanup : Note<
   "jump exits scope of variable with __attribute__((cleanup))">;
@@ -5686,6 +5688,8 @@
   "to destroy">;
 def note_exits_compound_literal_scope : Note<
   "jump exits lifetime of a compound literal that is non-trivial to destruct">;
+def note_exits_lifetime_extended_arc_obj_scope : Note<
+  "jump exits lifetime of an object whose lifetime is extended under ARC">;
 
 def err_func_returning_qualified_void : ExtWarn<
   "function cannot return qualified void type %0">,
Index: clang/include/clang/AST/ExprCXX.h
===================================================================
--- clang/include/clang/AST/ExprCXX.h
+++ clang/include/clang/AST/ExprCXX.h
@@ -3297,13 +3297,15 @@
     : public FullExpr,
       private llvm::TrailingObjects<
           ExprWithCleanups,
-          llvm::PointerUnion<BlockDecl *, CompoundLiteralExpr *>> {
+          llvm::PointerUnion<BlockDecl *, CompoundLiteralExpr *,
+                             ImplicitCastExpr *>> {
 public:
   /// The type of objects that are kept in the cleanup.
-  /// It's useful to remember the set of blocks and block-scoped compound
-  /// literals; we could also remember the set of temporaries, but there's
-  /// currently no need.
-  using CleanupObject = llvm::PointerUnion<BlockDecl *, CompoundLiteralExpr *>;
+  /// It's useful to remember the set of blocks, block-scoped compound
+  /// literals, and objects whose lifetime is extended under ARC; we could also
+  /// remember the set of temporaries, but there's currently no need.
+  using CleanupObject = llvm::PointerUnion<BlockDecl *, CompoundLiteralExpr *,
+                                           ImplicitCastExpr *>;
 
 private:
   friend class ASTStmtReader;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to