Added block type introspection support.

As per Fariborz's suggestion, committed now but can be reverted later if the used flag is problematic for Apple.



git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@89134 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp
index 1b3e092..c3dc8bc 100644
--- a/lib/AST/ASTContext.cpp
+++ b/lib/AST/ASTContext.cpp
@@ -3071,6 +3071,54 @@
   return sz / getTypeSize(CharTy);
 }
 
+/// getObjCEncodingForBlockDecl - Return the encoded type for this method
+/// declaration.
+void ASTContext::getObjCEncodingForBlock(const BlockExpr *Expr, 
+                                             std::string& S) {
+  const BlockDecl *Decl = Expr->getBlockDecl();
+  QualType BlockTy =
+      Expr->getType()->getAs<BlockPointerType>()->getPointeeType();
+  // Encode result type.
+  getObjCEncodingForType(cast<FunctionType>(BlockTy)->getResultType(), S);
+  // Compute size of all parameters.
+  // Start with computing size of a pointer in number of bytes.
+  // FIXME: There might(should) be a better way of doing this computation!
+  SourceLocation Loc;
+  int PtrSize = getTypeSize(VoidPtrTy) / getTypeSize(CharTy);
+  int ParmOffset = PtrSize;
+  for (ObjCMethodDecl::param_iterator PI = Decl->param_begin(),
+       E = Decl->param_end(); PI != E; ++PI) {
+    QualType PType = (*PI)->getType();
+    int sz = getObjCEncodingTypeSize(PType);
+    assert (sz > 0 && "BlockExpr - Incomplete param type");
+    ParmOffset += sz;
+  }
+  // Size of the argument frame
+  S += llvm::utostr(ParmOffset);
+  // Block pointer and offset.
+  S += "@?0";
+  ParmOffset = PtrSize;
+  
+  // Argument types.
+  ParmOffset = PtrSize;
+  for (BlockDecl::param_const_iterator PI = Decl->param_begin(), E =
+       Decl->param_end(); PI != E; ++PI) {
+    ParmVarDecl *PVDecl = *PI;
+    QualType PType = PVDecl->getOriginalType(); 
+    if (const ArrayType *AT =
+          dyn_cast<ArrayType>(PType->getCanonicalTypeInternal())) {
+      // Use array's original type only if it has known number of
+      // elements.
+      if (!isa<ConstantArrayType>(AT))
+        PType = PVDecl->getType();
+    } else if (PType->isFunctionType())
+      PType = PVDecl->getType();
+    getObjCEncodingForType(PType, S);
+    S += llvm::utostr(ParmOffset);
+    ParmOffset += getObjCEncodingTypeSize(PType);
+  }
+}
+
 /// getObjCEncodingForMethodDecl - Return the encoded type for this method
 /// declaration.
 void ASTContext::getObjCEncodingForMethodDecl(const ObjCMethodDecl *Decl,
diff --git a/lib/CodeGen/CGBlocks.cpp b/lib/CodeGen/CGBlocks.cpp
index 682cf5d..bc9eb67 100644
--- a/lib/CodeGen/CGBlocks.cpp
+++ b/lib/CodeGen/CGBlocks.cpp
@@ -146,7 +146,23 @@
   if (0 && CanBlockBeGlobal(Info))
     return CGM.GetAddrOfGlobalBlock(BE, Name.c_str());
 
-  std::vector<llvm::Constant*> Elts(5);
+  size_t BlockFields = 5;
+
+  bool hasIntrospection  = CGM.getContext().getLangOptions().BlockIntrospection;
+
+  if (hasIntrospection) {
+    BlockFields++;
+  }
+  std::vector<llvm::Constant*> Elts(BlockFields);
+
+  if (hasIntrospection) {
+    std::string BlockTypeEncoding;
+    CGM.getContext().getObjCEncodingForBlock(BE, BlockTypeEncoding);
+
+    Elts[5] = llvm::ConstantExpr::getBitCast(
+            CGM.GetAddrOfConstantCString(BlockTypeEncoding), PtrToInt8Ty);
+  }
+
   llvm::Constant *C;
   llvm::Value *V;
 
@@ -154,6 +170,9 @@
     // C = BuildBlockStructInitlist();
     unsigned int flags = BLOCK_HAS_DESCRIPTOR;
 
+    if (hasIntrospection)
+      flags |= BLOCK_HAS_OBJC_TYPE;
+
     // We run this first so that we set BlockHasCopyDispose from the entire
     // block literal.
     // __invoke
@@ -211,19 +230,21 @@
       return C;
     }
 
