Add faux-body support for dispatch_once().

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@164348 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Analysis/BodyFarm.cpp b/lib/Analysis/BodyFarm.cpp
index 217f607..c01d4e6 100644
--- a/lib/Analysis/BodyFarm.cpp
+++ b/lib/Analysis/BodyFarm.cpp
@@ -22,6 +22,113 @@
 
 typedef Stmt *(*FunctionFarmer)(ASTContext &C, const FunctionDecl *D);
 
+static bool isDispatchBlock(QualType Ty) {
+  // Is it a block pointer?
+  const BlockPointerType *BPT = Ty->getAs<BlockPointerType>();
+  if (!BPT)
+    return false;
+
+  // Check if the block pointer type takes no arguments and
+  // returns void.
+  const FunctionProtoType *FT =
+  BPT->getPointeeType()->getAs<FunctionProtoType>();
+  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<PointerType>();
+  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<ParmVarDecl*>(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<Expr*>(), 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<ParmVarDecl*>(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<ParmVarDecl*>(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 @@
   // Check if the second parameter is a block.
   const ParmVarDecl *PV = D->getParamDecl(1);
   QualType Ty = PV->getType();
-  const BlockPointerType *BPT = Ty->getAs<BlockPointerType>();
-  if (!BPT)
-    return 0;
-  
-  // Check if the block pointer type takes no arguments and
-  // returns void.
-  const FunctionProtoType *FT =
-    BPT->getPointeeType()->getAs<FunctionProtoType>();
-  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 @@
   //
   DeclRefExpr *DR = DeclRefExpr::CreateEmpty(C, false, false, false, false);
   DR->setDecl(const_cast<ParmVarDecl*>(PV));
+  DR->setType(Ty);
   DR->setValueKind(VK_LValue);
   ImplicitCastExpr *ICE = ImplicitCastExpr::Create(C, Ty, CK_LValueToRValue,
                                                    DR, 0, VK_RValue);
@@ -80,6 +179,7 @@
   FunctionFarmer FF =
     llvm::StringSwitch<FunctionFarmer>(Name)
       .Case("dispatch_sync", create_dispatch_sync)
+      .Case("dispatch_once", create_dispatch_once)
       .Default(NULL);
   
   if (FF) {