Index: test/CodeGen/blocks-2.c =================================================================== --- test/CodeGen/blocks-2.c (revision 81401) +++ test/CodeGen/blocks-2.c (working copy) @@ -1,5 +1,9 @@ // RUN: clang-cc -g %s -emit-llvm -o %t -fblocks && -// RUN: grep "func.start" %t | count 4 +// RUN: grep "func.start" %t | count 4 && +// RUN: clang-cc -g %s -triple i386-unknown-unknown -emit-llvm -o %t -fblocks -fblock-introspection && +// RUN: grep "v8@?0i4" %t | count 1 && +// RUN: clang-cc -g %s -triple i386-unknown-unknown -emit-llvm -o %t -fblocks && +// RUN: grep "v8@?0i4" %t | count 0 && // 1 declaration, 1 bar, 1 test_block_dbg and 1 for the block. static __inline__ __attribute__((always_inline)) int bar(int va, int vb) { return (va == vb); } Index: include/clang/Basic/LangOptions.h =================================================================== --- include/clang/Basic/LangOptions.h (revision 81401) +++ include/clang/Basic/LangOptions.h (working copy) @@ -57,6 +55,7 @@ unsigned POSIXThreads : 1; // Compiling with POSIX thread support // (-pthread) unsigned Blocks : 1; // block extension to C + unsigned BlockIntrospection: 1; // block have ObjC type encodings. unsigned EmitAllDecls : 1; // Emit all declarations, even if // they are unused. unsigned MathErrno : 1; // Math functions must respect errno @@ -139,6 +138,7 @@ ThreadsafeStatics = 0; POSIXThreads = 0; Blocks = 0; + BlockIntrospection = 0; EmitAllDecls = 0; MathErrno = 1; Index: include/clang/AST/ASTContext.h =================================================================== --- include/clang/AST/ASTContext.h (revision 81401) +++ include/clang/AST/ASTContext.h (working copy) @@ -36,6 +36,7 @@ namespace clang { class FileManager; class ASTRecordLayout; + class BlockExpr; class Expr; class ExternalASTSource; class IdentifierTable; @@ -599,6 +600,10 @@ /// declaration. void getObjCEncodingForMethodDecl(const ObjCMethodDecl *Decl, std::string &S); + /// getObjCEncodingForBlockDecl - Return the encoded type for this block + /// declaration. + void getObjCEncodingForBlock(const BlockExpr *Expr, std::string& S); + /// getObjCEncodingForPropertyDecl - Return the encoded type for /// this method declaration. If non-NULL, Container must be either /// an ObjCCategoryImplDecl or ObjCImplementationDecl; it should Index: include/clang/Driver/Options.def =================================================================== --- include/clang/Driver/Options.def (revision 81401) +++ include/clang/Driver/Options.def (working copy) @@ -368,6 +368,7 @@ OPTION("-fastf", fastf, Flag, f_Group, INVALID, "", 0, 0, 0) OPTION("-fast", fast, Flag, f_Group, INVALID, "", 0, 0, 0) OPTION("-fasynchronous-unwind-tables", fasynchronous_unwind_tables, Flag, f_Group, INVALID, "", 0, 0, 0) +OPTION("-fblock-introspection", fblock_introspection, Flag, f_Group, INVALID, "", 0, 0, 0) OPTION("-fblocks", fblocks, Flag, f_Group, INVALID, "", 0, 0, 0) OPTION("-fbootclasspath=", fbootclasspath_EQ, Joined, f_Group, INVALID, "", 0, 0, 0) OPTION("-fbuiltin", fbuiltin, Flag, f_Group, INVALID, "", 0, 0, 0) Index: tools/clang-cc/clang-cc.cpp =================================================================== --- tools/clang-cc/clang-cc.cpp (revision 81401) +++ tools/clang-cc/clang-cc.cpp (working copy) @@ -596,6 +590,10 @@ EnableBlocks("fblocks", llvm::cl::desc("enable the 'blocks' language feature")); static llvm::cl::opt + EnableBlockIntrospection("fblock-introspection", + llvm::cl::desc("emit type encodings with blocks")); + +static llvm::cl::opt EnableHeinousExtensions("fheinous-gnu-extensions", llvm::cl::desc("enable GNU extensions that you really really shouldn't use"), llvm::cl::ValueDisallowed, llvm::cl::Hidden); @@ -803,6 +801,9 @@ Options.Rtti = Rtti; if (EnableBlocks.getPosition()) Options.Blocks = EnableBlocks; + if (EnableBlockIntrospection || GNURuntime) + Options.BlockIntrospection = 1; + if (CharIsSigned.getPosition()) Options.CharIsSigned = CharIsSigned; } Index: lib/CodeGen/CGBlocks.cpp =================================================================== --- lib/CodeGen/CGBlocks.cpp (revision 81401) +++ lib/CodeGen/CGBlocks.cpp (working copy) @@ -112,7 +112,21 @@ if (0 && CanBlockBeGlobal(Info)) return CGM.GetAddrOfGlobalBlock(BE, Name.c_str()); - std::vector Elts(5); + size_t BlockFields = 5; + + if (CGM.getContext().getLangOptions().BlockIntrospection) { + BlockFields++; + } + std::vector Elts(BlockFields); + + if (CGM.getContext().getLangOptions().BlockIntrospection) { + std::string BlockTypeEncoding; + CGM.getContext().getObjCEncodingForBlock(BE, BlockTypeEncoding); + + Elts[5] = llvm::ConstantExpr::getBitCast( + CGM.GetAddrOfConstantCString(BlockTypeEncoding), PtrToInt8Ty); + } + llvm::Constant *C; llvm::Value *V; @@ -175,19 +189,21 @@ return C; } - std::vector Types(5+subBlockDeclRefDecls.size()); + std::vector Types(BlockFields+subBlockDeclRefDecls.size()); for (int i=0; i<4; ++i) Types[i] = Elts[i]->getType(); Types[4] = PtrToInt8Ty; + if (CGM.getContext().getLangOptions().BlockIntrospection) + Types[5] = PtrToInt8Ty; for (unsigned i=0; i < subBlockDeclRefDecls.size(); ++i) { const Expr *E = subBlockDeclRefDecls[i]; const BlockDeclRefExpr *BDRE = dyn_cast(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); @@ -201,6 +217,8 @@ for (unsigned i=0; i<4; ++i) Builder.CreateStore(Elts[i], Builder.CreateStructGEP(V, i, "block.tmp")); + if (CGM.getContext().getLangOptions().BlockIntrospection) + Builder.CreateStore(Elts[5], Builder.CreateStructGEP(V, 5, "block.tmp")); for (unsigned i=0; i < subBlockDeclRefDecls.size(); ++i) { @@ -216,7 +234,7 @@ BlockDeclRefExpr *BDRE = dyn_cast(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()); NoteForHelper[helpersize].flag @@ -227,7 +245,7 @@ NoteForHelper[helpersize].flag = BLOCK_FIELD_IS_BYREF | // FIXME: Someone double check this. (VD->getType().isObjCGCWeak() ? BLOCK_FIELD_IS_WEAK : 0); - const llvm::Type *Ty = Types[i+5]; + const llvm::Type *Ty = Types[i+BlockFields]; llvm::Value *Loc = LocalDeclMap[VD]; Loc = Builder.CreateStructGEP(Loc, 1, "forwarding"); Loc = Builder.CreateLoad(Loc, false); @@ -254,7 +272,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 @@ -338,14 +356,26 @@ // 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, + PtrToInt8Ty, + BlockDescPtrTy, + NULL); getModule().addTypeName("struct.__block_literal_generic", GenericBlockLiteralType); @@ -522,9 +552,13 @@ 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 LiteralFields(FieldCount); + CodeGenFunction::BlockInfo Info(0, n); uint64_t subBlockSize, subBlockAlign; llvm::SmallVector subBlockDeclRefDecls; @@ -554,9 +588,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); + llvm::ConstantStruct::get(VMContext, LiteralFields); llvm::GlobalVariable *BlockLiteral = new llvm::GlobalVariable(getModule(), BlockLiteralStruct->getType(), true, Index: lib/AST/ASTContext.cpp =================================================================== --- lib/AST/ASTContext.cpp (revision 81401) +++ lib/AST/ASTContext.cpp (working copy) @@ -2735,6 +2735,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()->getPointeeType(); + // Encode result type. + getObjCEncodingForType(BlockTy->getAsFunctionType()->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(PType->getCanonicalTypeInternal())) { + // Use array's original type only if it has known number of + // elements. + if (!isa(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, Index: lib/Driver/Tools.cpp =================================================================== --- lib/Driver/Tools.cpp (revision 81401) +++ lib/Driver/Tools.cpp (working copy) @@ -554,8 +554,10 @@ // -fblocks default varies depending on platform and language; only // pass if specified. if (Arg *A = Args.getLastArg(options::OPT_fblocks, options::OPT_fno_blocks)) { - if (A->getOption().matches(options::OPT_fblocks)) + if (A->getOption().matches(options::OPT_fblocks)) { + Args.AddLastArg(CmdArgs, options::OPT_fblock_introspection); CmdArgs.push_back("-fblocks"); + } else CmdArgs.push_back("-fblocks=0"); }