In ARC, reclaim all return values of retainable type, not just those
where we have an immediate need of a retained value.
As an exception, don't do this when the call is made as the immediate
operand of a __bridge retain. This is more in the way of a workaround
than an actual guarantee, so it's acceptable to be brittle here.
rdar://problem/9504800
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@134605 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h
index 66caa4a..369820a 100644
--- a/include/clang/AST/Expr.h
+++ b/include/clang/AST/Expr.h
@@ -538,6 +538,10 @@
/// the rules of C++ [expr.unary.noexcept].
CanThrowResult CanThrow(ASTContext &C) const;
+ /// IgnoreImplicit - Skip past any implicit AST nodes which might
+ /// surround this expression.
+ Expr *IgnoreImplicit() { return cast<Expr>(Stmt::IgnoreImplicit()); }
+
/// IgnoreParens - Ignore parentheses. If this Expr is a ParenExpr, return
/// its subexpression. If that subexpression is also a ParenExpr,
/// then this method recursively returns its subexpression, and so forth.
@@ -2330,6 +2334,7 @@
case CK_IntegralComplexToFloatingComplex:
case CK_ObjCProduceObject:
case CK_ObjCConsumeObject:
+ case CK_ObjCReclaimReturnedObject:
assert(!getType()->isBooleanType() && "unheralded conversion to bool");
// fallthrough to check for null base path
diff --git a/include/clang/AST/OperationKinds.h b/include/clang/AST/OperationKinds.h
index 004426e..92ff604 100644
--- a/include/clang/AST/OperationKinds.h
+++ b/include/clang/AST/OperationKinds.h
@@ -247,15 +247,20 @@
/// _Complex unsigned -> _Complex float
CK_IntegralComplexToFloatingComplex,
- /// \brief Produces an Objective-C object so that it may be
+ /// \brief Produces a retainable object pointer so that it may be
/// consumed, e.g. by being passed to a consuming parameter. Calls
/// objc_retain.
CK_ObjCProduceObject,
- /// \brief Consumes an Objective-C object that has just been
+ /// \brief Consumes a retainable object pointer that has just been
/// produced, e.g. as the return value of a retaining call. Enters
/// a cleanup to call objc_release at some indefinite time.
- CK_ObjCConsumeObject
+ CK_ObjCConsumeObject,
+
+ /// \brief Reclaim a retainable object pointer object that may have
+ /// been produced and autoreleased as part of a function return
+ /// sequence.
+ CK_ObjCReclaimReturnedObject
};
#define CK_Invalid ((CastKind) -1)
diff --git a/include/clang/AST/Stmt.h b/include/clang/AST/Stmt.h
index 73a878c..fcec08d 100644
--- a/include/clang/AST/Stmt.h
+++ b/include/clang/AST/Stmt.h
@@ -293,6 +293,10 @@
/// works on systems with GraphViz (Mac OS X) or dot+gv installed.
void viewAST() const;
+ /// Skip past any implicit AST nodes which might surround this
+ /// statement, such as ExprWithCleanups or ImplicitCastExpr nodes.
+ Stmt *IgnoreImplicit();
+
// Implement isa<T> support.
static bool classof(const Stmt *) { return true; }
diff --git a/lib/ARCMigrate/TransRetainReleaseDealloc.cpp b/lib/ARCMigrate/TransRetainReleaseDealloc.cpp
index c177363..fe80a43 100644
--- a/lib/ARCMigrate/TransRetainReleaseDealloc.cpp
+++ b/lib/ARCMigrate/TransRetainReleaseDealloc.cpp
@@ -116,11 +116,16 @@
return true;
}
- if (ParenExpr *parenE = dyn_cast_or_null<ParenExpr>(StmtMap->getParent(E)))
+ Stmt *parent = StmtMap->getParent(E);
+
+ if (ImplicitCastExpr *castE = dyn_cast_or_null<ImplicitCastExpr>(parent))
+ return tryRemoving(castE);
+
+ if (ParenExpr *parenE = dyn_cast_or_null<ParenExpr>(parent))
return tryRemoving(parenE);
if (BinaryOperator *
- bopE = dyn_cast_or_null<BinaryOperator>(StmtMap->getParent(E))) {
+ bopE = dyn_cast_or_null<BinaryOperator>(parent)) {
if (bopE->getOpcode() == BO_Comma && bopE->getLHS() == E &&
isRemovable(bopE)) {
Pass.TA.replace(bopE->getSourceRange(), bopE->getRHS()->getSourceRange());
diff --git a/lib/ARCMigrate/TransformActions.cpp b/lib/ARCMigrate/TransformActions.cpp
index 45a3d47..2ae9798 100644
--- a/lib/ARCMigrate/TransformActions.cpp
+++ b/lib/ARCMigrate/TransformActions.cpp
@@ -313,7 +313,7 @@
assert(IsInTransaction && "Actions only allowed during a transaction");
ActionData data;
data.Kind = Act_RemoveStmt;
- data.S = S;
+ data.S = S->IgnoreImplicit(); // important for uniquing
CachedActions.push_back(data);
}
diff --git a/lib/ARCMigrate/Transforms.cpp b/lib/ARCMigrate/Transforms.cpp
index 1a98833..80c52d7 100644
--- a/lib/ARCMigrate/Transforms.cpp
+++ b/lib/ARCMigrate/Transforms.cpp
@@ -180,12 +180,9 @@
void mark(Stmt *S) {
if (!S) return;
- if (LabelStmt *Label = dyn_cast<LabelStmt>(S))
- return mark(Label->getSubStmt());
- if (ImplicitCastExpr *CE = dyn_cast<ImplicitCastExpr>(S))
- return mark(CE->getSubExpr());
- if (ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(S))
- return mark(EWC->getSubExpr());
+ while (LabelStmt *Label = dyn_cast<LabelStmt>(S))
+ S = Label->getSubStmt();
+ S = S->IgnoreImplicit();
if (Expr *E = dyn_cast<Expr>(S))
Removables.insert(E);
}
diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp
index 3002e30..5d8789f 100644
--- a/lib/AST/Expr.cpp
+++ b/lib/AST/Expr.cpp
@@ -1102,6 +1102,8 @@
return "ObjCConsumeObject";
case CK_ObjCProduceObject:
return "ObjCProduceObject";
+ case CK_ObjCReclaimReturnedObject:
+ return "ObjCReclaimReturnedObject";
}
llvm_unreachable("Unhandled cast kind!");
diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp
index cc8c501..9943222 100644
--- a/lib/AST/ExprConstant.cpp
+++ b/lib/AST/ExprConstant.cpp
@@ -1819,6 +1819,7 @@
case CK_UserDefinedConversion:
case CK_ObjCProduceObject:
case CK_ObjCConsumeObject:
+ case CK_ObjCReclaimReturnedObject:
return false;
case CK_LValueToRValue:
@@ -2325,6 +2326,7 @@
case CK_IntegralComplexToBoolean:
case CK_ObjCProduceObject:
case CK_ObjCConsumeObject:
+ case CK_ObjCReclaimReturnedObject:
llvm_unreachable("invalid cast kind for complex value");
case CK_LValueToRValue:
diff --git a/lib/AST/Stmt.cpp b/lib/AST/Stmt.cpp
index e293f32..fd6f21d 100644
--- a/lib/AST/Stmt.cpp
+++ b/lib/AST/Stmt.cpp
@@ -85,6 +85,18 @@
return StatSwitch;
}
+Stmt *Stmt::IgnoreImplicit() {
+ Stmt *s = this;
+
+ if (ExprWithCleanups *ewc = dyn_cast<ExprWithCleanups>(s))
+ s = ewc->getSubExpr();
+
+ while (ImplicitCastExpr *ice = dyn_cast<ImplicitCastExpr>(s))
+ s = ice->getSubExpr();
+
+ return s;
+}
+
namespace {
struct good {};
struct bad {};
diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp
index d9e565a..289eaa0 100644
--- a/lib/CodeGen/CGExpr.cpp
+++ b/lib/CodeGen/CGExpr.cpp
@@ -2011,7 +2011,8 @@
case CK_MemberPointerToBoolean:
case CK_AnyPointerToBlockPointerCast:
case CK_ObjCProduceObject:
- case CK_ObjCConsumeObject: {
+ case CK_ObjCConsumeObject:
+ case CK_ObjCReclaimReturnedObject: {
// These casts only produce lvalues when we're binding a reference to a
// temporary realized from a (converted) pure rvalue. Emit the expression
// as a value, copy it into a temporary, and return an lvalue referring to
diff --git a/lib/CodeGen/CGExprAgg.cpp b/lib/CodeGen/CGExprAgg.cpp
index 99e9584..93f93b7 100644
--- a/lib/CodeGen/CGExprAgg.cpp
+++ b/lib/CodeGen/CGExprAgg.cpp
@@ -360,6 +360,7 @@
case CK_IntegralComplexToFloatingComplex:
case CK_ObjCProduceObject:
case CK_ObjCConsumeObject:
+ case CK_ObjCReclaimReturnedObject:
llvm_unreachable("cast kind invalid for aggregate types");
}
}
diff --git a/lib/CodeGen/CGExprComplex.cpp b/lib/CodeGen/CGExprComplex.cpp
index 95dcc0b..a88f112 100644
--- a/lib/CodeGen/CGExprComplex.cpp
+++ b/lib/CodeGen/CGExprComplex.cpp
@@ -407,6 +407,7 @@
case CK_IntegralComplexToBoolean:
case CK_ObjCProduceObject:
case CK_ObjCConsumeObject:
+ case CK_ObjCReclaimReturnedObject:
llvm_unreachable("invalid cast kind for complex value");
case CK_FloatingRealToComplex:
diff --git a/lib/CodeGen/CGExprConstant.cpp b/lib/CodeGen/CGExprConstant.cpp
index e88c287..42e1450 100644
--- a/lib/CodeGen/CGExprConstant.cpp
+++ b/lib/CodeGen/CGExprConstant.cpp
@@ -582,6 +582,7 @@
case CK_Dynamic:
case CK_ObjCProduceObject:
case CK_ObjCConsumeObject:
+ case CK_ObjCReclaimReturnedObject:
return 0;
// These might need to be supported for constexpr.
diff --git a/lib/CodeGen/CGExprScalar.cpp b/lib/CodeGen/CGExprScalar.cpp
index e7ae6b5..84457cb 100644
--- a/lib/CodeGen/CGExprScalar.cpp
+++ b/lib/CodeGen/CGExprScalar.cpp
@@ -1109,6 +1109,11 @@
return CGF.EmitARCRetainScalarExpr(E);
case CK_ObjCConsumeObject:
return CGF.EmitObjCConsumeObject(E->getType(), Visit(E));
+ case CK_ObjCReclaimReturnedObject: {
+ llvm::Value *value = Visit(E);
+ value = CGF.EmitARCRetainAutoreleasedReturnValue(value);
+ return CGF.EmitObjCConsumeObject(E->getType(), value);
+ }
case CK_FloatingRealToComplex:
case CK_FloatingComplexCast:
diff --git a/lib/CodeGen/CGObjC.cpp b/lib/CodeGen/CGObjC.cpp
index d8ce1f4..1b271ef 100644
--- a/lib/CodeGen/CGObjC.cpp
+++ b/lib/CodeGen/CGObjC.cpp
@@ -2333,6 +2333,14 @@
return TryEmitResult(result, true);
}
+ // For reclaims, emit the subexpression as a retained call and
+ // skip the consumption.
+ case CK_ObjCReclaimReturnedObject: {
+ llvm::Value *result = emitARCRetainCall(CGF, ce->getSubExpr());
+ if (resultType) result = CGF.Builder.CreateBitCast(result, resultType);
+ return TryEmitResult(result, true);
+ }
+
case CK_GetObjCProperty: {
llvm::Value *result = emitARCRetainCall(CGF, ce);
if (resultType) result = CGF.Builder.CreateBitCast(result, resultType);
diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp
index 35b6e9a..8bad032 100644
--- a/lib/Sema/SemaChecking.cpp
+++ b/lib/Sema/SemaChecking.cpp
@@ -3543,6 +3543,7 @@
case CK_BitCast:
case CK_LValueBitCast:
case CK_LValueToRValue:
+ case CK_ObjCReclaimReturnedObject:
e = cast->getSubExpr();
continue;
diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp
index 6ae48dd..4c096fe 100644
--- a/lib/Sema/SemaExprCXX.cpp
+++ b/lib/Sema/SemaExprCXX.cpp
@@ -4039,13 +4039,12 @@
ReturnsRetained = (D && D->hasAttr<NSReturnsRetainedAttr>());
}
- if (ReturnsRetained) {
- ExprNeedsCleanups = true;
- E = ImplicitCastExpr::Create(Context, E->getType(),
- CK_ObjCConsumeObject, E, 0,
- VK_RValue);
- }
- return Owned(E);
+ ExprNeedsCleanups = true;
+
+ CastKind ck = (ReturnsRetained ? CK_ObjCConsumeObject
+ : CK_ObjCReclaimReturnedObject);
+ return Owned(ImplicitCastExpr::Create(Context, E->getType(), ck, E, 0,
+ VK_RValue));
}
if (!getLangOptions().CPlusPlus)
diff --git a/lib/Sema/SemaExprObjC.cpp b/lib/Sema/SemaExprObjC.cpp
index 17a7401..fa51098 100644
--- a/lib/Sema/SemaExprObjC.cpp
+++ b/lib/Sema/SemaExprObjC.cpp
@@ -1771,6 +1771,20 @@
<< castRange << castExpr->getSourceRange();
}
+/// Look for an ObjCReclaimReturnedObject cast and destroy it.
+static Expr *maybeUndoReclaimObject(Expr *e) {
+ // For now, we just undo operands that are *immediately* reclaim
+ // expressions, which prevents the vast majority of potential
+ // problems here. To catch them all, we'd need to rebuild arbitrary
+ // value-propagating subexpressions --- we can't reliably rebuild
+ // in-place because of expression sharing.
+ if (ImplicitCastExpr *ice = dyn_cast<ImplicitCastExpr>(e))
+ if (ice->getCastKind() == CK_ObjCReclaimReturnedObject)
+ return ice->getSubExpr();
+
+ return e;
+}
+
ExprResult Sema::BuildObjCBridgedCast(SourceLocation LParenLoc,
ObjCBridgeCastKind Kind,
SourceLocation BridgeKeywordLoc,
@@ -1815,6 +1829,9 @@
// Okay: id -> CF
switch (Kind) {
case OBC_Bridge:
+ // Reclaiming a value that's going to be __bridge-casted to CF
+ // is very dangerous, so we don't do it.
+ SubExpr = maybeUndoReclaimObject(SubExpr);
break;
case OBC_BridgeRetained:
diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 4aa5e35..fbaa849 100644
--- a/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -2194,7 +2194,8 @@
// The analyzer doesn't do anything special with these casts,
// since it understands retain/release semantics already.
case CK_ObjCProduceObject:
- case CK_ObjCConsumeObject: // Fall-through.
+ case CK_ObjCConsumeObject:
+ case CK_ObjCReclaimReturnedObject: // Fall-through.
// True no-ops.
case CK_NoOp:
case CK_FunctionToPointerDecay: {
diff --git a/test/CodeGenObjC/arc.m b/test/CodeGenObjC/arc.m
index a7db3f0..062773d 100644
--- a/test/CodeGenObjC/arc.m
+++ b/test/CodeGenObjC/arc.m
@@ -283,7 +283,10 @@
// CHECK-NEXT: load [[TEST10]]** [[X]], align
// CHECK-NEXT: load i8** @"\01L_OBJC_SELECTOR_REFERENCES_{{[0-9]*}}"
// CHECK-NEXT: bitcast
- // CHECK-NEXT: call [[TEST10]]* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend
+ // CHECK-NEXT: [[T0:%.*]] = call [[TEST10]]* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend
+ // CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST10]]* [[T0]] to i8*
+ // CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T1]])
+ // CHECK-NEXT: [[V:%.*]] = bitcast i8* [[T2]] to [[TEST10]]*
// CHECK-NEXT: load i8** @"\01L_OBJC_SELECTOR_REFERENCES_{{[0-9]*}}"
// CHECK-NEXT: bitcast
// CHECK-NEXT: [[T0:%.*]] = call [[TEST10]]* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend
@@ -292,6 +295,8 @@
// CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] to [[TEST10]]*
// CHECK-NEXT: [[T4:%.*]] = bitcast [[TEST10]]* [[T3]] to i8*
// CHECK-NEXT: store i8* [[T4]], i8** [[Y]]
+ // CHECK-NEXT: [[T0:%.*]] = bitcast [[TEST10]]* [[V]] to i8*
+ // CHECK-NEXT: call void @objc_release(i8* [[T0]])
// CHECK-NEXT: [[T0:%.*]] = load i8** [[Y]]
// CHECK-NEXT: call void @objc_release(i8* [[T0]])
// CHECK-NEXT: [[T0:%.*]] = load [[TEST10]]** [[X]]
@@ -323,11 +328,15 @@
__weak id x = test12_helper();
// CHECK-NEXT: [[T0:%.*]] = call i8* @test12_helper()
- // CHECK-NEXT: call i8* @objc_initWeak(i8** [[X]], i8* [[T0]])
+ // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T0]])
+ // CHECK-NEXT: call i8* @objc_initWeak(i8** [[X]], i8* [[T1]])
+ // CHECK-NEXT: call void @objc_release(i8* [[T1]])
x = test12_helper();
- // CHECK-NEXT: [[T1:%.*]] = call i8* @test12_helper()
+ // CHECK-NEXT: [[T0:%.*]] = call i8* @test12_helper()
+ // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T0]])
// CHECK-NEXT: call i8* @objc_storeWeak(i8** [[X]], i8* [[T1]])
+ // CHECK-NEXT: call void @objc_release(i8* [[T1]])
id y = x;
// CHECK-NEXT: [[T2:%.*]] = call i8* @objc_loadWeakRetained(i8** [[X]])
@@ -1214,7 +1223,9 @@
// CHECK: [[VAR:%.*]] = alloca i8*
// CHECK-NEXT: [[BLOCK:%.*]] = alloca [[BLOCK_T:%.*]],
// CHECK-NEXT: [[T0:%.*]] = call i8* @test39_source()
- // CHECK-NEXT: store i8* [[T0]], i8** [[VAR]],
+ // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T0]])
+ // CHECK-NEXT: store i8* [[T1]], i8** [[VAR]],
+ // CHECK-NEXT: call void @objc_release(i8* [[T1]])
// 0x40000000 - has signature but no copy/dispose
// CHECK: store i32 1073741824, i32*
// CHECK: [[CAPTURE:%.*]] = getelementptr inbounds [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5
@@ -1239,7 +1250,9 @@
// CHECK-NEXT: store i32 33554432, i32* [[T0]]
// CHECK: [[SLOT:%.*]] = getelementptr inbounds [[BYREF_T]]* [[VAR]], i32 0, i32 6
// CHECK-NEXT: [[T0:%.*]] = call i8* @test40_source()
- // CHECK-NEXT: call i8* @objc_initWeak(i8** [[SLOT]], i8* [[T0]])
+ // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T0]])
+ // CHECK-NEXT: call i8* @objc_initWeak(i8** [[SLOT]], i8* [[T1]])
+ // CHECK-NEXT: call void @objc_release(i8* [[T1]])
// CHECK-NEXT: [[SLOT:%.*]] = getelementptr inbounds [[BYREF_T]]* [[VAR]], i32 0, i32 6
// 0x42000000 - has signature, copy/dispose helpers
// CHECK: store i32 1107296256,
@@ -1287,7 +1300,9 @@
// CHECK: [[VAR:%.*]] = alloca i8*,
// CHECK-NEXT: [[BLOCK:%.*]] = alloca [[BLOCK_T:%.*]],
// CHECK: [[T0:%.*]] = call i8* @test41_source()
- // CHECK-NEXT: call i8* @objc_initWeak(i8** [[VAR]], i8* [[T0]])
+ // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T0]])
+ // CHECK-NEXT: call i8* @objc_initWeak(i8** [[VAR]], i8* [[T1]])
+ // CHECK-NEXT: call void @objc_release(i8* [[T1]])
// 0x42000000 - has signature, copy/dispose helpers
// CHECK: store i32 1107296256,
// CHECK: [[SLOT:%.*]] = getelementptr inbounds [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5
@@ -1389,17 +1404,21 @@
// TODO: this is sub-optimal, we should retain at the actual call site.
// CHECK: [[T0:%.*]] = call i8* @test46_helper()
- // CHECK-NEXT: [[T1:%.*]] = load i8*** {{%.*}}, align 8
- // CHECK-NEXT: [[T2:%.*]] = call i8* @objc_storeWeak(i8** [[T1]], i8* [[T0]])
- // CHECK-NEXT: [[T3:%.*]] = call i8* @objc_retain(i8* [[T2]])
- // CHECK-NEXT: store i8* [[T3]], i8**
+ // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T0]])
+ // CHECK-NEXT: [[T2:%.*]] = load i8*** {{%.*}}, align 8
+ // CHECK-NEXT: [[T3:%.*]] = call i8* @objc_storeWeak(i8** [[T2]], i8* [[T1]])
+ // CHECK-NEXT: [[T4:%.*]] = call i8* @objc_retain(i8* [[T3]])
+ // CHECK-NEXT: store i8* [[T4]], i8**
+ // CHECK-NEXT: call void @objc_release(i8* [[T1]])
id x = *wp = test46_helper();
// CHECK: [[T0:%.*]] = call i8* @test46_helper()
- // CHECK-NEXT: [[T1:%.*]] = load i8*** {{%.*}}, align 8
- // CHECK-NEXT: [[T2:%.*]] = call i8* @objc_storeWeak(i8** [[T1]], i8* [[T0]])
- // CHECK-NEXT: [[T3:%.*]] = call i8* @objc_retain(i8* [[T2]])
- // CHECK-NEXT: store i8* [[T3]], i8**
+ // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T0]])
+ // CHECK-NEXT: [[T2:%.*]] = load i8*** {{%.*}}, align 8
+ // CHECK-NEXT: [[T3:%.*]] = call i8* @objc_storeWeak(i8** [[T2]], i8* [[T1]])
+ // CHECK-NEXT: [[T4:%.*]] = call i8* @objc_retain(i8* [[T3]])
+ // CHECK-NEXT: store i8* [[T4]], i8**
+ // CHECK-NEXT: call void @objc_release(i8* [[T1]])
id y = *wvp = test46_helper();
}
@@ -1432,8 +1451,10 @@
// CHECK: [[X:%.*]] = alloca i8*
// CHECK-NEXT: [[T0:%.*]] = call i8* @objc_initWeak(i8** [[X]], i8* null)
// CHECK-NEXT: [[T1:%.*]] = call i8* @test48_helper()
- // CHECK-NEXT: [[T2:%.*]] = call i8* @objc_storeWeak(i8** [[X]], i8* [[T1]])
+ // CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T1]])
// CHECK-NEXT: [[T3:%.*]] = call i8* @objc_storeWeak(i8** [[X]], i8* [[T2]])
+ // CHECK-NEXT: [[T4:%.*]] = call i8* @objc_storeWeak(i8** [[X]], i8* [[T3]])
+ // CHECK-NEXT: call void @objc_release(i8* [[T2]])
// CHECK-NEXT: call void @objc_destroyWeak(i8** [[X]])
// CHECK-NEXT: ret void
}
diff --git a/test/CodeGenObjCXX/arc.mm b/test/CodeGenObjCXX/arc.mm
index d4d9ba7..0c5466a 100644
--- a/test/CodeGenObjCXX/arc.mm
+++ b/test/CodeGenObjCXX/arc.mm
@@ -9,17 +9,21 @@
// TODO: in the non-volatile case, we do not need to be reloading.
// CHECK: [[T0:%.*]] = call i8* @_Z12test0_helperv()
- // CHECK-NEXT: [[T1:%.*]] = load i8*** {{%.*}}, align 8
- // CHECK-NEXT: [[T2:%.*]] = call i8* @objc_storeWeak(i8** [[T1]], i8* [[T0]])
- // CHECK-NEXT: [[T3:%.*]] = call i8* @objc_retain(i8* [[T2]])
- // CHECK-NEXT: store i8* [[T3]], i8**
+ // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T0]])
+ // CHECK-NEXT: [[T2:%.*]] = load i8*** {{%.*}}, align 8
+ // CHECK-NEXT: [[T3:%.*]] = call i8* @objc_storeWeak(i8** [[T2]], i8* [[T1]])
+ // CHECK-NEXT: [[T4:%.*]] = call i8* @objc_retain(i8* [[T3]])
+ // CHECK-NEXT: store i8* [[T4]], i8**
+ // CHECK-NEXT: call void @objc_release(i8* [[T1]])
id x = *wp = test0_helper();
// CHECK: [[T0:%.*]] = call i8* @_Z12test0_helperv()
- // CHECK-NEXT: [[T1:%.*]] = load i8*** {{%.*}}, align 8
- // CHECK-NEXT: [[T2:%.*]] = call i8* @objc_storeWeak(i8** [[T1]], i8* [[T0]])
- // CHECK-NEXT: [[T3:%.*]] = call i8* @objc_loadWeakRetained(i8** [[T1]])
- // CHECK-NEXT: store i8* [[T3]], i8**
+ // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T0]])
+ // CHECK-NEXT: [[T2:%.*]] = load i8*** {{%.*}}, align 8
+ // CHECK-NEXT: [[T3:%.*]] = call i8* @objc_storeWeak(i8** [[T2]], i8* [[T1]])
+ // CHECK-NEXT: [[T4:%.*]] = call i8* @objc_loadWeakRetained(i8** [[T2]])
+ // CHECK-NEXT: store i8* [[T4]], i8**
+ // CHECK-NEXT: call void @objc_release(i8* [[T1]])
id y = *wvp = test0_helper();
}