//===--- CGBlocks.cpp - Emit LLVM Code for declarations -------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This contains code to emit blocks. // //===----------------------------------------------------------------------===// #include "CodeGenFunction.h" #include "CodeGenModule.h" #include "llvm/Module.h" #include "llvm/Target/TargetData.h" #include using namespace clang; using namespace CodeGen; // Temporary code to enable testing of __block variables // #include "clang/Frontend/CompileOptions.h" #include "llvm/Support/CommandLine.h" static llvm::cl::opt Enable__block("f__block", // See all the FIXMEs for the various work that needs to be done llvm::cl::desc("temporary option to turn on __block precessing " "even though the code isn't done yet"), llvm::cl::ValueDisallowed, llvm::cl::AllowInverse, llvm::cl::ZeroOrMore); llvm::Constant *CodeGenFunction::BuildDescriptorBlockDecl(uint64_t Size) { const llvm::PointerType *PtrToInt8Ty = llvm::PointerType::getUnqual(llvm::Type::Int8Ty); const llvm::Type *UnsignedLongTy = CGM.getTypes().ConvertType(getContext().UnsignedLongTy); llvm::Constant *C; std::vector Elts; // reserved C = llvm::ConstantInt::get(UnsignedLongTy, 0); Elts.push_back(C); // Size // FIXME: What is the right way to say this doesn't fit? We should give // a user diagnostic in that case. Better fix would be to change the // API to size_t. C = llvm::ConstantInt::get(UnsignedLongTy, Size); Elts.push_back(C); if (BlockHasCopyDispose) { // copy_func_helper_decl // FIXME: implement C = llvm::ConstantInt::get(UnsignedLongTy, 0); C = llvm::ConstantExpr::getBitCast(C, PtrToInt8Ty); Elts.push_back(C); // destroy_func_decl // FIXME: implement C = llvm::ConstantInt::get(UnsignedLongTy, 0); C = llvm::ConstantExpr::getBitCast(C, PtrToInt8Ty); Elts.push_back(C); } C = llvm::ConstantStruct::get(Elts); C = new llvm::GlobalVariable(C->getType(), true, llvm::GlobalValue::InternalLinkage, C, "__block_descriptor_tmp", &CGM.getModule()); return C; } llvm::Constant *BlockModule::getNSConcreteGlobalBlock() { if (NSConcreteGlobalBlock) return NSConcreteGlobalBlock; const llvm::PointerType *PtrToInt8Ty = llvm::PointerType::getUnqual(llvm::Type::Int8Ty); // FIXME: We should have a CodeGenModule::AddRuntimeVariable that does the // same thing as CreateRuntimeFunction if there's already a variable with // the same name. NSConcreteGlobalBlock = new llvm::GlobalVariable(PtrToInt8Ty, false, llvm::GlobalValue::ExternalLinkage, 0, "_NSConcreteGlobalBlock", &getModule()); return NSConcreteGlobalBlock; } llvm::Constant *BlockModule::getNSConcreteStackBlock() { if (NSConcreteStackBlock) return NSConcreteStackBlock; const llvm::PointerType *PtrToInt8Ty = llvm::PointerType::getUnqual(llvm::Type::Int8Ty); // FIXME: We should have a CodeGenModule::AddRuntimeVariable that does the // same thing as CreateRuntimeFunction if there's already a variable with // the same name. NSConcreteStackBlock = new llvm::GlobalVariable(PtrToInt8Ty, false, llvm::GlobalValue::ExternalLinkage, 0, "_NSConcreteStackBlock", &getModule()); return NSConcreteStackBlock; } static void CollectBlockDeclRefInfo(const Stmt *S, CodeGenFunction::BlockInfo &Info) { for (Stmt::const_child_iterator I = S->child_begin(), E = S->child_end(); I != E; ++I) if (*I) CollectBlockDeclRefInfo(*I, Info); if (const BlockDeclRefExpr *DE = dyn_cast(S)) { // FIXME: Handle enums. if (isa(DE->getDecl())) return; if (DE->isByRef()) Info.ByRefDeclRefs.push_back(DE); else Info.ByCopyDeclRefs.push_back(DE); } } /// CanBlockBeGlobal - Given a BlockInfo struct, determines if a block /// can be declared as a global variable instead of on the stack. static bool CanBlockBeGlobal(const CodeGenFunction::BlockInfo &Info) { return Info.ByRefDeclRefs.empty() && Info.ByCopyDeclRefs.empty(); } // FIXME: Push most into CGM, passing down a few bits, like current // function name. llvm::Value *CodeGenFunction::BuildBlockLiteralTmp(const BlockExpr *BE) { std::string Name = CurFn->getName(); CodeGenFunction::BlockInfo Info(0, Name.c_str()); CollectBlockDeclRefInfo(BE->getBody(), Info); // Check if the block can be global. // FIXME: This test doesn't work for nested blocks yet. Longer // term, I'd like to just have one code path. We should move // this function into CGM and pass CGF, then we can just check to // see if CGF is 0. if (0 && CanBlockBeGlobal(Info)) return CGM.GetAddrOfGlobalBlock(BE, Name.c_str()); std::vector Elts; llvm::Constant *C; llvm::Value *V; { // C = BuildBlockStructInitlist(); unsigned int flags = BLOCK_HAS_DESCRIPTOR; if (BlockHasCopyDispose) flags |= BLOCK_HAS_COPY_DISPOSE; // __isa C = CGM.getNSConcreteStackBlock(); const llvm::PointerType *PtrToInt8Ty = llvm::PointerType::getUnqual(llvm::Type::Int8Ty); C = llvm::ConstantExpr::getBitCast(C, PtrToInt8Ty); Elts.push_back(C); // __flags const llvm::IntegerType *IntTy = cast( CGM.getTypes().ConvertType(CGM.getContext().IntTy)); C = llvm::ConstantInt::get(IntTy, flags); Elts.push_back(C); // __reserved C = llvm::ConstantInt::get(IntTy, 0); Elts.push_back(C); // __invoke uint64_t subBlockSize, subBlockAlign; llvm::SmallVector subBlockDeclRefDecls; llvm::Function *Fn = CodeGenFunction(CGM).GenerateBlockFunction(BE, Info, subBlockSize, subBlockAlign, subBlockDeclRefDecls); Elts.push_back(Fn); // __descriptor Elts.push_back(BuildDescriptorBlockDecl(subBlockSize)); if (subBlockDeclRefDecls.size() == 0) { // Optimize to being a global block. Elts[0] = CGM.getNSConcreteGlobalBlock(); Elts[1] = llvm::ConstantInt::get(IntTy, flags|BLOCK_IS_GLOBAL); C = llvm::ConstantStruct::get(Elts); char Name[32]; sprintf(Name, "__block_holder_tmp_%d", CGM.getGlobalUniqueCount()); C = new llvm::GlobalVariable(C->getType(), true, llvm::GlobalValue::InternalLinkage, C, Name, &CGM.getModule()); QualType BPT = BE->getType(); C = llvm::ConstantExpr::getBitCast(C, ConvertType(BPT)); return C; } std::vector Types(5+subBlockDeclRefDecls.size()); for (int i=0; i<5; ++i) Types[i] = Elts[i]->getType(); 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()) { uint64_t Align = getContext().getDeclAlignInBytes(BDRE->getDecl()); Types[i+5] = llvm::PointerType::get(BuildByRefType(Ty, Align), 0); } else Types[i+5] = ConvertType(Ty); } llvm::Type *Ty = llvm::StructType::get(Types, true); llvm::AllocaInst *A = CreateTempAlloca(Ty); A->setAlignment(subBlockAlign); V = A; for (unsigned i=0; i<5; ++i) Builder.CreateStore(Elts[i], Builder.CreateStructGEP(V, i, "block.tmp")); for (unsigned i=0; i < subBlockDeclRefDecls.size(); ++i) { // FIXME: Push const down. Expr *E = const_cast(subBlockDeclRefDecls[i]); DeclRefExpr *DR; ValueDecl *VD; DR = dyn_cast(E); // Skip padding. if (DR) continue; BlockDeclRefExpr *BDRE = dyn_cast(E); VD = BDRE->getDecl(); llvm::Value* Addr = Builder.CreateStructGEP(V, i+5, "tmp"); // FIXME: I want a better way to do this. if (LocalDeclMap[VD]) { if (BDRE->isByRef()) { const llvm::Type *Ty = Types[i+5]; llvm::Value *Loc = LocalDeclMap[VD]; Loc = Builder.CreateStructGEP(Loc, 1, "forwarding"); Loc = Builder.CreateLoad(Loc, false); Loc = Builder.CreateBitCast(Loc, Ty); Builder.CreateStore(Loc, Addr); continue; } else E = new (getContext()) DeclRefExpr (cast(VD), VD->getType(), SourceLocation(), false, false); } if (BDRE->isByRef()) { // FIXME: __block in nested literals E = new (getContext()) UnaryOperator(E, UnaryOperator::AddrOf, getContext().getPointerType(E->getType()), SourceLocation()); } RValue r = EmitAnyExpr(E, Addr, false); if (r.isScalar()) { llvm::Value *Loc = r.getScalarVal(); const llvm::Type *Ty = Types[i+5]; if (BDRE->isByRef()) { Loc = Builder.CreateBitCast(Loc, Ty); Loc = Builder.CreateStructGEP(Loc, 1, "forwarding"); Loc = Builder.CreateBitCast(Loc, Ty); } Builder.CreateStore(Loc, Addr); } else if (r.isComplex()) // FIXME: implement ErrorUnsupported(BE, "complex in block literal"); else if (r.isAggregate()) ; // Already created into the destination else assert (0 && "bad block variable"); // FIXME: Ensure that the offset created by the backend for // the struct matches the previously computed offset in BlockDecls. } } QualType BPT = BE->getType(); return Builder.CreateBitCast(V, ConvertType(BPT)); } const llvm::Type *BlockModule::getBlockDescriptorType() { if (BlockDescriptorType) return BlockDescriptorType; const llvm::Type *UnsignedLongTy = getTypes().ConvertType(getContext().UnsignedLongTy); // struct __block_descriptor { // unsigned long reserved; // unsigned long block_size; // }; BlockDescriptorType = llvm::StructType::get(UnsignedLongTy, UnsignedLongTy, NULL); getModule().addTypeName("struct.__block_descriptor", BlockDescriptorType); return BlockDescriptorType; } const llvm::Type *BlockModule::getGenericBlockLiteralType() { if (GenericBlockLiteralType) return GenericBlockLiteralType; const llvm::Type *Int8PtrTy = llvm::PointerType::getUnqual(llvm::Type::Int8Ty); const llvm::Type *BlockDescPtrTy = llvm::PointerType::getUnqual(getBlockDescriptorType()); const llvm::IntegerType *IntTy = cast( getTypes().ConvertType(getContext().IntTy)); // struct __block_literal_generic { // void *__isa; // int __flags; // int __reserved; // void (*__invoke)(void *); // struct __block_descriptor *__descriptor; // }; GenericBlockLiteralType = llvm::StructType::get(Int8PtrTy, IntTy, IntTy, Int8PtrTy, BlockDescPtrTy, NULL); getModule().addTypeName("struct.__block_literal_generic", GenericBlockLiteralType); return GenericBlockLiteralType; } const llvm::Type *BlockModule::getGenericExtendedBlockLiteralType() { if (GenericExtendedBlockLiteralType) return GenericExtendedBlockLiteralType; const llvm::Type *Int8PtrTy = llvm::PointerType::getUnqual(llvm::Type::Int8Ty); const llvm::Type *BlockDescPtrTy = llvm::PointerType::getUnqual(getBlockDescriptorType()); const llvm::IntegerType *IntTy = cast( getTypes().ConvertType(getContext().IntTy)); // struct __block_literal_generic { // void *__isa; // int __flags; // int __reserved; // void (*__invoke)(void *); // struct __block_descriptor *__descriptor; // void *__copy_func_helper_decl; // void *__destroy_func_decl; // }; GenericExtendedBlockLiteralType = llvm::StructType::get(Int8PtrTy, IntTy, IntTy, Int8PtrTy, BlockDescPtrTy, Int8PtrTy, Int8PtrTy, NULL); getModule().addTypeName("struct.__block_literal_extended_generic", GenericExtendedBlockLiteralType); return GenericExtendedBlockLiteralType; } /// getBlockFunctionType - Given a BlockPointerType, will return the /// function type for the block, including the first block literal argument. static QualType getBlockFunctionType(ASTContext &Ctx, const BlockPointerType *BPT) { const FunctionProtoType *FTy = cast(BPT->getPointeeType()); llvm::SmallVector Types; Types.push_back(Ctx.getPointerType(Ctx.VoidTy)); for (FunctionProtoType::arg_type_iterator i = FTy->arg_type_begin(), e = FTy->arg_type_end(); i != e; ++i) Types.push_back(*i); return Ctx.getFunctionType(FTy->getResultType(), &Types[0], Types.size(), FTy->isVariadic(), 0); } RValue CodeGenFunction::EmitBlockCallExpr(const CallExpr* E) { const BlockPointerType *BPT = E->getCallee()->getType()->getAsBlockPointerType(); llvm::Value *Callee = EmitScalarExpr(E->getCallee()); // Get a pointer to the generic block literal. const llvm::Type *BlockLiteralTy = llvm::PointerType::getUnqual(CGM.getGenericBlockLiteralType()); // Bitcast the callee to a block literal. llvm::Value *BlockLiteral = Builder.CreateBitCast(Callee, BlockLiteralTy, "block.literal"); // Get the function pointer from the literal. llvm::Value *FuncPtr = Builder.CreateStructGEP(BlockLiteral, 3, "tmp"); llvm::Value *Func = Builder.CreateLoad(FuncPtr, false, "tmp"); // Cast the function pointer to the right type. const llvm::Type *BlockFTy = ConvertType(getBlockFunctionType(getContext(), BPT)); const llvm::Type *BlockFTyPtr = llvm::PointerType::getUnqual(BlockFTy); Func = Builder.CreateBitCast(Func, BlockFTyPtr); BlockLiteral = Builder.CreateBitCast(BlockLiteral, llvm::PointerType::getUnqual(llvm::Type::Int8Ty), "tmp"); // Add the block literal. QualType VoidPtrTy = getContext().getPointerType(getContext().VoidTy); CallArgList Args; Args.push_back(std::make_pair(RValue::get(BlockLiteral), VoidPtrTy)); // And the rest of the arguments. for (CallExpr::const_arg_iterator i = E->arg_begin(), e = E->arg_end(); i != e; ++i) Args.push_back(std::make_pair(EmitAnyExprToTemp(*i), i->getType())); // And call the block. return EmitCall(CGM.getTypes().getFunctionInfo(E->getType(), Args), Func, Args); } llvm::Value *CodeGenFunction::GetAddrOfBlockDecl(const BlockDeclRefExpr *E) { uint64_t &offset = BlockDecls[E->getDecl()]; const llvm::Type *Ty; Ty = CGM.getTypes().ConvertType(E->getDecl()->getType()); // FIXME: add support for copy/dispose helpers. if (!Enable__block && E->isByRef()) ErrorUnsupported(E, "__block variable in block literal"); else if (E->getType()->isBlockPointerType()) ErrorUnsupported(E, "block pointer in block literal"); else if (E->getDecl()->getAttr() || getContext().isObjCNSObjectType(E->getType())) ErrorUnsupported(E, "__attribute__((NSObject)) variable in block " "literal"); else if (getContext().isObjCObjectPointerType(E->getType())) ErrorUnsupported(E, "Objective-C variable in block literal"); // See if we have already allocated an offset for this variable. if (offset == 0) { // if not, allocate one now. offset = getBlockOffset(E); } llvm::Value *BlockLiteral = LoadBlockStruct(); llvm::Value *V = Builder.CreateGEP(BlockLiteral, llvm::ConstantInt::get(llvm::Type::Int64Ty, offset), "tmp"); if (E->isByRef()) { bool needsCopyDispose = BlockRequiresCopying(E->getType()); uint64_t Align = getContext().getDeclAlignInBytes(E->getDecl()); const llvm::Type *PtrStructTy = llvm::PointerType::get(BuildByRefType(E->getType(), Align), 0); Ty = PtrStructTy; Ty = llvm::PointerType::get(Ty, 0); V = Builder.CreateBitCast(V, Ty); V = Builder.CreateLoad(V, false); V = Builder.CreateStructGEP(V, 1, "forwarding"); V = Builder.CreateLoad(V, false); V = Builder.CreateBitCast(V, PtrStructTy); V = Builder.CreateStructGEP(V, needsCopyDispose*2 + 4, "x"); } else { Ty = llvm::PointerType::get(Ty, 0); V = Builder.CreateBitCast(V, Ty); } return V; } llvm::Constant * BlockModule::GetAddrOfGlobalBlock(const BlockExpr *BE, const char * n) { // Generate the block descriptor. const llvm::Type *UnsignedLongTy = Types.ConvertType(Context.UnsignedLongTy); const llvm::IntegerType *IntTy = cast( getTypes().ConvertType(getContext().IntTy)); llvm::Constant *DescriptorFields[2]; // Reserved DescriptorFields[0] = llvm::Constant::getNullValue(UnsignedLongTy); // Block literal size. For global blocks we just use the size of the generic // block literal struct. uint64_t BlockLiteralSize = TheTargetData.getTypeStoreSizeInBits(getGenericBlockLiteralType()) / 8; DescriptorFields[1] = llvm::ConstantInt::get(UnsignedLongTy,BlockLiteralSize); llvm::Constant *DescriptorStruct = llvm::ConstantStruct::get(&DescriptorFields[0], 2); llvm::GlobalVariable *Descriptor = new llvm::GlobalVariable(DescriptorStruct->getType(), true, llvm::GlobalVariable::InternalLinkage, DescriptorStruct, "__block_descriptor_global", &getModule()); // Generate the constants for the block literal. llvm::Constant *LiteralFields[5]; CodeGenFunction::BlockInfo Info(0, n); uint64_t subBlockSize, subBlockAlign; llvm::SmallVector subBlockDeclRefDecls; llvm::Function *Fn = CodeGenFunction(CGM).GenerateBlockFunction(BE, Info, subBlockSize, subBlockAlign, subBlockDeclRefDecls); assert(subBlockSize == BlockLiteralSize && "no imports allowed for global block"); // isa LiteralFields[0] = getNSConcreteGlobalBlock(); // Flags LiteralFields[1] = llvm::ConstantInt::get(IntTy, BLOCK_IS_GLOBAL | BLOCK_HAS_DESCRIPTOR); // Reserved LiteralFields[2] = llvm::Constant::getNullValue(IntTy); // Function LiteralFields[3] = Fn; // Descriptor LiteralFields[4] = Descriptor; llvm::Constant *BlockLiteralStruct = llvm::ConstantStruct::get(&LiteralFields[0], 5); llvm::GlobalVariable *BlockLiteral = new llvm::GlobalVariable(BlockLiteralStruct->getType(), true, llvm::GlobalVariable::InternalLinkage, BlockLiteralStruct, "__block_literal_global", &getModule()); return BlockLiteral; } llvm::Value *CodeGenFunction::LoadBlockStruct() { return Builder.CreateLoad(LocalDeclMap[getBlockStructDecl()], "self"); } llvm::Function *CodeGenFunction::GenerateBlockFunction(const BlockExpr *BExpr, const BlockInfo& Info, uint64_t &Size, uint64_t &Align, llvm::SmallVector &subBlockDeclRefDecls) { const FunctionProtoType *FTy = cast(BExpr->getFunctionType()); FunctionArgList Args; const BlockDecl *BD = BExpr->getBlockDecl(); // FIXME: This leaks ImplicitParamDecl *SelfDecl = ImplicitParamDecl::Create(getContext(), 0, SourceLocation(), 0, getContext().getPointerType(getContext().VoidTy)); Args.push_back(std::make_pair(SelfDecl, SelfDecl->getType())); BlockStructDecl = SelfDecl; for (BlockDecl::param_iterator i = BD->param_begin(), e = BD->param_end(); i != e; ++i) Args.push_back(std::make_pair(*i, (*i)->getType())); const CGFunctionInfo &FI = CGM.getTypes().getFunctionInfo(FTy->getResultType(), Args); std::string Name = std::string("__") + Info.Name + "_block_invoke_"; CodeGenTypes &Types = CGM.getTypes(); const llvm::FunctionType *LTy = Types.GetFunctionType(FI, FTy->isVariadic()); llvm::Function *Fn = llvm::Function::Create(LTy, llvm::GlobalValue::InternalLinkage, Name, &CGM.getModule()); StartFunction(BD, FTy->getResultType(), Fn, Args, BExpr->getBody()->getLocEnd()); EmitStmt(BExpr->getBody()); FinishFunction(cast(BExpr->getBody())->getRBracLoc()); // The runtime needs a minimum alignment of a void *. uint64_t MinAlign = getContext().getTypeAlign(getContext().VoidPtrTy) / 8; BlockOffset = llvm::RoundUpToAlignment(BlockOffset, MinAlign); Size = BlockOffset; Align = BlockAlign; subBlockDeclRefDecls = BlockDeclRefDecls; return Fn; } uint64_t CodeGenFunction::getBlockOffset(const BlockDeclRefExpr *BDRE) { const ValueDecl *D = dyn_cast(BDRE->getDecl()); uint64_t Size = getContext().getTypeSize(D->getType()) / 8; uint64_t Align = getContext().getDeclAlignInBytes(D); if (BDRE->isByRef()) { Size = getContext().getTypeSize(getContext().VoidPtrTy) / 8; Align = getContext().getTypeAlign(getContext().VoidPtrTy) / 8; } assert ((Align > 0) && "alignment must be 1 byte or more"); uint64_t OldOffset = BlockOffset; // Ensure proper alignment, even if it means we have to have a gap BlockOffset = llvm::RoundUpToAlignment(BlockOffset, Align); BlockAlign = std::max(Align, BlockAlign); uint64_t Pad = BlockOffset - OldOffset; if (Pad) { llvm::ArrayType::get(llvm::Type::Int8Ty, Pad); QualType PadTy = getContext().getConstantArrayType(getContext().CharTy, llvm::APInt(32, Pad), ArrayType::Normal, 0); ValueDecl *PadDecl = VarDecl::Create(getContext(), 0, SourceLocation(), 0, QualType(PadTy), VarDecl::None, SourceLocation()); Expr *E; E = new (getContext()) DeclRefExpr(PadDecl, PadDecl->getType(), SourceLocation(), false, false); BlockDeclRefDecls.push_back(E); } BlockDeclRefDecls.push_back(BDRE); BlockOffset += Size; return BlockOffset-Size; } llvm::Value *CodeGenFunction::BuildCopyHelper(int flag) { const llvm::PointerType *PtrToInt8Ty = llvm::PointerType::getUnqual(llvm::Type::Int8Ty); // FIXME: implement llvm::Value *V = llvm::ConstantInt::get(llvm::Type::Int32Ty, 43); V = Builder.CreateIntToPtr(V, PtrToInt8Ty, "tmp"); V = Builder.CreateBitCast(V, PtrToInt8Ty, "tmp"); return V; } llvm::Value *CodeGenFunction::BuildDestroyHelper(int flag) { const llvm::PointerType *PtrToInt8Ty = llvm::PointerType::getUnqual(llvm::Type::Int8Ty); // FIXME: implement llvm::Value *V = llvm::ConstantInt::get(llvm::Type::Int32Ty, 44); V = Builder.CreateIntToPtr(V, PtrToInt8Ty, "tmp"); V = Builder.CreateBitCast(V, PtrToInt8Ty, "tmp"); return V; }