-    std::vector<const llvm::Type *> Types(5+subBlockDeclRefDecls.size());
+    std::vector<const llvm::Type *> Types(BlockFields+subBlockDeclRefDecls.size());
     for (int i=0; i<4; ++i)
       Types[i] = Elts[i]->getType();
     Types[4] = PtrToInt8Ty;
+    if (hasIntrospection)
+      Types[5] = PtrToInt8Ty;
 
     for (unsigned i=0; i < subBlockDeclRefDecls.size(); ++i) {
       const Expr *E = subBlockDeclRefDecls[i];
       const BlockDeclRefExpr *BDRE = dyn_cast<BlockDeclRefExpr>(E);
       QualType Ty = E->getType();
       if (BDRE && BDRE->isByRef()) {
-        Types[i+5] = llvm::PointerType::get(BuildByRefType(BDRE->getDecl()), 0);
+        Types[i+BlockFields] = llvm::PointerType::get(BuildByRefType(BDRE->getDecl()), 0);
       } else
-        Types[i+5] = ConvertType(Ty);
+        Types[i+BlockFields] = ConvertType(Ty);
     }
 
     llvm::StructType *Ty = llvm::StructType::get(VMContext, Types, true);
@@ -237,6 +258,8 @@
 
     for (unsigned i=0; i<4; ++i)
       Builder.CreateStore(Elts[i], Builder.CreateStructGEP(V, i, "block.tmp"));
