A few more tweaks to the blocks AST representation:

- BlockDeclRefExprs always store VarDecls
  - BDREs no longer store copy expressions
  - BlockDecls now store a list of captured variables, information about
    how they're captured, and a copy expression if necessary
    
With that in hand, change IR generation to use the captures data in       
blocks instead of walking the block independently.        

Additionally, optimize block layout by emitting fields in descending
alignment order, with a heuristic for filling in words when alignment
of the end of the block header is insufficient for the most aligned
field.

llvm-svn: 125005
This commit is contained in:
John McCall 2011-02-07 10:33:21 +00:00
parent 867cb633b4
commit 351762cda2
34 changed files with 1482 additions and 1202 deletions

View File

@ -855,9 +855,9 @@ public:
void getObjCEncodingForMethodDecl(const ObjCMethodDecl *Decl, std::string &S)
const;
/// getObjCEncodingForBlockDecl - Return the encoded type for this block
/// getObjCEncodingForBlock - Return the encoded type for this block
/// declaration.
void getObjCEncodingForBlock(const BlockExpr *Expr, std::string& S) const;
std::string getObjCEncodingForBlock(const BlockExpr *blockExpr) const;
/// getObjCEncodingForPropertyDecl - Return the encoded type for
/// this method declaration. If non-NULL, Container must be either

View File

