diff options
-rw-r--r-- | include/clang/AST/Expr.h | 5 | ||||
-rw-r--r-- | include/clang/AST/OperationKinds.h | 11 | ||||
-rw-r--r-- | include/clang/AST/Stmt.h | 4 | ||||
-rw-r--r-- | lib/ARCMigrate/TransRetainReleaseDealloc.cpp | 9 | ||||
-rw-r--r-- | lib/ARCMigrate/TransformActions.cpp | 2 | ||||
-rw-r--r-- | lib/ARCMigrate/Transforms.cpp | 9 | ||||
-rw-r--r-- | lib/AST/Expr.cpp | 2 | ||||
-rw-r--r-- | lib/AST/ExprConstant.cpp | 2 | ||||
-rw-r--r-- | lib/AST/Stmt.cpp | 12 | ||||
-rw-r--r-- | lib/CodeGen/CGExpr.cpp | 3 | ||||
-rw-r--r-- | lib/CodeGen/CGExprAgg.cpp | 1 | ||||
-rw-r--r-- | lib/CodeGen/CGExprComplex.cpp | 1 | ||||
-rw-r--r-- | lib/CodeGen/CGExprConstant.cpp | 1 | ||||
-rw-r--r-- | lib/CodeGen/CGExprScalar.cpp | 5 | ||||
-rw-r--r-- | lib/CodeGen/CGObjC.cpp | 8 | ||||
-rw-r--r-- | lib/Sema/SemaChecking.cpp | 1 | ||||
-rw-r--r-- | lib/Sema/SemaExprCXX.cpp | 13 | ||||
-rw-r--r-- | lib/Sema/SemaExprObjC.cpp | 17 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/ExprEngine.cpp | 3 | ||||
-rw-r--r-- | test/CodeGenObjC/arc.m | 51 | ||||
-rw-r--r-- | test/CodeGenObjCXX/arc.mm | 20 |
21 files changed, 136 insertions, 44 deletions
diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h index 66caa4af57..369820aa74 100644 --- a/include/clang/AST/Expr.h +++ b/include/clang/AST/Expr.h @@ -538,6 +538,10 @@ public: /// 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 @@ private: 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 004426e89a..92ff604137 100644 --- a/include/clang/AST/OperationKinds.h +++ b/include/clang/AST/OperationKinds.h @@ -247,15 +247,20 @@ enum CastKind { /// _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 73a878ce7f..fcec08dcc5 100644 --- a/include/clang/AST/Stmt.h +++ b/include/clang/AST/Stmt.h @@ -293,6 +293,10 @@ public: /// 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 c177363b18..fe80a43a6a 100644 --- a/lib/ARCMigrate/TransRetainReleaseDealloc.cpp +++ b/lib/ARCMigrate/TransRetainReleaseDealloc.cpp @@ -116,11 +116,16 @@ private: 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 45a3d473e6..2ae979896a 100644 --- a/lib/ARCMigrate/TransformActions.cpp +++ b/lib/ARCMigrate/TransformActions.cpp @@ -313,7 +313,7 @@ void TransformActionsImpl::removeStmt(Stmt *S) { 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 1a98833fe0..80c52d7940 100644 --- a/lib/ARCMigrate/Transforms.cpp +++ b/lib/ARCMigrate/Transforms.cpp @@ -180,12 +180,9 @@ private: 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 3002e305ac..5d8789fdda 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -1102,6 +1102,8 @@ const char *CastExpr::getCastKindName() const { 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 cc8c50198c..9943222b1d 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -1819,6 +1819,7 @@ bool IntExprEvaluator::VisitCastExpr(const CastExpr *E) { case CK_UserDefinedConversion: case CK_ObjCProduceObject: case CK_ObjCConsumeObject: + case CK_ObjCReclaimReturnedObject: return false; case CK_LValueToRValue: @@ -2325,6 +2326,7 @@ bool ComplexExprEvaluator::VisitCastExpr(const CastExpr *E) { 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 e293f324ab..fd6f21d43b 100644 --- a/lib/AST/Stmt.cpp +++ b/lib/AST/Stmt.cpp @@ -85,6 +85,18 @@ bool Stmt::CollectingStats(bool Enable) { 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 d9e565a440..289eaa08fb 100644 --- a/lib/CodeGen/CGExpr.cpp +++ b/lib/CodeGen/CGExpr.cpp @@ -2011,7 +2011,8 @@ LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) { 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 99e95842eb..93f93b77e8 100644 --- a/lib/CodeGen/CGExprAgg.cpp +++ b/lib/CodeGen/CGExprAgg.cpp @@ -360,6 +360,7 @@ void AggExprEmitter::VisitCastExpr(CastExpr *E) { 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 95dcc0b3ae..a88f112a16 100644 --- a/lib/CodeGen/CGExprComplex.cpp +++ b/lib/CodeGen/CGExprComplex.cpp @@ -407,6 +407,7 @@ ComplexPairTy ComplexExprEmitter::EmitCast(CastExpr::CastKind CK, Expr *Op, 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 e88c28737c..42e1450125 100644 --- a/lib/CodeGen/CGExprConstant.cpp +++ b/lib/CodeGen/CGExprConstant.cpp @@ -582,6 +582,7 @@ public: 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 e7ae6b5708..84457cbbfe 100644 --- a/lib/CodeGen/CGExprScalar.cpp +++ b/lib/CodeGen/CGExprScalar.cpp @@ -1109,6 +1109,11 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) { 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 d8ce1f4a75..1b271ef2af 100644 --- a/lib/CodeGen/CGObjC.cpp +++ b/lib/CodeGen/CGObjC.cpp @@ -2333,6 +2333,14 @@ tryEmitARCRetainScalarExpr(CodeGenFunction &CGF, const Expr *e) { 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 35b6e9abc9..8bad032911 100644 --- a/lib/Sema/SemaChecking.cpp +++ b/lib/Sema/SemaChecking.cpp @@ -3543,6 +3543,7 @@ static bool findRetainCycleOwner(Expr *e, RetainCycleOwner &owner) { 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 6ae48dd28c..4c096fe0ec 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -4039,13 +4039,12 @@ ExprResult Sema::MaybeBindToTemporary(Expr *E) { 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 17a7401d65..fa51098f1a 100644 --- a/lib/Sema/SemaExprObjC.cpp +++ b/lib/Sema/SemaExprObjC.cpp @@ -1771,6 +1771,20 @@ Sema::CheckObjCARCConversion(SourceRange castRange, QualType castType, << 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 @@ ExprResult Sema::BuildObjCBridgedCast(SourceLocation LParenLoc, // 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 4aa5e350bc..fbaa8490e9 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -2194,7 +2194,8 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, // 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 a7db3f0402..062773d34f 100644 --- a/test/CodeGenObjC/arc.m +++ b/test/CodeGenObjC/arc.m @@ -283,7 +283,10 @@ void test10() { // 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 @@ void test10() { // 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 @@ void test12(void) { __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 @@ void test39(void) { // 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 @@ void test40(void) { // 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 @@ void test41(void) { // 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 @@ void test46(__weak id *wp, __weak volatile id *wvp) { // 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 @@ void test48(void) { // 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 d4d9ba7516..0c5466a4ce 100644 --- a/test/CodeGenObjCXX/arc.mm +++ b/test/CodeGenObjCXX/arc.mm @@ -9,17 +9,21 @@ void test0(__weak id *wp, __weak volatile id *wvp) { // 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(); } |