+    if (hasIntrospection)
+      Builder.CreateStore(Elts[5], Builder.CreateStructGEP(V, 5, "block.tmp"));
 
     for (unsigned i=0; i < subBlockDeclRefDecls.size(); ++i)
       {
@@ -252,7 +275,7 @@
         BlockDeclRefExpr *BDRE = dyn_cast<BlockDeclRefExpr>(E);
         VD = BDRE->getDecl();
 
-        llvm::Value* Addr = Builder.CreateStructGEP(V, i+5, "tmp");
+        llvm::Value* Addr = Builder.CreateStructGEP(V, i+BlockFields, "tmp");
         NoteForHelper[helpersize].index = i+5;
         NoteForHelper[helpersize].RequiresCopying
           = BlockRequiresCopying(VD->getType());
@@ -291,7 +314,7 @@
         RValue r = EmitAnyExpr(E, Addr, false);
         if (r.isScalar()) {
           llvm::Value *Loc = r.getScalarVal();
-          const llvm::Type *Ty = Types[i+5];
+          const llvm::Type *Ty = Types[i+BlockFields];
           if  (BDRE->isByRef()) {
             // E is now the address of the value field, instead, we want the
             // address of the actual ByRef struct.  We optimize this slightly
@@ -375,8 +398,20 @@
   //   int __reserved;
   //   void (*__invoke)(void *);
   //   struct __block_descriptor *__descriptor;
+  //   // GNU runtime only:
+  //   const char *types;
   // };
-  GenericBlockLiteralType = llvm::StructType::get(IntTy->getContext(),
+  if (CGM.getContext().getLangOptions().BlockIntrospection)
+    GenericBlockLiteralType = llvm::StructType::get(IntTy->getContext(),
+                                                  PtrToInt8Ty,
+                                                  IntTy,
+                                                  IntTy,
+                                                  PtrToInt8Ty,
+                                                  BlockDescPtrTy,
+                                                  PtrToInt8Ty,
+                                                  NULL);
+  else
+    GenericBlockLiteralType = llvm::StructType::get(IntTy->getContext(),
                                                   PtrToInt8Ty,
                                                   IntTy,
                                                   IntTy,
@@ -571,8 +606,12 @@
                              llvm::GlobalVariable::InternalLinkage,
                              DescriptorStruct, "__block_descriptor_global");
 
+  int FieldCount = 5;
   // Generate the constants for the block literal.
-  llvm::Constant *LiteralFields[5];
+  if (CGM.getContext().getLangOptions().BlockIntrospection)
+    FieldCount = 6;
+
+  std::vector<llvm::Constant*> LiteralFields(FieldCount);
 
   CodeGenFunction::BlockInfo Info(0, n);
   uint64_t subBlockSize, subBlockAlign;
@@ -592,7 +631,9 @@
   LiteralFields[0] = getNSConcreteGlobalBlock();
 
   // Flags
-  LiteralFields[1] =
+  LiteralFields[1] = CGM.getContext().getLangOptions().BlockIntrospection ?
+    llvm::ConstantInt::get(IntTy, BLOCK_IS_GLOBAL | BLOCK_HAS_DESCRIPTOR |
+            BLOCK_HAS_OBJC_TYPE) :
     llvm::ConstantInt::get(IntTy, BLOCK_IS_GLOBAL | BLOCK_HAS_DESCRIPTOR);
 
   // Reserved
@@ -603,9 +644,17 @@
 
   // Descriptor
   LiteralFields[4] = Descriptor;
+  
+  // Type encoding
+  if (CGM.getContext().getLangOptions().BlockIntrospection) {
+    std::string BlockTypeEncoding;
+    CGM.getContext().getObjCEncodingForBlock(BE, BlockTypeEncoding);
+
+    LiteralFields[5] = CGM.GetAddrOfConstantCString(BlockTypeEncoding);
+  }
 
   llvm::Constant *BlockLiteralStruct =
-    llvm::ConstantStruct::get(VMContext, &LiteralFields[0], 5, false);
+    llvm::ConstantStruct::get(VMContext, LiteralFields, false);
 
   llvm::GlobalVariable *BlockLiteral =
     new llvm::GlobalVariable(getModule(), BlockLiteralStruct->getType(), true,
diff --git a/lib/CodeGen/CGBlocks.h b/lib/CodeGen/CGBlocks.h
index 3ab4efb..38e02a7 100644
--- a/lib/CodeGen/CGBlocks.h
+++ b/lib/CodeGen/CGBlocks.h
@@ -55,7 +55,8 @@
         BLOCK_HAS_CXX_OBJ =       (1 << 26),
         BLOCK_IS_GC =             (1 << 27),
         BLOCK_IS_GLOBAL =         (1 << 28),
-        BLOCK_HAS_DESCRIPTOR =    (1 << 29)
+        BLOCK_HAS_DESCRIPTOR =    (1 << 29),
+        BLOCK_HAS_OBJC_TYPE  =    (1 << 30)
     };
 };
 
diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp
index 9cfdfdc..34154f3 100644
--- a/lib/Driver/Tools.cpp
+++ b/lib/Driver/Tools.cpp
@@ -926,8 +926,10 @@
 
   // -fblocks=0 is default.
   if (Args.hasFlag(options::OPT_fblocks, options::OPT_fno_blocks,
-                   getToolChain().IsBlocksDefault()))
+                   getToolChain().IsBlocksDefault())) {
+    Args.AddLastArg(CmdArgs, options::OPT_fblock_introspection);
     CmdArgs.push_back("-fblocks");
+  }
 
   if (needsExceptions(Args, InputType, getToolChain().getTriple()))
     CmdArgs.push_back("-fexceptions");