@ -2493,6 +2493,46 @@ public:
/// ^{ statement-body } or ^(int arg1, float arg2){ statement-body }
///
class BlockDecl : public Decl, public DeclContext {
public:
/// A class which contains all the information about a particular
/// captured value.
class Capture {
enum {
flag_isByRef = 0x1,
flag_isNested = 0x2
};
/// The variable being captured.
llvm::PointerIntPair<VarDecl*, 2> VariableAndFlags;
/// The copy expression, expressed in terms of a DeclRef (or
/// BlockDeclRef) to the captured variable. Only required if the
/// variable has a C++ class type.
Expr *CopyExpr;
public:
Capture(VarDecl *variable, bool byRef, bool nested, Expr *copy)
: VariableAndFlags(variable,
(byRef ? flag_isByRef : 0) | (nested ? flag_isNested : 0)),
CopyExpr(copy) {}
/// The variable being captured.
VarDecl *getVariable() const { return VariableAndFlags.getPointer(); }
/// Whether this is a "by ref" capture, i.e. a capture of a __block
/// variable.
bool isByRef() const { return VariableAndFlags.getInt() & flag_isByRef; }
/// Whether this is a nested capture, i.e. the variable captured
/// is not from outside the immediately enclosing function/block.
bool isNested() const { return VariableAndFlags.getInt() & flag_isNested; }
bool hasCopyExpr() const { return CopyExpr != 0; }
Expr *getCopyExpr() const { return CopyExpr; }
void setCopyExpr(Expr *e) { CopyExpr = e; }
};
private:
// FIXME: This can be packed into the bitfields in Decl.
bool IsVariadic : 1;
bool CapturesCXXThis : 1;
@ -2505,15 +2545,15 @@ class BlockDecl : public Decl, public DeclContext {
Stmt *Body;
TypeSourceInfo *SignatureAsWritten;
VarDecl **CapturedDecls;
unsigned NumCapturedDecls;
Capture *Captures;
unsigned NumCaptures;
protected:
BlockDecl(DeclContext *DC, SourceLocation CaretLoc)
: Decl(Block, DC, CaretLoc), DeclContext(Block),
IsVariadic(false), CapturesCXXThis(false),
ParamInfo(0), NumParams(0), Body(0),
SignatureAsWritten(0), CapturedDecls(0), NumCapturedDecls(0) {}
SignatureAsWritten(0), Captures(0), NumCaptures(0) {}
public:
static BlockDecl *Create(ASTContext &C, DeclContext *DC, SourceLocation L);
@ -2555,25 +2595,25 @@ public:
/// hasCaptures - True if this block (or its nested blocks) captures
/// anything of local storage from its enclosing scopes.
bool hasCaptures() const { return NumCapturedDecls != 0 || CapturesCXXThis; }
bool hasCaptures() const { return NumCaptures != 0 || CapturesCXXThis; }
unsigned getNumCapturedDecls() const { return NumCapturedDecls; }
/// getNumCaptures - Returns the number of captured variables.
/// Does not include an entry for 'this'.
unsigned getNumCaptures() const { return NumCaptures; }
typedef VarDecl * const *capture_iterator;
typedef VarDecl const * const *capture_const_iterator;
capture_iterator capture_begin() { return CapturedDecls; }
capture_iterator capture_end() { return CapturedDecls + NumCapturedDecls; }
capture_const_iterator capture_begin() const { return CapturedDecls; }
capture_const_iterator capture_end() const {
return CapturedDecls + NumCapturedDecls;
}
typedef const Capture *capture_iterator;
typedef const Capture *capture_const_iterator;
capture_iterator capture_begin() { return Captures; }
capture_iterator capture_end() { return Captures + NumCaptures; }
capture_const_iterator capture_begin() const { return Captures; }
capture_const_iterator capture_end() const { return Captures + NumCaptures; }
bool capturesCXXThis() const { return CapturesCXXThis; }
void setCapturedDecls(ASTContext &Context,
VarDecl * const *begin,
VarDecl * const *end,
bool capturesCXXThis);
void setCaptures(ASTContext &Context,
const Capture *begin,
const Capture *end,
bool capturesCXXThis);
virtual SourceRange getSourceRange() const;

View File

@ -3565,27 +3565,25 @@ public:
virtual child_iterator child_end();
};
/// BlockDeclRefExpr - A reference to a declared variable, function,
/// enum, etc.
/// BlockDeclRefExpr - A reference to a local variable declared in an
/// enclosing scope.
class BlockDeclRefExpr : public Expr {
ValueDecl *D;
VarDecl *D;
SourceLocation Loc;
bool IsByRef : 1;
bool ConstQualAdded : 1;
Stmt *CopyConstructorVal;
public:
BlockDeclRefExpr(ValueDecl *d, QualType t, ExprValueKind VK,
SourceLocation l, bool ByRef, bool constAdded = false,
Stmt *copyConstructorVal = 0);
BlockDeclRefExpr(VarDecl *d, QualType t, ExprValueKind VK,
SourceLocation l, bool ByRef, bool constAdded = false);
// \brief Build an empty reference to a declared variable in a
// block.
explicit BlockDeclRefExpr(EmptyShell Empty)
: Expr(BlockDeclRefExprClass, Empty) { }
ValueDecl *getDecl() { return D; }
const ValueDecl *getDecl() const { return D; }
void setDecl(ValueDecl *VD) { D = VD; }
VarDecl *getDecl() { return D; }
const VarDecl *getDecl() const { return D; }
void setDecl(VarDecl *VD) { D = VD; }
SourceLocation getLocation() const { return Loc; }
void setLocation(SourceLocation L) { Loc = L; }
@ -3598,12 +3596,6 @@ public:
bool isConstQualAdded() const { return ConstQualAdded; }
void setConstQualAdded(bool C) { ConstQualAdded = C; }
const Expr *getCopyConstructorExpr() const
{ return cast_or_null<Expr>(CopyConstructorVal); }
Expr *getCopyConstructorExpr()
{ return cast_or_null<Expr>(CopyConstructorVal); }
void setCopyConstructorExpr(Expr *E) { CopyConstructorVal = E; }
static bool classof(const Stmt *T) {
return T->getStmtClass() == BlockDeclRefExprClass;
}

View File

@ -116,8 +116,11 @@ public:
/// Its return type may be BuiltinType::Dependent.
QualType FunctionType;
/// Captures - The set of variables captured by this block.
llvm::SmallSetVector<VarDecl*, 4> Captures;
/// CaptureMap - A map of captured variables to (index+1) into Captures.
llvm::DenseMap<VarDecl*, unsigned> CaptureMap;
/// Captures - The captured variables.
llvm::SmallVector<BlockDecl::Capture, 4> Captures;
/// CapturesCXXThis - Whether this block captures 'this'.
bool CapturesCXXThis;

View File

@ -3693,10 +3693,11 @@ std::string charUnitsToString(const CharUnits &CU) {
return llvm::itostr(CU.getQuantity());
}
/// getObjCEncodingForBlockDecl - Return the encoded type for this block
/// getObjCEncodingForBlock - Return the encoded type for this block
/// declaration.
void ASTContext::getObjCEncodingForBlock(const BlockExpr *Expr,
std::string& S) const {
std::string ASTContext::getObjCEncodingForBlock(const BlockExpr *Expr) const {
std::string S;
const BlockDecl *Decl = Expr->getBlockDecl();
QualType BlockTy =
Expr->getType()->getAs<BlockPointerType>()->getPointeeType();
@ -3739,6 +3740,8 @@ void ASTContext::getObjCEncodingForBlock(const BlockExpr *Expr,
S += charUnitsToString(ParmOffset);
ParmOffset += getObjCEncodingTypeSize(PType);
}
return S;
}
void ASTContext::getObjCEncodingForFunctionDecl(const FunctionDecl *Decl,

View File

@ -2104,21 +2104,26 @@ void BlockDecl::setParams(ParmVarDecl **NewParamInfo,
}
}
void BlockDecl::setCapturedDecls(ASTContext &Context,
VarDecl * const *begin,
VarDecl * const *end,
bool capturesCXXThis) {
void BlockDecl::setCaptures(ASTContext &Context,
const Capture *begin,
const Capture *end,
bool capturesCXXThis) {
CapturesCXXThis = capturesCXXThis;
if (begin == end) {
NumCapturedDecls = 0;
CapturedDecls = 0;
NumCaptures = 0;
Captures = 0;
return;
}
NumCapturedDecls = end - begin;
CapturedDecls = new (Context) VarDecl*[NumCapturedDecls];
memcpy(CapturedDecls, begin, NumCapturedDecls * sizeof(VarDecl*));
NumCaptures = end - begin;
// Avoid new Capture[] because we don't want to provide a default
// constructor.
size_t allocationSize = NumCaptures * sizeof(Capture);
void *buffer = Context.Allocate(allocationSize, /*alignment*/sizeof(void*));
memcpy(buffer, begin, allocationSize);
Captures = static_cast<Capture*>(buffer);
}
SourceRange BlockDecl::getSourceRange() const {

View File

@ -2961,13 +2961,12 @@ Stmt::child_iterator ObjCMessageExpr::child_end() {
}
// Blocks
BlockDeclRefExpr::BlockDeclRefExpr(ValueDecl *d, QualType t, ExprValueKind VK,
BlockDeclRefExpr::BlockDeclRefExpr(VarDecl *d, QualType t, ExprValueKind VK,
SourceLocation l, bool ByRef,
bool constAdded, Stmt *copyConstructorVal)
bool constAdded)
: Expr(BlockDeclRefExprClass, t, VK, OK_Ordinary, false, false,
d->isParameterPack()),
D(d), Loc(l), IsByRef(ByRef),
ConstQualAdded(constAdded), CopyConstructorVal(copyConstructorVal)
D(d), Loc(l), IsByRef(ByRef), ConstQualAdded(constAdded)
{
bool TypeDependent = false;
bool ValueDependent = false;

View File

@ -100,6 +100,7 @@ namespace {
OS << ":'" << QualType::getAsString(D_split) << "'";
}
}
void DumpDeclRef(Decl *node);
void DumpStmt(const Stmt *Node) {
Indent();
OS << "(" << Node->getStmtClassName()
@ -153,6 +154,7 @@ namespace {
void VisitBinaryOperator(BinaryOperator *Node);
void VisitCompoundAssignOperator(CompoundAssignOperator *Node);
void VisitAddrLabelExpr(AddrLabelExpr *Node);
void VisitBlockExpr(BlockExpr *Node);
// C++
void VisitCXXNamedCastExpr(CXXNamedCastExpr *Node);
@ -362,21 +364,21 @@ void StmtDumper::VisitDeclRefExpr(DeclRefExpr *Node) {
DumpExpr(Node);
OS << " ";
switch (Node->getDecl()->getKind()) {
default: OS << "Decl"; break;
case Decl::Function: OS << "FunctionDecl"; break;
case Decl::Var: OS << "Var"; break;
case Decl::ParmVar: OS << "ParmVar"; break;
case Decl::EnumConstant: OS << "EnumConstant"; break;
case Decl::Typedef: OS << "Typedef"; break;
case Decl::Record: OS << "Record"; break;
case Decl::Enum: OS << "Enum"; break;
case Decl::CXXRecord: OS << "CXXRecord"; break;
case Decl::ObjCInterface: OS << "ObjCInterface"; break;
case Decl::ObjCClass: OS << "ObjCClass"; break;
DumpDeclRef(Node->getDecl());
}
void StmtDumper::DumpDeclRef(Decl *d) {
OS << d->getDeclKindName() << ' ' << (void*) d;
if (NamedDecl *nd = dyn_cast<NamedDecl>(d)) {
OS << " '";
nd->getDeclName().printName(OS);
OS << "'";
}
OS << "='" << Node->getDecl() << "' " << (void*)Node->getDecl();
if (ValueDecl *vd = dyn_cast<ValueDecl>(d)) {
OS << ' '; DumpType(vd->getType());
}
}
void StmtDumper::VisitUnresolvedLookupExpr(UnresolvedLookupExpr *Node) {
@ -474,6 +476,30 @@ void StmtDumper::VisitCompoundAssignOperator(CompoundAssignOperator *Node) {
DumpType(Node->getComputationResultType());
}
void StmtDumper::VisitBlockExpr(BlockExpr *Node) {
DumpExpr(Node);
IndentLevel++;
BlockDecl *block = Node->getBlockDecl();
if (block->capturesCXXThis()) {
OS << '\n'; Indent(); OS << "(capture this)";
}
for (BlockDecl::capture_iterator
i = block->capture_begin(), e = block->capture_end(); i != e; ++i) {
OS << '\n';
Indent();
OS << "(capture ";
if (i->isByRef()) OS << "byref ";
if (i->isNested()) OS << "nested ";
DumpDeclRef(i->getVariable());
if (i->hasCopyExpr()) DumpSubTree(i->getCopyExpr());
OS << ")";
}
IndentLevel--;
DumpSubTree(block->getBody());
}
// GNU extensions.
void StmtDumper::VisitAddrLabelExpr(AddrLabelExpr *Node) {

View File

@ -425,8 +425,6 @@ void StmtProfiler::VisitBlockDeclRefExpr(BlockDeclRefExpr *S) {
VisitDecl(S->getDecl());
ID.AddBoolean(S->isByRef());
ID.AddBoolean(S->isConstQualAdded());
if (S->getCopyConstructorExpr())
Visit(S->getCopyConstructorExpr());
}
static Stmt::StmtClass DecodeOperatorCall(CXXOperatorCallExpr *S,

File diff suppressed because it is too large Load Diff

View File

@ -47,6 +47,7 @@ namespace clang {
namespace CodeGen {
class CodeGenModule;
class CGBlockInfo;
class BlockBase {
public:
@ -100,12 +101,6 @@ public:
Block.GlobalUniqueCount = 0;
PtrToInt8Ty = llvm::Type::getInt8PtrTy(M.getContext());
}
bool BlockRequiresCopying(QualType Ty)
{ return getContext().BlockRequiresCopying(Ty); }
bool BlockRequiresCopying(const BlockDeclRefExpr *E)
{ return E->getCopyConstructorExpr() != 0 ||
getContext().BlockRequiresCopying(E->getType()); }
};
class BlockFunction : public BlockBase {
@ -118,6 +113,9 @@ protected:
public:
CodeGenFunction &CGF;
const CodeGen::CGBlockInfo *BlockInfo;
llvm::Value *BlockPointer;
const llvm::PointerType *PtrToInt8Ty;
struct HelperInfo {
int index;
@ -143,51 +141,8 @@ public:
BlockFunction(CodeGenModule &cgm, CodeGenFunction &cgf, CGBuilderTy &B);
/// BlockOffset - The offset in bytes for the next allocation of an
/// imported block variable.
CharUnits BlockOffset;
/// BlockAlign - Maximal alignment needed for the Block expressed in
/// characters.
CharUnits BlockAlign;
/// getBlockOffset - Allocate a location within the block's storage
/// for a value with the given size and alignment requirements.
CharUnits getBlockOffset(CharUnits Size, CharUnits Align);
/// SynthesizeCopyDisposeHelpers - True iff the block uses copy/dispose.
bool SynthesizeCopyDisposeHelpers;
/// BlockLayout - The layout of the block's storage, represented as
/// a sequence of expressions which require such storage. The
/// expressions can be:
/// - a BlockDeclRefExpr, indicating that the given declaration
/// from an enclosing scope is needed by the block;
/// - a DeclRefExpr, which always wraps an anonymous VarDecl with
/// array type, used to insert padding into the block; or
/// - a CXXThisExpr, indicating that the C++ 'this' value should
/// propagate from the parent to the block.
/// This is a really silly representation.
llvm::SmallVector<const Expr *, 8> BlockLayout;
/// BlockDecls - Offsets for all Decls in BlockDeclRefExprs.
llvm::DenseMap<const Decl*, CharUnits> BlockDecls;
/// BlockCXXThisOffset - The offset of the C++ 'this' value within
/// the block structure.
CharUnits BlockCXXThisOffset;
ImplicitParamDecl *BlockStructDecl;
ImplicitParamDecl *getBlockStructDecl() { return BlockStructDecl; }
llvm::Constant *GenerateCopyHelperFunction(const llvm::StructType *,
std::vector<HelperInfo> *);
llvm::Constant *GenerateDestroyHelperFunction(const llvm::StructType *,
std::vector<HelperInfo> *);
llvm::Constant *BuildCopyHelper(const llvm::StructType *,
std::vector<HelperInfo> *);
llvm::Constant *BuildDestroyHelper(const llvm::StructType *,
std::vector<HelperInfo> *);
llvm::Constant *GenerateCopyHelperFunction(const CGBlockInfo &blockInfo);
llvm::Constant *GenerateDestroyHelperFunction(const CGBlockInfo &blockInfo);
llvm::Constant *GeneratebyrefCopyHelperFunction(const llvm::Type *, int flag,
const VarDecl *BD);
@ -195,18 +150,12 @@ public:
int flag,
const VarDecl *BD);
llvm::Constant *BuildbyrefCopyHelper(const llvm::Type *T, int flag,
llvm::Constant *BuildbyrefCopyHelper(const llvm::Type *T, uint32_t flags,
unsigned Align, const VarDecl *BD);
llvm::Constant *BuildbyrefDestroyHelper(const llvm::Type *T, int flag,
llvm::Constant *BuildbyrefDestroyHelper(const llvm::Type *T, uint32_t flags,
unsigned Align, const VarDecl *BD);
void BuildBlockRelease(llvm::Value *DeclPtr, int flag = BLOCK_FIELD_IS_BYREF);
bool BlockRequiresCopying(QualType Ty)
{ return getContext().BlockRequiresCopying(Ty); }
bool BlockRequiresCopying(const BlockDeclRefExpr *E)
{ return E->getCopyConstructorExpr() != 0 ||
getContext().BlockRequiresCopying(E->getType()); }
void BuildBlockRelease(llvm::Value *DeclPtr, uint32_t flags);
};
} // end namespace CodeGen

View File

@ -33,6 +33,7 @@
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Dwarf.h"
#include "llvm/Support/Path.h"
#include "llvm/Target/TargetData.h"
#include "llvm/Target/TargetMachine.h"
using namespace clang;
using namespace clang::CodeGen;
@ -1685,7 +1686,7 @@ llvm::DIType CGDebugInfo::EmitTypeForVarWithBlocksAttr(const ValueDecl *VD,
EltTys.push_back(CreateMemberType(Unit, FType, "__flags", &FieldOffset));
EltTys.push_back(CreateMemberType(Unit, FType, "__size", &FieldOffset));
bool HasCopyAndDispose = CGM.BlockRequiresCopying(Type);
bool HasCopyAndDispose = CGM.getContext().BlockRequiresCopying(Type);
if (HasCopyAndDispose) {
FType = CGM.getContext().getPointerType(CGM.getContext().VoidTy);
EltTys.push_back(CreateMemberType(Unit, FType, "__copy_helper",
@ -1834,19 +1835,20 @@ void CGDebugInfo::EmitDeclare(const VarDecl *VD, unsigned Tag,
}
/// EmitDeclare - Emit local variable declaration debug info.
void CGDebugInfo::EmitDeclare(const BlockDeclRefExpr *BDRE, unsigned Tag,
void CGDebugInfo::EmitDeclare(const VarDecl *VD, unsigned Tag,
llvm::Value *Storage, CGBuilderTy &Builder,
CodeGenFunction *CGF) {
const ValueDecl *VD = BDRE->getDecl();
const CGBlockInfo &blockInfo) {
assert(!RegionStack.empty() && "Region stack mismatch, stack empty!");
if (Builder.GetInsertBlock() == 0)
return;
bool isByRef = VD->hasAttr<BlocksAttr>();
uint64_t XOffset = 0;
llvm::DIFile Unit = getOrCreateFile(VD->getLocation());
llvm::DIType Ty;
if (VD->hasAttr<BlocksAttr>())
if (isByRef)
Ty = EmitTypeForVarWithBlocksAttr(VD, &XOffset);
else
Ty = getOrCreateType(VD->getType(), Unit);
@ -1855,17 +1857,22 @@ void CGDebugInfo::EmitDeclare(const BlockDeclRefExpr *BDRE, unsigned Tag,
unsigned Line = getLineNumber(VD->getLocation());
unsigned Column = getColumnNumber(VD->getLocation());
CharUnits offset = CGF->BlockDecls[VD];
const llvm::TargetData &target = CGM.getTargetData();
CharUnits offset = CharUnits::fromQuantity(
target.getStructLayout(blockInfo.StructureType)
->getElementOffset(blockInfo.getCapture(VD).getIndex()));
llvm::SmallVector<llvm::Value *, 9> addr;
const llvm::Type *Int64Ty = llvm::Type::getInt64Ty(CGM.getLLVMContext());
addr.push_back(llvm::ConstantInt::get(Int64Ty, llvm::DIFactory::OpDeref));
addr.push_back(llvm::ConstantInt::get(Int64Ty, llvm::DIFactory::OpPlus));
addr.push_back(llvm::ConstantInt::get(Int64Ty, offset.getQuantity()));
if (BDRE->isByRef()) {
if (isByRef) {
addr.push_back(llvm::ConstantInt::get(Int64Ty, llvm::DIFactory::OpDeref));
addr.push_back(llvm::ConstantInt::get(Int64Ty, llvm::DIFactory::OpPlus));
// offset of __forwarding field
offset = CharUnits::fromQuantity(CGF->LLVMPointerWidth/8);
offset = CharUnits::fromQuantity(target.getPointerSize()/8);
addr.push_back(llvm::ConstantInt::get(Int64Ty, offset.getQuantity()));
addr.push_back(llvm::ConstantInt::get(Int64Ty, llvm::DIFactory::OpDeref));
addr.push_back(llvm::ConstantInt::get(Int64Ty, llvm::DIFactory::OpPlus));
@ -1894,9 +1901,10 @@ void CGDebugInfo::EmitDeclareOfAutoVariable(const VarDecl *VD,
}
void CGDebugInfo::EmitDeclareOfBlockDeclRefVariable(
const BlockDeclRefExpr *BDRE, llvm::Value *Storage, CGBuilderTy &Builder,
CodeGenFunction *CGF) {
EmitDeclare(BDRE, llvm::dwarf::DW_TAG_auto_variable, Storage, Builder, CGF);
const VarDecl *variable, llvm::Value *Storage, CGBuilderTy &Builder,
const CGBlockInfo &blockInfo) {
EmitDeclare(variable, llvm::dwarf::DW_TAG_auto_variable, Storage, Builder,
blockInfo);
}
/// EmitDeclareOfArgVariable - Emit call to llvm.dbg.declare for an argument

View File

@ -37,6 +37,7 @@ namespace CodeGen {
class CodeGenModule;
class CodeGenFunction;
class GlobalDecl;
class CGBlockInfo;
/// CGDebugInfo - This class gathers all debug information during compilation
/// and is responsible for emitting to llvm globals or pass directly to
@ -169,10 +170,10 @@ public:
/// EmitDeclareOfBlockDeclRefVariable - Emit call to llvm.dbg.declare for an
/// imported variable declaration in a block.
void EmitDeclareOfBlockDeclRefVariable(const BlockDeclRefExpr *BDRE,
llvm::Value *AI,
void EmitDeclareOfBlockDeclRefVariable(const VarDecl *variable,
llvm::Value *storage,
CGBuilderTy &Builder,
CodeGenFunction *CGF);
const CGBlockInfo &blockInfo);
/// EmitDeclareOfArgVariable - Emit call to llvm.dbg.declare for an argument
/// variable declaration.
@ -195,9 +196,10 @@ private:
void EmitDeclare(const VarDecl *decl, unsigned Tag, llvm::Value *AI,
CGBuilderTy &Builder);
/// EmitDeclare - Emit call to llvm.dbg.declare for a variable declaration.
void EmitDeclare(const BlockDeclRefExpr *BDRE, unsigned Tag, llvm::Value *AI,
CGBuilderTy &Builder, CodeGenFunction *CGF);
/// EmitDeclare - Emit call to llvm.dbg.declare for a variable
/// declaration from an enclosing block.
void EmitDeclare(const VarDecl *decl, unsigned Tag, llvm::Value *AI,
CGBuilderTy &Builder, const CGBlockInfo &blockInfo);
// EmitTypeForVarWithBlocksAttr - Build up structure info for the byref.
// See BuildByRefType.

View File

@ -328,7 +328,7 @@ llvm::Value *CodeGenFunction::BuildBlockByrefAddress(llvm::Value *BaseAddr,
/// T x;
/// } x
///
const llvm::Type *CodeGenFunction::BuildByRefType(const ValueDecl *D) {
const llvm::Type *CodeGenFunction::BuildByRefType(const VarDecl *D) {
std::pair<const llvm::Type *, unsigned> &Info = ByRefValueInfo[D];
if (Info.first)
return Info.first;
@ -353,7 +353,7 @@ const llvm::Type *CodeGenFunction::BuildByRefType(const ValueDecl *D) {
// int32_t __size;
Types.push_back(Int32Ty);
bool HasCopyAndDispose = BlockRequiresCopying(Ty);
bool HasCopyAndDispose = getContext().BlockRequiresCopying(Ty);
if (HasCopyAndDispose) {
/// void *__copy_helper;
Types.push_back(Int8PtrTy);
@ -507,7 +507,7 @@ namespace {
void Emit(CodeGenFunction &CGF, bool IsForEH) {
llvm::Value *V = CGF.Builder.CreateStructGEP(Addr, 1, "forwarding");
V = CGF.Builder.CreateLoad(V);
CGF.BuildBlockRelease(V);
CGF.BuildBlockRelease(V, BlockFunction::BLOCK_FIELD_IS_BYREF);
}
};
}
@ -788,7 +788,6 @@ void CodeGenFunction::EmitAutoVarDecl(const VarDecl &D,
Builder.CreateStore(V, size_field);
if (flags & BLOCK_HAS_COPY_DISPOSE) {
SynthesizeCopyDisposeHelpers = true;
llvm::Value *copy_helper = Builder.CreateStructGEP(DeclPtr, 4);
Builder.CreateStore(BuildbyrefCopyHelper(DeclPtr->getType(), flag,
Align.getQuantity(), &D),

View File

@ -1300,7 +1300,9 @@ LValue CodeGenFunction::EmitPredefinedLValue(const PredefinedExpr *E) {
CurDecl = getContext().getTranslationUnitDecl();
std::string FunctionName =
PredefinedExpr::ComputeName((PredefinedExpr::IdentType)Type, CurDecl);
(isa<BlockDecl>(CurDecl)
? FnName.str()
: PredefinedExpr::ComputeName((PredefinedExpr::IdentType)Type, CurDecl));
llvm::Constant *C =
CGM.GetAddrOfConstantCString(FunctionName, GlobalVarName.c_str());

View File

@ -2532,8 +2532,8 @@ Value *ScalarExprEmitter::VisitVAArgExpr(VAArgExpr *VE) {
return Builder.CreateLoad(ArgPtr);
}
Value *ScalarExprEmitter::VisitBlockExpr(const BlockExpr *BE) {
return CGF.BuildBlockLiteralTmp(BE);
Value *ScalarExprEmitter::VisitBlockExpr(const BlockExpr *block) {
return CGF.EmitBlockLiteral(block);
}
//===----------------------------------------------------------------------===//

View File

@ -216,8 +216,8 @@ public:
virtual llvm::Value *EmitIvarOffset(CodeGen::CodeGenFunction &CGF,
const ObjCInterfaceDecl *Interface,
const ObjCIvarDecl *Ivar);
virtual llvm::Constant *GCBlockLayout(CodeGen::CodeGenFunction &CGF,
const llvm::SmallVectorImpl<const Expr *> &) {
virtual llvm::Constant *BuildGCBlockLayout(CodeGen::CodeGenModule &CGM,
const CGBlockInfo &blockInfo) {
return NULLPtr;
}
};

View File

@ -1000,8 +1000,8 @@ public:
/// forward references will be filled in with empty bodies if no
/// definition is seen. The return value has type ProtocolPtrTy.
virtual llvm::Constant *GetOrEmitProtocolRef(const ObjCProtocolDecl *PD)=0;
virtual llvm::Constant *GCBlockLayout(CodeGen::CodeGenFunction &CGF,
const llvm::SmallVectorImpl<const Expr *> &);
virtual llvm::Constant *BuildGCBlockLayout(CodeGen::CodeGenModule &CGM,
const CGBlockInfo &blockInfo);
};
@ -1667,12 +1667,14 @@ static Qualifiers::GC GetGCAttrTypeForType(ASTContext &Ctx, QualType FQT) {
return Qualifiers::GCNone;
}
llvm::Constant *CGObjCCommonMac::GCBlockLayout(CodeGen::CodeGenFunction &CGF,
const llvm::SmallVectorImpl<const Expr *> &BlockLayout) {
llvm::Constant *NullPtr =
llvm::Constant *CGObjCCommonMac::BuildGCBlockLayout(CodeGenModule &CGM,
const CGBlockInfo &blockInfo) {
llvm::Constant *nullPtr =
llvm::Constant::getNullValue(llvm::Type::getInt8PtrTy(VMContext));
if (CGM.getLangOptions().getGCMode() == LangOptions::NonGC)
return NullPtr;
return nullPtr;
bool hasUnion = false;
SkipIvars.clear();
IvarsInfo.clear();
@ -1682,47 +1684,59 @@ llvm::Constant *CGObjCCommonMac::GCBlockLayout(CodeGen::CodeGenFunction &CGF,
// __isa is the first field in block descriptor and must assume by runtime's
// convention that it is GC'able.
IvarsInfo.push_back(GC_IVAR(0, 1));
for (size_t i = 0; i < BlockLayout.size(); ++i) {
const Expr *E = BlockLayout[i];
const BlockDeclRefExpr *BDRE = dyn_cast<BlockDeclRefExpr>(E);
if (!BDRE)
const BlockDecl *blockDecl = blockInfo.getBlockDecl();
// Calculate the basic layout of the block structure.
const llvm::StructLayout *layout =
CGM.getTargetData().getStructLayout(blockInfo.StructureType);
// Ignore the optional 'this' capture: C++ objects are not assumed
// to be GC'ed.
// Walk the captured variables.
for (BlockDecl::capture_const_iterator ci = blockDecl->capture_begin(),
ce = blockDecl->capture_end(); ci != ce; ++ci) {
const VarDecl *variable = ci->getVariable();
QualType type = variable->getType();
const CGBlockInfo::Capture &capture = blockInfo.getCapture(variable);
// Ignore constant captures.
if (capture.isConstant()) continue;
uint64_t fieldOffset = layout->getElementOffset(capture.getIndex());
// __block variables are passed by their descriptor address.
if (ci->isByRef()) {
IvarsInfo.push_back(GC_IVAR(fieldOffset, /*size in words*/ 1));
continue;
const ValueDecl *VD = BDRE->getDecl();
CharUnits Offset = CGF.BlockDecls[VD];
uint64_t FieldOffset = Offset.getQuantity();
QualType Ty = VD->getType();
assert(!Ty->isArrayType() &&
"Array block variable should have been caught");
if ((Ty->isRecordType() || Ty->isUnionType()) && !BDRE->isByRef()) {
BuildAggrIvarRecordLayout(Ty->getAs<RecordType>(),
FieldOffset,
true,
hasUnion);
}
assert(!type->isArrayType() && "array variable should not be caught");
if (const RecordType *record = type->getAs<RecordType>()) {
BuildAggrIvarRecordLayout(record, fieldOffset, true, hasUnion);
continue;
}
Qualifiers::GC GCAttr = GetGCAttrTypeForType(CGM.getContext(), Ty);
unsigned FieldSize = CGM.getContext().getTypeSize(Ty);
// __block variables are passed by their descriptior address. So, size
// must reflect this.
if (BDRE->isByRef())
FieldSize = WordSizeInBits;
if (GCAttr == Qualifiers::Strong || BDRE->isByRef())
IvarsInfo.push_back(GC_IVAR(FieldOffset,
FieldSize / WordSizeInBits));
Qualifiers::GC GCAttr = GetGCAttrTypeForType(CGM.getContext(), type);
unsigned fieldSize = CGM.getContext().getTypeSize(type);
if (GCAttr == Qualifiers::Strong)
IvarsInfo.push_back(GC_IVAR(fieldOffset,
fieldSize / WordSizeInBits));
else if (GCAttr == Qualifiers::GCNone || GCAttr == Qualifiers::Weak)
SkipIvars.push_back(GC_IVAR(FieldOffset,
FieldSize / ByteSizeInBits));
SkipIvars.push_back(GC_IVAR(fieldOffset,
fieldSize / ByteSizeInBits));
}
if (IvarsInfo.empty())
return NullPtr;
// Sort on byte position in case we encounterred a union nested in
// block variable type's aggregate type.
if (hasUnion && !IvarsInfo.empty())
std::sort(IvarsInfo.begin(), IvarsInfo.end());
if (hasUnion && !SkipIvars.empty())
std::sort(SkipIvars.begin(), SkipIvars.end());
return nullPtr;
// Sort on byte position; captures might not be allocated in order,
// and unions can do funny things.
llvm::array_pod_sort(IvarsInfo.begin(), IvarsInfo.end());
llvm::array_pod_sort(SkipIvars.begin(), SkipIvars.end());
std::string BitMap;
llvm::Constant *C = BuildIvarLayoutBitmap(BitMap);

View File

@ -56,6 +56,7 @@ namespace CodeGen {
namespace CodeGen {
class CodeGenModule;
class CGBlockInfo;
// FIXME: Several methods should be pure virtual but aren't to avoid the
// partially-implemented subclass breaking.
@ -221,9 +222,8 @@ public:
llvm::Value *DestPtr,
llvm::Value *SrcPtr,
llvm::Value *Size) = 0;
virtual llvm::Constant *GCBlockLayout(CodeGen::CodeGenFunction &CGF,
const llvm::SmallVectorImpl<const Expr *> &) = 0;
virtual llvm::Constant *BuildGCBlockLayout(CodeGen::CodeGenModule &CGM,
const CodeGen::CGBlockInfo &blockInfo) = 0;
};
/// Creates an instance of an Objective-C runtime class.

View File

@ -858,7 +858,8 @@ private:
/// LocalDeclMap - This keeps track of the LLVM allocas or globals for local C
/// decls.
llvm::DenseMap<const Decl*, llvm::Value*> LocalDeclMap;
typedef llvm::DenseMap<const Decl*, llvm::Value*> DeclMapTy;
DeclMapTy LocalDeclMap;
/// LabelMap - This keeps track of the LLVM basic block for each C label.
llvm::DenseMap<const LabelStmt*, JumpDest> LabelMap;
@ -979,7 +980,7 @@ public:
// Block Bits
//===--------------------------------------------------------------------===//
llvm::Value *BuildBlockLiteralTmp(const BlockExpr *);
llvm::Value *EmitBlockLiteral(const BlockExpr *);
llvm::Constant *BuildDescriptorBlockDecl(const BlockExpr *,
const CGBlockInfo &Info,
const llvm::StructType *,
@ -987,21 +988,22 @@ public:
std::vector<HelperInfo> *);
llvm::Function *GenerateBlockFunction(GlobalDecl GD,
const BlockExpr *BExpr,
CGBlockInfo &Info,
const CGBlockInfo &Info,
const Decl *OuterFuncDecl,
llvm::Constant *& BlockVarLayout,
llvm::DenseMap<const Decl*, llvm::Value*> ldm);
const DeclMapTy &ldm);
llvm::Value *LoadBlockStruct();
llvm::Value *LoadBlockStruct() {
assert(BlockPointer && "no block pointer set!");
return BlockPointer;
}
void AllocateBlockCXXThisPointer(const CXXThisExpr *E);
void AllocateBlockDecl(const BlockDeclRefExpr *E);
llvm::Value *GetAddrOfBlockDecl(const BlockDeclRefExpr *E) {
return GetAddrOfBlockDecl(E->getDecl(), E->isByRef());
}
llvm::Value *GetAddrOfBlockDecl(const ValueDecl *D, bool ByRef);
const llvm::Type *BuildByRefType(const ValueDecl *D);
llvm::Value *GetAddrOfBlockDecl(const VarDecl *var, bool ByRef);
const llvm::Type *BuildByRefType(const VarDecl *var);
void GenerateCode(GlobalDecl GD, llvm::Function *Fn);
void StartFunction(GlobalDecl GD, QualType RetTy,
@ -2026,32 +2028,68 @@ public:
/// Name - The name of the block, kindof.
const char *Name;
/// DeclRefs - Variables from parent scopes that have been
/// imported into this block.
llvm::SmallVector<const BlockDeclRefExpr *, 8> DeclRefs;
/// The field index of 'this' within the block, if there is one.
unsigned CXXThisIndex;
/// InnerBlocks - This block and the blocks it encloses.
llvm::SmallPtrSet<const DeclContext *, 4> InnerBlocks;
class Capture {
uintptr_t Data;
/// CXXThisRef - Non-null if 'this' was required somewhere, in
/// which case this is that expression.
const CXXThisExpr *CXXThisRef;
public:
bool isIndex() const { return (Data & 1) != 0; }
bool isConstant() const { return !isIndex(); }
unsigned getIndex() const { assert(isIndex()); return Data >> 1; }
llvm::Value *getConstant() const {
assert(isConstant());
return reinterpret_cast<llvm::Value*>(Data);
}
/// NeedsObjCSelf - True if something in this block has an implicit
/// reference to 'self'.
bool NeedsObjCSelf : 1;
/// HasCXXObject - True if block has imported c++ object requiring copy
/// construction in copy helper and destruction in copy dispose helpers.
static Capture makeIndex(unsigned index) {
Capture v;
v.Data = (index << 1) | 1;
return v;
}
static Capture makeConstant(llvm::Value *value) {
Capture v;
v.Data = reinterpret_cast<uintptr_t>(value);
return v;
}
};
/// The mapping of allocated indexes within the block.
llvm::DenseMap<const VarDecl*, Capture> Captures;
/// CanBeGlobal - True if the block can be global, i.e. it has
/// no non-constant captures.
bool CanBeGlobal : 1;
/// True if the block needs a custom copy or dispose function.
bool NeedsCopyDispose : 1;
/// HasCXXObject - True if the block's custom copy/dispose functions
/// need to be run even in GC mode.
bool HasCXXObject : 1;
/// HasWeakBlockVariable - True if block captures a weak __block variable.
bool HasWeakBlockVariable : 1;
/// These are initialized by GenerateBlockFunction.
bool BlockHasCopyDispose : 1;
const llvm::StructType *StructureType;
const BlockExpr *Block;
CharUnits BlockSize;
CharUnits BlockAlign;
llvm::SmallVector<const Expr*, 8> BlockLayout;
CGBlockInfo(const char *Name);
const Capture &getCapture(const VarDecl *var) const {
llvm::DenseMap<const VarDecl*, Capture>::const_iterator
it = Captures.find(var);
assert(it != Captures.end() && "no entry for variable!");
return it->second;
}
const BlockDecl *getBlockDecl() const { return Block->getBlockDecl(); }
const BlockExpr *getBlockExpr() const { return Block; }
CGBlockInfo(const BlockExpr *blockExpr, const char *Name);
};
} // end namespace CodeGen

View File

@ -4562,7 +4562,8 @@ void RewriteObjC::GetBlockDeclRefExprs(Stmt *S) {
else if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(S))
if (HasLocalVariableExternalStorage(DRE->getDecl())) {
BlockDeclRefExpr *BDRE =
new (Context)BlockDeclRefExpr(DRE->getDecl(), DRE->getType(),
new (Context)BlockDeclRefExpr(cast<VarDecl>(DRE->getDecl()),
DRE->getType(),
VK_LValue, DRE->getLocation(), false);
BlockDeclRefs.push_back(BDRE);
}

View File

@ -740,6 +740,9 @@ enum CaptureResult {
/// A capture is required.
CR_Capture,
/// A by-ref capture is required.
CR_CaptureByRef,
/// An error occurred when trying to capture the given variable.
CR_Error
};
@ -749,7 +752,7 @@ enum CaptureResult {
/// \param var - the variable referenced
/// \param DC - the context which we couldn't capture through
static CaptureResult
DiagnoseUncapturableValueReference(Sema &S, SourceLocation loc,
diagnoseUncapturableValueReference(Sema &S, SourceLocation loc,
VarDecl *var, DeclContext *DC) {
switch (S.ExprEvalContexts.back().Context) {
case Sema::Unevaluated:
@ -789,12 +792,31 @@ DiagnoseUncapturableValueReference(Sema &S, SourceLocation loc,
return CR_Error;
}
/// ShouldCaptureValueReference - Determine if a reference to the
/// There is a well-formed capture at a particular scope level;
/// propagate it through all the nested blocks.
static CaptureResult propagateCapture(Sema &S, unsigned validScopeIndex,
const BlockDecl::Capture &capture) {
VarDecl *var = capture.getVariable();
// Update all the inner blocks with the capture information.
for (unsigned i = validScopeIndex + 1, e = S.FunctionScopes.size();
i != e; ++i) {
BlockScopeInfo *innerBlock = cast<BlockScopeInfo>(S.FunctionScopes[i]);
innerBlock->Captures.push_back(
BlockDecl::Capture(capture.getVariable(), capture.isByRef(),
/*nested*/ true, capture.getCopyExpr()));
innerBlock->CaptureMap[var] = innerBlock->Captures.size(); // +1
}
return capture.isByRef() ? CR_CaptureByRef : CR_Capture;
}
/// shouldCaptureValueReference - Determine if a reference to the
/// given value in the current context requires a variable capture.
///
/// This also keeps the captures set in the BlockScopeInfo records
/// up-to-date.
static CaptureResult ShouldCaptureValueReference(Sema &S, SourceLocation loc,
static CaptureResult shouldCaptureValueReference(Sema &S, SourceLocation loc,
ValueDecl *value) {
// Only variables ever require capture.
VarDecl *var = dyn_cast<VarDecl>(value);
@ -811,27 +833,118 @@ static CaptureResult ShouldCaptureValueReference(Sema &S, SourceLocation loc,
// Otherwise, we need to capture.
unsigned functionScopesIndex = S.FunctionScopes.size() - 1;
do {
// Only blocks (and eventually C++0x closures) can capture; other
// scopes don't work.
if (!isa<BlockDecl>(DC))
return DiagnoseUncapturableValueReference(S, loc, var, DC);
return diagnoseUncapturableValueReference(S, loc, var, DC);
BlockScopeInfo *blockScope =
cast<BlockScopeInfo>(S.FunctionScopes[functionScopesIndex]);
assert(blockScope->TheDecl == static_cast<BlockDecl*>(DC));
// Try to capture it in this block. If we've already captured at
// this level, we're done.
if (!blockScope->Captures.insert(var))
return CR_Capture;
// Check whether we've already captured it in this block. If so,
// we're done.
if (unsigned indexPlus1 = blockScope->CaptureMap[var])
return propagateCapture(S, functionScopesIndex,
blockScope->Captures[indexPlus1 - 1]);
functionScopesIndex--;
DC = cast<BlockDecl>(DC)->getDeclContext();
} while (var->getDeclContext() != DC);
return CR_Capture;
// Okay, we descended all the way to the block that defines the variable.
// Actually try to capture it.
QualType type = var->getType();
// Prohibit variably-modified types.
if (type->isVariablyModifiedType()) {
S.Diag(loc, diag::err_ref_vm_type);
S.Diag(var->getLocation(), diag::note_declared_at);
return CR_Error;
}
// Prohibit arrays, even in __block variables, but not references to
// them.
if (type->isArrayType()) {
S.Diag(loc, diag::err_ref_array_type);
S.Diag(var->getLocation(), diag::note_declared_at);
return CR_Error;
}
S.MarkDeclarationReferenced(loc, var);
// The BlocksAttr indicates the variable is bound by-reference.
bool byRef = var->hasAttr<BlocksAttr>();
// Build a copy expression.
Expr *copyExpr = 0;
if (!byRef && S.getLangOptions().CPlusPlus &&
!type->isDependentType() && type->isStructureOrClassType()) {
// According to the blocks spec, the capture of a variable from
// the stack requires a const copy constructor. This is not true
// of the copy/move done to move a __block variable to the heap.
type.addConst();
Expr *declRef = new (S.Context) DeclRefExpr(var, type, VK_LValue, loc);
ExprResult result =
S.PerformCopyInitialization(
InitializedEntity::InitializeBlock(var->getLocation(),
type, false),
loc, S.Owned(declRef));
// Build a full-expression copy expression if initialization
// succeeded and used a non-trivial constructor. Recover from
// errors by pretending that the copy isn't necessary.
if (!result.isInvalid() &&
!cast<CXXConstructExpr>(result.get())->getConstructor()->isTrivial()) {
result = S.MaybeCreateExprWithCleanups(result);
copyExpr = result.take();
}
}
// We're currently at the declarer; go back to the closure.
functionScopesIndex++;
BlockScopeInfo *blockScope =
cast<BlockScopeInfo>(S.FunctionScopes[functionScopesIndex]);
// Build a valid capture in this scope.
blockScope->Captures.push_back(
BlockDecl::Capture(var, byRef, /*nested*/ false, copyExpr));
blockScope->CaptureMap[var] = blockScope->Captures.size(); // +1
// Propagate that to inner captures if necessary.
return propagateCapture(S, functionScopesIndex,
blockScope->Captures.back());
}
static ExprResult BuildBlockDeclRefExpr(Sema &S, ValueDecl *vd,
const DeclarationNameInfo &NameInfo,
bool byRef) {
assert(isa<VarDecl>(vd) && "capturing non-variable");
VarDecl *var = cast<VarDecl>(vd);
assert(var->hasLocalStorage() && "capturing non-local");
assert(byRef == var->hasAttr<BlocksAttr>() && "byref set wrong");
QualType exprType = var->getType().getNonReferenceType();
BlockDeclRefExpr *BDRE;
if (!byRef) {
// The variable will be bound by copy; make it const within the
// closure, but record that this was done in the expression.
bool constAdded = !exprType.isConstQualified();
exprType.addConst();
BDRE = new (S.Context) BlockDeclRefExpr(var, exprType, VK_LValue,
NameInfo.getLoc(), false,
constAdded);
} else {
BDRE = new (S.Context) BlockDeclRefExpr(var, exprType, VK_LValue,
NameInfo.getLoc(), true);
}
return S.Owned(BDRE);
}
ExprResult
@ -2227,7 +2340,6 @@ static ExprValueKind getValueKindForDecl(ASTContext &Context,
return Expr::getValueKindForType(D->getType());
}
/// \brief Complete semantic analysis for a reference to the given declaration.
ExprResult
Sema::BuildDeclarationNameExpr(const CXXScopeSpec &SS,
@ -2278,8 +2390,6 @@ Sema::BuildDeclarationNameExpr(const CXXScopeSpec &SS,
return BuildAnonymousStructUnionMemberReference(SS, NameInfo.getLoc(),
indirectField);
ExprValueKind VK = getValueKindForDecl(Context, VD);
// If the identifier reference is inside a block, and it refers to a value
// that is outside the block, create a BlockDeclRefExpr instead of a
// DeclRefExpr. This ensures the value is treated as a copy-in snapshot when
@ -2288,74 +2398,30 @@ Sema::BuildDeclarationNameExpr(const CXXScopeSpec &SS,
// We do not do this for things like enum constants, global variables, etc,
// as they do not get snapshotted.
//
switch (ShouldCaptureValueReference(*this, NameInfo.getLoc(), VD)) {
switch (shouldCaptureValueReference(*this, NameInfo.getLoc(), VD)) {
case CR_Error:
return ExprError();
case CR_NoCapture:
case CR_NoCapture: {
ExprValueKind VK = getValueKindForDecl(Context, VD);
// If this reference is not in a block or if the referenced
// variable is within the block, create a normal DeclRefExpr.
return BuildDeclRefExpr(VD, VD->getType().getNonReferenceType(), VK,
NameInfo, &SS);
}
case CR_Capture:
break;
assert(!SS.isSet() && "referenced local variable with scope specifier?");
return BuildBlockDeclRefExpr(*this, VD, NameInfo, /*byref*/ false);
case CR_CaptureByRef:
assert(!SS.isSet() && "referenced local variable with scope specifier?");
return BuildBlockDeclRefExpr(*this, VD, NameInfo, /*byref*/ true);
}
// If we got here, we need to capture.
if (VD->getType().getTypePtr()->isVariablyModifiedType()) {
Diag(Loc, diag::err_ref_vm_type);
Diag(D->getLocation(), diag::note_declared_at);
return ExprError();
}
if (VD->getType()->isArrayType()) {
Diag(Loc, diag::err_ref_array_type);
Diag(D->getLocation(), diag::note_declared_at);
return ExprError();
}
MarkDeclarationReferenced(Loc, VD);
QualType ExprTy = VD->getType().getNonReferenceType();
// The BlocksAttr indicates the variable is bound by-reference.
bool byrefVar = (VD->getAttr<BlocksAttr>() != 0);
QualType T = VD->getType();
BlockDeclRefExpr *BDRE;
if (!byrefVar) {
// This is to record that a 'const' was actually synthesize and added.
bool constAdded = !ExprTy.isConstQualified();
// Variable will be bound by-copy, make it const within the closure.
ExprTy.addConst();
BDRE = new (Context) BlockDeclRefExpr(VD, ExprTy, VK,
Loc, false, constAdded);
}
else
BDRE = new (Context) BlockDeclRefExpr(VD, ExprTy, VK, Loc, true);
if (getLangOptions().CPlusPlus) {
if (!T->isDependentType() && !T->isReferenceType()) {
Expr *E = new (Context)
DeclRefExpr(const_cast<ValueDecl*>(BDRE->getDecl()), T,
VK, SourceLocation());
if (T->isStructureOrClassType()) {
ExprResult Res = PerformCopyInitialization(
InitializedEntity::InitializeBlock(VD->getLocation(),
T, false),
SourceLocation(),
Owned(E));
if (!Res.isInvalid()) {
Res = MaybeCreateExprWithCleanups(Res);
Expr *Init = Res.takeAs<Expr>();
BDRE->setCopyConstructorExpr(Init);
}
}
}
}
return Owned(BDRE);
llvm_unreachable("unknown capture result");
return ExprError();
}
ExprResult Sema::ActOnPredefinedExpr(SourceLocation Loc,
@ -8558,10 +8624,8 @@ ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc,
QualType BlockTy;
// Set the captured variables on the block.
BSI->TheDecl->setCapturedDecls(Context,
BSI->Captures.begin(),
BSI->Captures.end(),
BSI->CapturesCXXThis);
BSI->TheDecl->setCaptures(Context, BSI->Captures.begin(), BSI->Captures.end(),
BSI->CapturesCXXThis);
// If the user wrote a function type in some form, try to use that.
if (!BSI->FunctionType.isNull()) {

View File

@ -220,9 +220,16 @@ ObjCMethodDecl *Sema::tryCaptureObjCSelf() {
// Mark that we're closing on 'this' in all the block scopes, if applicable.
for (unsigned idx = FunctionScopes.size() - 1;
isa<BlockScopeInfo>(FunctionScopes[idx]);
--idx)
if (!cast<BlockScopeInfo>(FunctionScopes[idx])->Captures.insert(self))
break;
--idx) {
BlockScopeInfo *blockScope = cast<BlockScopeInfo>(FunctionScopes[idx]);
unsigned &captureIndex = blockScope->CaptureMap[self];
if (captureIndex) break;
bool nested = isa<BlockScopeInfo>(FunctionScopes[idx-1]);
blockScope->Captures.push_back(
BlockDecl::Capture(self, /*byref*/ false, nested, /*copy*/ 0));
captureIndex = blockScope->Captures.size(); // +1
}
return method;
}

View File

@ -7206,7 +7206,7 @@ TreeTransform<Derived>::TransformBlockExpr(BlockExpr *E) {
for (BlockDecl::capture_iterator i = oldBlock->capture_begin(),
e = oldBlock->capture_end(); i != e; ++i) {
VarDecl *oldCapture = *i;
VarDecl *oldCapture = i->getVariable();
// Ignore parameter packs.
if (isa<ParmVarDecl>(oldCapture) &&
@ -7216,7 +7216,7 @@ TreeTransform<Derived>::TransformBlockExpr(BlockExpr *E) {
VarDecl *newCapture =
cast<VarDecl>(getDerived().TransformDecl(E->getCaretLocation(),
oldCapture));
assert(blockScope->Captures.count(newCapture));
assert(blockScope->CaptureMap.count(newCapture));
}
#endif

View File

@ -698,13 +698,20 @@ void ASTDeclReader::VisitBlockDecl(BlockDecl *BD) {
BD->setParams(Params.data(), NumParams);
bool capturesCXXThis = Record[Idx++];
unsigned numCapturedDecls = Record[Idx++];
llvm::SmallVector<VarDecl*, 16> capturedDecls;
capturedDecls.reserve(numCapturedDecls);
for (unsigned i = 0; i != numCapturedDecls; ++i)
capturedDecls.push_back(cast<VarDecl>(Reader.GetDecl(Record[Idx++])));
BD->setCapturedDecls(*Reader.getContext(), capturedDecls.begin(),
capturedDecls.end(), capturesCXXThis);
unsigned numCaptures = Record[Idx++];
llvm::SmallVector<BlockDecl::Capture, 16> captures;
captures.reserve(numCaptures);
for (unsigned i = 0; i != numCaptures; ++i) {
VarDecl *decl = cast<VarDecl>(Reader.GetDecl(Record[Idx++]));
unsigned flags = Record[Idx++];
bool byRef = (flags & 1);
bool nested = (flags & 2);
Expr *copyExpr = ((flags & 4) ? Reader.ReadExpr(F) : 0);
captures.push_back(BlockDecl::Capture(decl, byRef, nested, copyExpr));
}
BD->setCaptures(*Reader.getContext(), captures.begin(),
captures.end(), capturesCXXThis);
}
void ASTDeclReader::VisitLinkageSpecDecl(LinkageSpecDecl *D) {

View File

@ -798,11 +798,10 @@ void ASTStmtReader::VisitBlockExpr(BlockExpr *E) {
void ASTStmtReader::VisitBlockDeclRefExpr(BlockDeclRefExpr *E) {
VisitExpr(E);
E->setDecl(cast<ValueDecl>(Reader.GetDecl(Record[Idx++])));
E->setDecl(cast<VarDecl>(Reader.GetDecl(Record[Idx++])));
E->setLocation(ReadSourceLocation(Record, Idx));
E->setByRef(Record[Idx++]);
E->setConstQualAdded(Record[Idx++]);
E->setCopyConstructorExpr(Reader.ReadSubExpr());
}
//===----------------------------------------------------------------------===//

View File

@ -624,10 +624,20 @@ void ASTDeclWriter::VisitBlockDecl(BlockDecl *D) {
P != PEnd; ++P)
Writer.AddDeclRef(*P, Record);
Record.push_back(D->capturesCXXThis());
Record.push_back(D->getNumCapturedDecls());
Record.push_back(D->getNumCaptures());
for (BlockDecl::capture_iterator
i = D->capture_begin(), e = D->capture_end(); i != e; ++i)
Writer.AddDeclRef(*i, Record);
i = D->capture_begin(), e = D->capture_end(); i != e; ++i) {
const BlockDecl::Capture &capture = *i;
Writer.AddDeclRef(capture.getVariable(), Record);
unsigned flags = 0;
if (capture.isByRef()) flags |= 1;
if (capture.isNested()) flags |= 2;
if (capture.hasCopyExpr()) flags |= 4;
Record.push_back(flags);
if (capture.hasCopyExpr()) Writer.AddStmt(capture.getCopyExpr());
}
Code = serialization::DECL_BLOCK;
}

View File

@ -769,7 +769,6 @@ void ASTStmtWriter::VisitBlockDeclRefExpr(BlockDeclRefExpr *E) {
Writer.AddSourceLocation(E->getLocation(), Record);
Record.push_back(E->isByRef());
Record.push_back(E->isConstQualAdded());
Writer.AddStmt(E->getCopyConstructorExpr());
Code = serialization::EXPR_BLOCK_DECL_REF;
}

View File

@ -7,16 +7,16 @@ struct C : A, B { };
// CHECK: casting_away_constness
void casting_away_constness(const B &b, const C &c, const B *bp, const C *cp) {
// CHECK: DerivedToBase (B)
// CHECK: DeclRefExpr {{.*}} ParmVar='c'
// CHECK: DeclRefExpr {{.*}} ParmVar {{.*}} 'c'
(void)(B&)c;
// CHECK: BaseToDerived (B)
// CHECK: DeclRefExpr {{.*}} ParmVar='b'
// CHECK: DeclRefExpr {{.*}} ParmVar {{.*}} 'b'
(void)(C&)b;
// CHECK: DerivedToBase (B)
// CHECK: DeclRefExpr {{.*}} ParmVar='cp'
// CHECK: DeclRefExpr {{.*}} ParmVar {{.*}} 'cp'
(void)(B*)cp;
// CHECK: BaseToDerived (B)
// CHECK: DeclRefExpr {{.*}} ParmVar='bp'
// CHECK: DeclRefExpr {{.*}} ParmVar {{.*}} 'bp'
(void)(C*)bp;
// CHECK: ReturnStmt
return;

View File

@ -1,7 +1,7 @@
// RUN: %clang_cc1 %s -emit-llvm -o %t -fblocks
// RUN: grep "_Block_object_dispose" %t | count 17
// RUN: grep "__copy_helper_block_" %t | count 16
// RUN: grep "__destroy_helper_block_" %t | count 16
// RUN: grep "__copy_helper_block_" %t | count 14
// RUN: grep "__destroy_helper_block_" %t | count 14
// RUN: grep "__Block_byref_object_copy_" %t | count 2
// RUN: grep "__Block_byref_object_dispose_" %t | count 2
// RUN: grep "i32 135)" %t | count 2
@ -14,7 +14,7 @@ void test1() {
int b=2;
a=1;
printf("a is %d, b is %d\n", a, b);
^{ a = 10; printf("a is %d, b is %d\n", a, b); }();
^{ a = 10; printf("a is %d, b is %d\n", a, b); }(); // needs copy/dispose
printf("a is %d, b is %d\n", a, b);
a = 1;
printf("a is %d, b is %d\n", a, b);
@ -24,8 +24,8 @@ void test2() {
__block int a;
a=1;
printf("a is %d\n", a);
^{
^{
^{ // needs copy/dispose
^{ // needs copy/dispose
a = 10;
}();
}();
@ -37,13 +37,13 @@ void test2() {
void test3() {
__block int k;
__block int (^j)(int);
^{j=0; k=0;}();
^{j=0; k=0;}(); // needs copy/dispose
}
int test4() {
extern int g;
static int i = 1;
^(int j){ i = j; g = 0; }(0);
^(int j){ i = j; g = 0; }(0); // does not need copy/dispose
return i + g;
}
@ -51,19 +51,19 @@ int g;
void test5() {
__block struct { int i; } i;
^{ (void)i; }();
^{ (void)i; }(); // needs copy/dispose
}
void test6() {
__block int i;
^{ i=1; }();
^{}();
^{ i=1; }(); // needs copy/dispose
^{}(); // does not need copy/dispose
}
void test7() {
^{
^{ // does not need copy/dispose
__block int i;
^{ i = 1; }();
^{ i = 1; }(); // needs copy/dispose
}();
}

View File

@ -6,9 +6,11 @@
// X64: @.str1 = private unnamed_addr constant [12 x i8] c"i16@?0c8f12\00"
// X64: store i32 1073741824, i32*
// X32: @.str = private unnamed_addr constant [6 x i8] c"v4@?0\00"
// X32: @__block_literal_global = internal constant %1 { i8** @_NSConcreteGlobalBlock, i32 1342177280,
// X32: @.str1 = private unnamed_addr constant [11 x i8] c"i12@?0c4f8\00"
// X32: [[STR1:@.*]] = private unnamed_addr constant [6 x i8] c"v4@?0\00"
// X32: @__block_descriptor_tmp = internal constant [[FULL_DESCRIPTOR_T:%.*]] { i32 0, i32 20, i8* getelementptr inbounds ([6 x i8]* [[STR1]], i32 0, i32 0), i8* null }
// X32: @__block_literal_global = internal constant [[GLOBAL_LITERAL_T:%.*]] { i8** @_NSConcreteGlobalBlock, i32 1342177280, i32 0, i8* bitcast (void (i8*)* @__block_global_{{.*}} to i8*), [[DESCRIPTOR_T:%.*]]* bitcast ([[FULL_DESCRIPTOR_T]]* @__block_descriptor_tmp to {{%.*}}*) }
// X32: [[STR2:@.*]] = private unnamed_addr constant [11 x i8] c"i12@?0c4f8\00"
// X32: @__block_descriptor_tmp{{.*}} = internal constant [[FULL_DESCRIPTOR_T]] { i32 0, i32 24, i8* getelementptr inbounds ([11 x i8]* [[STR2]], i32 0, i32 0), i8* null }
// X32: store i32 1073741824, i32*
// rdar://7635294

View File

@ -1,7 +1,7 @@
// RUN: %clang_cc1 -fblocks -triple x86_64-apple-darwin9 %s -emit-llvm -o - | FileCheck %s -check-prefix=X64
// RUN: %clang_cc1 -fblocks -triple i686-apple-darwin9 %s -emit-llvm -o - | FileCheck %s -check-prefix=X32
// X64: internal constant %2 { i8** @_NSConcreteGlobalBlock, i32 1879048192
// X64: internal constant {{%.*}} { i8** @_NSConcreteGlobalBlock, i32 1879048192
// X64: store i32 1610612736, i32* %want
// X32: @_NSConcreteGlobalBlock, i32 1879048192, i32 0,

View File

@ -1,4 +1,4 @@
// RUN: %clang_cc1 -fblocks -fobjc-gc -triple x86_64-apple-darwin -O0 -S %s -o %t-64.s
// RUN: %clang_cc1 -fblocks -fobjc-gc -triple x86_64-apple-darwin -O0 -emit-llvm %s -o %t-64.s
// RUN: FileCheck -check-prefix LP64 --input-file=%t-64.s %s
struct S {
@ -16,19 +16,38 @@ __weak id wid;
void x(id y) {}
void y(int a) {}
extern id opaque_id();
void f() {
__block int byref_int = 0;
char ch = 'a';
char ch1 = 'b';
char ch2 = 'c';
short sh = 2;
const id bar = (id)0;
const id bar = (id) opaque_id();
id baz = 0;
__strong void *strong_void_sta;
__block id byref_bab = (id)0;
__block void *bl_var1;
int i; double dob;
// The patterns here are a sequence of bytes, each saying first how
// many sizeof(void*) chunks to skip (high nibble) and then how many
// to scan (low nibble). A zero byte says that we've reached the end
// of the pattern.
//
// All of these patterns start with 01 3x because the block header on
// LP64 consists of an isa pointer (which we're supposed to scan for
// some reason) followed by three words (2 ints, a function pointer,
// and a descriptor pointer).
// FIXME: do these really have to be named L_OBJC_CLASS_NAME_xxx?
// FIXME: sequences should never end in x0 00 instead of just 00
// Test 1
// byref int, short, char, char, char, id, id, strong void*, byref id
// 01 35 10 00
// CHECK-LP64: @"\01L_OBJC_CLASS_NAME_{{.*}}" = internal global [4 x i8] c"\015\10\00"
void (^b)() = ^{
byref_int = sh + ch+ch1+ch2 ;
x(bar);
@ -39,6 +58,9 @@ void f() {
b();
// Test 2
// byref int, short, char, char, char, id, id, strong void*, byref void*, byref id
// 01 36 10 00
// CHECK-LP64: @"\01L_OBJC_CLASS_NAME_{{.*}}" = internal global [4 x i8] c"\016\10\00"
void (^c)() = ^{
byref_int = sh + ch+ch1+ch2 ;
x(bar);
@ -51,7 +73,11 @@ void f() {
c();
// Test 3
void (^d)() = ^{
// byref int, short, char, char, char, id, id, byref void*, int, double, byref id
// 01 34 11 30 00
// FIXME: we'd get a better format here if we sorted by scannability, not just alignment
// CHECK-LP64: @"\01L_OBJC_CLASS_NAME_{{.*}}" = internal global [5 x i8] c"\014\11 \00"
void (^d)() = ^{
byref_int = sh + ch+ch1+ch2 ;
x(bar);
x(baz);
@ -62,7 +88,10 @@ void (^d)() = ^{
};
d();
// Test4
// Test 4
// struct S (int, id, int, id, int, id)
// 01 41 11 11
// CHECK-LP64: @"\01L_OBJC_CLASS_NAME_{{.*}}" = internal global [5 x i8] c"\01A\11\11\00"
struct S s2;
void (^e)() = ^{
x(s2.o1);
@ -72,7 +101,7 @@ void (^d)() = ^{
// Test 5 (unions/structs and their nesting):
void Test5() {
struct S5 {
struct S5 {
int i1;
id o1;
struct V {
@ -86,23 +115,26 @@ struct S5 {
int i3;
id o3;
}ui;
};
};
union U {
union U {
void * i1;
id o1;
int i3;
id o3;
}ui;
}ui;
struct S5 s2;
union U u2;
void (^c)() = ^{
struct S5 s2;
union U u2;
// struct s2 (int, id, int, id, int, id?), union u2 (id?)
// 01 41 11 12 70 00
// CHECK-LP64: @"\01L_OBJC_CLASS_NAME_{{.*}}" = internal global [6 x i8] c"\01A\11\12p\00"
void (^c)() = ^{
x(s2.ui.o1);
x(u2.o1);
};
c();
};
c();
}
// rdar: //8417746
@ -111,6 +143,10 @@ void notifyBlock(id dependentBlock) {
id singleObservationToken;
id token;
void (^b)();
// id, id, void(^)()
// 01 33 00
// CHECK-LP64: @"\01L_OBJC_CLASS_NAME_{{.*}}" = internal global [3 x i8] c"\013\00"
void (^wrapperBlock)() = ^() {
CFRelease(singleObservationToken);
CFRelease(singleObservationToken);
@ -122,28 +158,9 @@ void notifyBlock(id dependentBlock) {
}
void test_empty_block() {
void (^wrapperBlock)() = ^() {
};
// 01 00
// CHECK-LP64: @"\01L_OBJC_CLASS_NAME_{{.*}}" = internal global [2 x i8] c"\01\00"
void (^wrapperBlock)() = ^() {
};
wrapperBlock();
}
// CHECK-LP64: L_OBJC_CLASS_NAME_:
// CHECK-LP64-NEXT: .asciz "\0011\024"
// CHECK-LP64: L_OBJC_CLASS_NAME_1:
// CHECK-LP64-NEXT: .asciz "\0011\025"
// CHECK-LP64: L_OBJC_CLASS_NAME_6:
// CHECK-LP64-NEXT: .asciz "\0011\023!"
// CHECK-LP64: L_OBJC_CLASS_NAME_11:
// CHECK-LP64-NEXT: .asciz "\001A\021\021"
// CHECK-LP64: L_OBJC_CLASS_NAME_14:
// CHECK-LP64-NEXT: .asciz "\001A\021\022p"
// CHECK-LP64: L_OBJC_CLASS_NAME_16:
// CHECK-LP64-NEXT: .asciz "\0013"
// CHECK-LP64: L_OBJC_CLASS_NAME_20:
// CHECK-LP64-NEXT: .asciz "\001"

View File

@ -1,6 +1,8 @@
// RUN: %clang_cc1 -x objective-c++ -fblocks -fobjc-gc -triple x86_64-apple-darwin -O0 -S %s -o %t-64.s
// RUN: FileCheck -check-prefix LP64 --input-file=%t-64.s %s
// RUN: %clang_cc1 -x objective-c++ -fblocks -fobjc-gc -triple x86_64-apple-darwin -emit-llvm %s -o %t-64.ll
// RUN: FileCheck -check-prefix LP64 --input-file=%t-64.ll %s
// See commentary in test/CodeGenObjC/block-var-layout.m, from which
// this is largely cloned.
struct S {
int i1;
@ -17,19 +19,25 @@ __weak id wid;
void x(id y) {}
void y(int a) {}
extern id opaque_id();
void f() {
__block int byref_int = 0;
char ch = 'a';
char ch1 = 'b';
char ch2 = 'c';
short sh = 2;
const id bar = (id)0;
const id bar = (id) opaque_id();
id baz = 0;
__strong void *strong_void_sta;
__block id byref_bab = (id)0;
__block void *bl_var1;
int i; double dob;
// Test 1
// byref int, short, char, char, char, id, id, strong void*, byref id
// 01 35 10 00
// CHECK-LP64: @"\01L_OBJC_CLASS_NAME_{{.*}}" = internal global [4 x i8] c"\015\10\00"
void (^b)() = ^{
byref_int = sh + ch+ch1+ch2 ;
x(bar);
@ -40,6 +48,9 @@ void f() {
b();
// Test 2
// byref int, short, char, char, char, id, id, strong void*, byref void*, byref id
// 01 36 10 00
// CHECK-LP64: @"\01L_OBJC_CLASS_NAME_{{.*}}" = internal global [4 x i8] c"\016\10\00"
void (^c)() = ^{
byref_int = sh + ch+ch1+ch2 ;
x(bar);
@ -52,6 +63,10 @@ void f() {
c();
// Test 3
// byref int, short, char, char, char, id, id, byref void*, int, double, byref id
// 01 34 11 30 00
// FIXME: we'd get a better format here if we sorted by scannability, not just alignment
// CHECK-LP64: @"\01L_OBJC_CLASS_NAME_{{.*}}" = internal global [5 x i8] c"\014\11 \00"
void (^d)() = ^{
byref_int = sh + ch+ch1+ch2 ;
x(bar);
@ -64,6 +79,9 @@ void (^d)() = ^{
d();
// Test4
// struct S (int, id, int, id, int, id)
// 01 41 11 11
// CHECK-LP64: @"\01L_OBJC_CLASS_NAME_{{.*}}" = internal global [5 x i8] c"\01A\11\11\00"
struct S s2;
void (^e)() = ^{
x(s2.o1);
@ -73,7 +91,7 @@ void (^d)() = ^{
// Test 5 (unions/structs and their nesting):
void Test5() {
struct S5 {
struct S5 {
int i1;
id o1;
struct V {
@ -87,22 +105,26 @@ struct S5 {
int i3;
id o3;
}ui;
};
};
union U {
union U {
void * i1;
id o1;
int i3;
id o3;
}ui;
}ui;
struct S5 s2;
union U u2;
void (^c)() = ^{
struct S5 s2;
union U u2;
// struct s2 (int, id, int, id, int, id?), union u2 (id?)
// 01 41 11 12 70 00
// CHECK-LP64: @"\01L_OBJC_CLASS_NAME_{{.*}}" = internal global [6 x i8] c"\01A\11\12p\00"
void (^c)() = ^{
x(s2.ui.o1);
x(u2.o1);
};
c();
};
c();
}
@ -112,6 +134,10 @@ void notifyBlock(id dependentBlock) {
id singleObservationToken;
id token;
void (^b)();
// id, id, void(^)()
// 01 33 00
// CHECK-LP64: @"\01L_OBJC_CLASS_NAME_{{.*}}" = internal global [3 x i8] c"\013\00"
void (^wrapperBlock)() = ^() {
CFRelease(singleObservationToken);
CFRelease(singleObservationToken);
@ -123,28 +149,9 @@ void notifyBlock(id dependentBlock) {
}
void test_empty_block() {
// 01 00
// CHECK-LP64: @"\01L_OBJC_CLASS_NAME_{{.*}}" = internal global [2 x i8] c"\01\00"
void (^wrapperBlock)() = ^() {
};
wrapperBlock();
}
// CHECK-LP64: L_OBJC_CLASS_NAME_:
// CHECK-LP64-NEXT: .asciz "\0011\024"
// CHECK-LP64: L_OBJC_CLASS_NAME_1:
// CHECK-LP64-NEXT: .asciz "\0011\025"
// CHECK-LP64: L_OBJC_CLASS_NAME_6:
// CHECK-LP64-NEXT: .asciz "\0011\023!"
// CHECK-LP64: L_OBJC_CLASS_NAME_11:
// CHECK-LP64-NEXT: .asciz "\001A\021\021"
// CHECK-LP64: L_OBJC_CLASS_NAME_16:
// CHECK-LP64-NEXT: .asciz "\001A\021\022p"
// CHECK-LP64: L_OBJC_CLASS_NAME_20:
// CHECK-LP64-NEXT: .asciz "\0013"
// CHECK-LP64: L_OBJC_CLASS_NAME_24:
// CHECK-LP64-NEXT: .asciz "\001"