diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -3297,13 +3297,15 @@ : public FullExpr, private llvm::TrailingObjects< ExprWithCleanups, - llvm::PointerUnion> { + llvm::PointerUnion> { 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; + /// 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; private: friend class ASTStmtReader; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/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">, diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/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 { diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -7997,6 +7997,9 @@ if (auto *CLE = From.dyn_cast()) { if (Expected R = Import(CLE)) return ExprWithCleanups::CleanupObject(cast(*R)); + } else if (auto *ICE = From.dyn_cast()) { + if (Expected R = Import(ICE)) + return ExprWithCleanups::CleanupObject(cast(*R)); } // FIXME: Handle BlockDecl when we implement importing BlockExpr in diff --git a/clang/lib/AST/JSONNodeDumper.cpp b/clang/lib/AST/JSONNodeDumper.cpp --- a/clang/lib/AST/JSONNodeDumper.cpp +++ b/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()) { + llvm::json::Object Obj; + Obj["id"] = createPointerRepresentation(ICE); + Obj["kind"] = ICE->getStmtClassName(); + JOS.value(std::move(Obj)); } else { llvm_unreachable("unexpected cleanup object type"); } diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -685,6 +685,15 @@ } dumpPointer(CLE); }); + else if (auto *ICE = C.dyn_cast()) + AddChild([=] { + OS << "cleanup "; + { + ColorScope Color(OS, ShowColors, StmtColor); + OS << ICE->getStmtClassName(); + } + dumpPointer(ICE); + }); else llvm_unreachable("unexpected cleanup type"); } diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/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(E) || isa(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(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(Cleanup, ArgVal); - } - } } else { ArgVal = Builder.getInt32(Item.getConstValue().getQuantity()); } diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/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()); } diff --git a/clang/lib/CodeGen/CGExprComplex.cpp b/clang/lib/CodeGen/CGExprComplex.cpp --- a/clang/lib/CodeGen/CGExprComplex.cpp +++ b/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 diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/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 diff --git a/clang/lib/CodeGen/CGObjC.cpp b/clang/lib/CodeGen/CGObjC.cpp --- a/clang/lib/CodeGen/CGObjC.cpp +++ b/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(getARCCleanupKind(), object); + CleanupKind cleanupKind = getARCCleanupKind(); + if (extendLifetime) { + if (cleanupKind & EHCleanup) + pushFullExprCleanup(EHCleanup, object); + pushCleanupAfterFullExpr(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(NormalCleanup, object); + } else + pushFullExprCleanup(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); } } diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/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 @@ -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); diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/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() == E; + }); +} + void CodeGenFunction::InsertHelper(llvm::Instruction *I, const llvm::Twine &Name, llvm::BasicBlock *BB, diff --git a/clang/lib/Sema/JumpDiagnostics.cpp b/clang/lib/Sema/JumpDiagnostics.cpp --- a/clang/lib/Sema/JumpDiagnostics.cpp +++ b/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()) BuildScopeInformation(CLE, origParentScope); + else if (auto *ICE = EWC->getObject(i).dyn_cast()) + BuildLifetimeExtendedARCObjScopeInformation(ICE, origParentScope); else llvm_unreachable("unexpected cleanup object type"); } diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp --- a/clang/lib/Sema/SemaChecking.cpp +++ b/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(E); + lifetimeExtendOSLogFormatArg(ACO->getTrueExpr(), S); + lifetimeExtendOSLogFormatArg(ACO->getFalseExpr(), S); + return; + } + case Expr::BinaryOperatorClass: { + auto *BO = cast(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(E)->getResultExpr(); + continue; + } + default: { + if (auto *CE = dyn_cast(E)) { + switch (CE->getCastKind()) { + case CK_ARCConsumeObject: + case CK_ARCReclaimReturnedObject: { + S.Cleanup.setExprNeedsCleanups(true); + S.ExprCleanupObjects.push_back(cast(E)); + S.getCurFunction()->setHasBranchProtectedScope(); + return; + } + default: { + // Ignore any other cast expressions. + E = CE->getSubExpr(); + continue; + } + } + } + return; + } + } + } +} + bool Sema::SemaBuiltinOSLogFormat(CallExpr *TheCall) { unsigned BuiltinID = cast(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) diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -1916,6 +1916,8 @@ Obj = readDeclAs(); else if (CleanupKind == COK_CompoundLiteral) Obj = cast(Record.readSubExpr()); + else if (CleanupKind == COK_ImplicitCastExpr) + Obj = cast(Record.readSubExpr()); else llvm_unreachable("unexpected cleanup object type"); E->getTrailingObjects()[i] = Obj; diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -1824,6 +1824,11 @@ } else if (auto *CLE = Obj.dyn_cast()) { Record.push_back(serialization::COK_CompoundLiteral); Record.AddStmt(CLE); + } else if (auto *ICE = Obj.dyn_cast()) { + Record.push_back(serialization::COK_ImplicitCastExpr); + Record.AddStmt(ICE); + } else { + llvm_unreachable("unexpected cleanup object type"); } } diff --git a/clang/test/AST/ast-dump-objc-arc-json.m b/clang/test/AST/ast-dump-objc-arc-json.m --- a/clang/test/AST/ast-dump-objc-arc-json.m +++ b/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()); +} diff --git a/clang/test/AST/ast-dump-stmt.m b/clang/test/AST/ast-dump-stmt.m --- a/clang/test/AST/ast-dump-stmt.m +++ b/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)' diff --git a/clang/test/CodeGenObjC/os_log.m b/clang/test/CodeGenObjC/os_log.m --- a/clang/test/CodeGenObjC/os_log.m +++ b/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); +} diff --git a/clang/test/CodeGenObjCXX/os_log.mm b/clang/test/CodeGenObjCXX/os_log.mm --- a/clang/test/CodeGenObjCXX/os_log.mm +++ b/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 diff --git a/clang/test/PCH/Inputs/arc.h b/clang/test/PCH/Inputs/arc.h --- a/clang/test/PCH/Inputs/arc.h +++ b/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); +} diff --git a/clang/test/PCH/arc.m b/clang/test/PCH/arc.m --- a/clang/test/PCH/arc.m +++ b/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); +} diff --git a/clang/test/SemaObjC/format-strings-oslog.m b/clang/test/SemaObjC/format-strings-oslog.m --- a/clang/test/SemaObjC/format-strings-oslog.m +++ b/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 #include @@ -6,6 +6,7 @@ #include // 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; +}