//===--- 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 "clang/AST/DeclObjC.h"
#include "llvm/Module.h"
#include "llvm/Target/TargetData.h"
#include <algorithm>
using namespace clang;
using namespace CodeGen;
llvm::Constant *CodeGenFunction::
BuildDescriptorBlockDecl(bool BlockHasCopyDispose, uint64_t Size,
const llvm::StructType* Ty,
std::vector<HelperInfo> *NoteForHelper) {
const llvm::Type *UnsignedLongTy
= CGM.getTypes().ConvertType(getContext().UnsignedLongTy);
llvm::Constant *C;
std::vector<llvm::Constant*> 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
Elts.push_back(BuildCopyHelper(Ty, NoteForHelper));
// destroy_func_decl
Elts.push_back(BuildDestroyHelper(Ty, NoteForHelper));
}
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 == 0)
NSConcreteGlobalBlock = CGM.CreateRuntimeVariable(PtrToInt8Ty,
"_NSConcreteGlobalBlock");
return NSConcreteGlobalBlock;
}
llvm::Constant *BlockModule::getNSConcreteStackBlock() {
if (NSConcreteStackBlock == 0)
NSConcreteStackBlock = CGM.CreateRuntimeVariable(PtrToInt8Ty,
"_NSConcreteStackBlock");
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<BlockDeclRefExpr>(S)) {
// FIXME: Handle enums.
if (isa<FunctionDecl>(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<llvm::Constant*> Elts(5);
llvm::Constant *C;
llvm::Value *V;
{
// C = BuildBlockStructInitlist();
unsigned int flags = BLOCK_HAS_DESCRIPTOR;
// We run this first so that we set BlockHasCopyDispose from the entire
// block literal.
// __invoke
uint64_t subBlockSize, subBlockAlign;
llvm::SmallVector<const Expr *,