From cc85d217d329aa3c78aa3f57a238e5b7931ee2c5 Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Fri, 21 Sep 2012 00:52:24 +0000 Subject: [PATCH] Add faux-body support for dispatch_once(). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@164348 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Analysis/BodyFarm.cpp | 120 +++++++++++++-- test/Analysis/unix-fns.c | 312 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 422 insertions(+), 10 deletions(-) diff --git a/lib/Analysis/BodyFarm.cpp b/lib/Analysis/BodyFarm.cpp index 217f607d56..c01d4e6fcb 100644 --- a/lib/Analysis/BodyFarm.cpp +++ b/lib/Analysis/BodyFarm.cpp @@ -22,6 +22,113 @@ using namespace clang; typedef Stmt *(*FunctionFarmer)(ASTContext &C, const FunctionDecl *D); +static bool isDispatchBlock(QualType Ty) { + // Is it a block pointer? + const BlockPointerType *BPT = Ty->getAs(); + if (!BPT) + return false; + + // Check if the block pointer type takes no arguments and + // returns void. + const FunctionProtoType *FT = + BPT->getPointeeType()->getAs(); + if (!FT || !FT->getResultType()->isVoidType() || + FT->getNumArgs() != 0) + return false; + + return true; +} + +/// Create a fake body for dispatch_once. +static Stmt *create_dispatch_once(ASTContext &C, const FunctionDecl *D) { + // Check if we have at least two parameters. + if (D->param_size() != 2) + return 0; + + // Check if the first parameter is a pointer to integer type. + const ParmVarDecl *Predicate = D->getParamDecl(0); + QualType PredicateQPtrTy = Predicate->getType(); + const PointerType *PredicatePtrTy = PredicateQPtrTy->getAs(); + if (!PredicatePtrTy) + return 0; + QualType PredicateTy = PredicatePtrTy->getPointeeType(); + if (!PredicateTy->isIntegerType()) + return 0; + + // Check if the second parameter is the proper block type. + const ParmVarDecl *Block = D->getParamDecl(1); + QualType Ty = Block->getType(); + if (!isDispatchBlock(Ty)) + return 0; + + // Everything checks out. Create a fakse body that checks the predicate, + // sets it, and calls the block. Basically, an AST dump of: + // + // void dispatch_once(dispatch_once_t *predicate, dispatch_block_t block) { + // if (!*predicate) { + // *predicate = 1; + // block(); + // } + // } + + // (1) Create the call. + DeclRefExpr *DR = DeclRefExpr::CreateEmpty(C, false, false, false, false); + DR->setDecl(const_cast(Block)); + DR->setType(Ty); + DR->setValueKind(VK_LValue); + ImplicitCastExpr *ICE = ImplicitCastExpr::Create(C, Ty, CK_LValueToRValue, + DR, 0, VK_RValue); + CallExpr *CE = new (C) CallExpr(C, ICE, ArrayRef(), C.VoidTy, + VK_RValue, SourceLocation()); + + // (2) Create the assignment to the predicate. + IntegerLiteral *IL = + IntegerLiteral::Create(C, llvm::APInt(C.getTypeSize(C.IntTy), (uint64_t) 1), + C.IntTy, SourceLocation()); + ICE = ImplicitCastExpr::Create(C, PredicateTy, CK_IntegralCast, IL, 0, + VK_RValue); + DR = DeclRefExpr::CreateEmpty(C, false, false, false, false); + DR->setDecl(const_cast(Predicate)); + DR->setType(PredicateQPtrTy); + DR->setValueKind(VK_LValue); + ImplicitCastExpr *LValToRval = + ImplicitCastExpr::Create(C, PredicateQPtrTy, CK_LValueToRValue, DR, + 0, VK_RValue); + UnaryOperator *UO = new (C) UnaryOperator(LValToRval, UO_Deref, PredicateTy, + VK_LValue, OK_Ordinary, + SourceLocation()); + BinaryOperator *B = new (C) BinaryOperator(UO, ICE, BO_Assign, + PredicateTy, VK_RValue, + OK_Ordinary, + SourceLocation()); + // (3) Create the compound statement. + Stmt *Stmts[2]; + Stmts[0] = B; + Stmts[1] = CE; + CompoundStmt *CS = new (C) CompoundStmt(C, Stmts, 2, SourceLocation(), + SourceLocation()); + + // (4) Create the 'if' condition. + DR = DeclRefExpr::CreateEmpty(C, false, false, false, false); + DR->setDecl(const_cast(Predicate)); + DR->setType(PredicateQPtrTy); + DR->setValueKind(VK_LValue); + LValToRval = ImplicitCastExpr::Create(C, PredicateQPtrTy, CK_LValueToRValue, + DR, 0, VK_RValue); + UO = new (C) UnaryOperator(LValToRval, UO_Deref, PredicateTy, + VK_LValue, OK_Ordinary, + SourceLocation()); + LValToRval = ImplicitCastExpr::Create(C, PredicateTy, CK_LValueToRValue, + UO, 0, VK_RValue); + UO = new (C) UnaryOperator(LValToRval, UO_LNot, C.IntTy, + VK_RValue, OK_Ordinary, SourceLocation()); + + // (5) Create the 'if' statement. + IfStmt *If = new (C) IfStmt(C, SourceLocation(), 0, UO, CS); + return If; +} + + /// Create a fake body for dispatch_sync. static Stmt *create_dispatch_sync(ASTContext &C, const FunctionDecl *D) { @@ -32,16 +139,7 @@ static Stmt *create_dispatch_sync(ASTContext &C, const FunctionDecl *D) { // Check if the second parameter is a block. const ParmVarDecl *PV = D->getParamDecl(1); QualType Ty = PV->getType(); - const BlockPointerType *BPT = Ty->getAs(); - if (!BPT) - return 0; - - // Check if the block pointer type takes no arguments and - // returns void. - const FunctionProtoType *FT = - BPT->getPointeeType()->getAs(); - if (!FT || !FT->getResultType()->isVoidType() || - FT->getNumArgs() != 0) + if (!isDispatchBlock(Ty)) return 0; // Everything checks out. Create a fake body that just calls the block. @@ -53,6 +151,7 @@ static Stmt *create_dispatch_sync(ASTContext &C, const FunctionDecl *D) { // DeclRefExpr *DR = DeclRefExpr::CreateEmpty(C, false, false, false, false); DR->setDecl(const_cast(PV)); + DR->setType(Ty); DR->setValueKind(VK_LValue); ImplicitCastExpr *ICE = ImplicitCastExpr::Create(C, Ty, CK_LValueToRValue, DR, 0, VK_RValue); @@ -80,6 +179,7 @@ Stmt *BodyFarm::getBody(const FunctionDecl *D) { FunctionFarmer FF = llvm::StringSwitch(Name) .Case("dispatch_sync", create_dispatch_sync) + .Case("dispatch_once", create_dispatch_once) .Default(NULL); if (FF) { diff --git a/test/Analysis/unix-fns.c b/test/Analysis/unix-fns.c index 23d08d281a..1f1edad799 100644 --- a/test/Analysis/unix-fns.c +++ b/test/Analysis/unix-fns.c @@ -181,6 +181,15 @@ void test_dispatch_sync(dispatch_queue_t queue, int *q) { }); } +// Test inlining if dispatch_once. +void test_inline_dispatch_once() { + static dispatch_once_t pred; + int *p = 0; + dispatch_once(&pred, ^(void) { + *p = 1; // expected-warning {{null}} + }); +} + // CHECK: diagnostics // CHECK-NEXT: // CHECK-NEXT: @@ -1585,4 +1594,307 @@ void test_dispatch_sync(dispatch_queue_t queue, int *q) { // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: path +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line186 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line186 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line187 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line187 +// CHECK-NEXT: col5 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line187 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line187 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line187 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Variable 'p' initialized to a null pointer value +// CHECK-NEXT: message +// CHECK-NEXT: Variable 'p' initialized to a null pointer value +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line188 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line188 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line190 +// CHECK-NEXT: col4 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Calling '_dispatch_once' +// CHECK-NEXT: message +// CHECK-NEXT: Calling '_dispatch_once' +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line162 +// CHECK-NEXT: col1 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: depth1 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Entered call from 'test_inline_dispatch_once' +// CHECK-NEXT: message +// CHECK-NEXT: Entered call from 'test_inline_dispatch_once' +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line162 +// CHECK-NEXT: col1 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line162 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line164 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line164 +// CHECK-NEXT: col15 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line164 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line164 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line164 +// CHECK-NEXT: col33 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth1 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Calling 'dispatch_once' +// CHECK-NEXT: message +// CHECK-NEXT: Calling 'dispatch_once' +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line38 +// CHECK-NEXT: col1 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: depth2 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Entered call from '_dispatch_once' +// CHECK-NEXT: message +// CHECK-NEXT: Entered call from '_dispatch_once' +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line164 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line164 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line164 +// CHECK-NEXT: col33 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth2 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Calling anonymous block +// CHECK-NEXT: message +// CHECK-NEXT: Calling anonymous block +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line188 +// CHECK-NEXT: col24 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: depth3 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Entered call from 'dispatch_once' +// CHECK-NEXT: message +// CHECK-NEXT: Entered call from 'dispatch_once' +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line188 +// CHECK-NEXT: col24 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line188 +// CHECK-NEXT: col24 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line189 +// CHECK-NEXT: col4 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line189 +// CHECK-NEXT: col4 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line189 +// CHECK-NEXT: col4 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line189 +// CHECK-NEXT: col5 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line189 +// CHECK-NEXT: col5 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth3 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Dereference of null pointer (loaded from variable 'p') +// CHECK-NEXT: message +// CHECK-NEXT: Dereference of null pointer (loaded from variable 'p') +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: descriptionDereference of null pointer (loaded from variable 'p') +// CHECK-NEXT: categoryLogic error +// CHECK-NEXT: typeDereference of null pointer +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line189 +// CHECK-NEXT: col4 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: -- 2.50.1