From 97d01d50d9407f8781fc6ae7c8d6d14f16f81c23 Mon Sep 17 00:00:00 2001 From: Mike Stump Date: Wed, 4 Mar 2009 03:23:46 +0000 Subject: [PATCH] Improved ABI compliance for __block variables. No testcases yet as we still give an unsupported error for them due to the fact this is a work in progress. llvm-svn: 66007 --- clang/lib/CodeGen/CGBlocks.cpp | 126 +++++++++++++++++++++++----- clang/lib/CodeGen/CGDecl.cpp | 111 ++++++++++++++++++++++-- clang/lib/CodeGen/CGExpr.cpp | 50 +++-------- clang/lib/CodeGen/CodeGenFunction.h | 33 ++++++++ clang/lib/CodeGen/CodeGenModule.h | 9 ++ 5 files changed, 264 insertions(+), 65 deletions(-) diff --git a/clang/lib/CodeGen/CGBlocks.cpp b/clang/lib/CodeGen/CGBlocks.cpp index ee17a862b161..772c9ad2c180 100644 --- a/clang/lib/CodeGen/CGBlocks.cpp +++ b/clang/lib/CodeGen/CGBlocks.cpp @@ -21,15 +21,6 @@ using namespace clang; using namespace CodeGen; -enum { - BLOCK_NEEDS_FREE = (1 << 24), - BLOCK_HAS_COPY_DISPOSE = (1 << 25), - BLOCK_HAS_CXX_OBJ = (1 << 26), - BLOCK_IS_GC = (1 << 27), - BLOCK_IS_GLOBAL = (1 << 28), - BLOCK_HAS_DESCRIPTOR = (1 << 29) -}; - llvm::Constant *CodeGenFunction::BuildDescriptorBlockDecl(uint64_t Size) { const llvm::PointerType *PtrToInt8Ty = llvm::PointerType::getUnqual(llvm::Type::Int8Ty); @@ -142,7 +133,11 @@ llvm::Value *CodeGenFunction::BuildBlockLiteralTmp(const BlockExpr *BE) { CollectBlockDeclRefInfo(BE->getBody(), Info); // Check if the block can be global. - if (CanBlockBeGlobal(Info)) + // 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; @@ -209,9 +204,11 @@ llvm::Value *CodeGenFunction::BuildBlockLiteralTmp(const BlockExpr *BE) { const Expr *E = subBlockDeclRefDecls[i]; const BlockDeclRefExpr *BDRE = dyn_cast(E); QualType Ty = E->getType(); - if (BDRE && BDRE->isByRef()) - Ty = getContext().getPointerType(Ty); - Types[i+5] = ConvertType(Ty); + 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); @@ -237,23 +234,41 @@ llvm::Value *CodeGenFunction::BuildBlockLiteralTmp(const BlockExpr *BE) { 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]) { - E = new (getContext()) DeclRefExpr (cast(VD), - VD->getType(), SourceLocation(), - false, false); + 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()) + if (BDRE->isByRef()) { + // FIXME: __block in nested literals E = new (getContext()) UnaryOperator(E, UnaryOperator::AddrOf, getContext().getPointerType(E->getType()), SourceLocation()); + } - llvm::Value* Addr = Builder.CreateStructGEP(V, i+5, "tmp"); RValue r = EmitAnyExpr(E, Addr, false); - if (r.isScalar()) - Builder.CreateStore(r.getScalarVal(), Addr); - else if (r.isComplex()) + 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()) @@ -426,6 +441,55 @@ RValue CodeGenFunction::EmitBlockCallExpr(const CallExpr* E) { 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 (1 && 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 * CodeGenModule::GetAddrOfGlobalBlock(const BlockExpr *BE, const char * n) { // Generate the block descriptor. @@ -589,3 +653,23 @@ uint64_t CodeGenFunction::getBlockOffset(const BlockDeclRefExpr *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; +} diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index d8123c8ec136..91071afeb800 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -21,6 +21,7 @@ #include "clang/Basic/TargetInfo.h" #include "llvm/GlobalVariable.h" #include "llvm/Intrinsics.h" +#include "llvm/Target/TargetData.h" #include "llvm/Type.h" using namespace clang; using namespace CodeGen; @@ -188,20 +189,61 @@ void CodeGenFunction::EmitStaticBlockVarDecl(const VarDecl &D) { } } +/// BuildByRefType - This routine changes a __block variable declared as T x +/// into: +/// +/// struct { +/// void *__isa; +/// void *__forwarding; +/// int32_t __flags; +/// int32_t __size; +/// void *__copy_helper; +/// void *__destroy_helper; +/// T x; +/// } x +/// +/// Align is the alignment needed in bytes for x. +const llvm::Type *CodeGenFunction::BuildByRefType(QualType Ty, + uint64_t Align) { + const llvm::Type *LTy = ConvertType(Ty); + bool needsCopyDispose = BlockRequiresCopying(Ty); + std::vector Types(needsCopyDispose*2+5); + const llvm::PointerType *PtrToInt8Ty + = llvm::PointerType::getUnqual(llvm::Type::Int8Ty); + Types[0] = PtrToInt8Ty; + Types[1] = PtrToInt8Ty; + Types[2] = llvm::Type::Int32Ty; + Types[3] = llvm::Type::Int32Ty; + if (needsCopyDispose) { + Types[4] = PtrToInt8Ty; + Types[5] = PtrToInt8Ty; + } + // FIXME: Align this on at least an Align boundary. + Types[needsCopyDispose*2 + 4] = LTy; + return llvm::StructType::get(Types, false); +} + /// EmitLocalBlockVarDecl - Emit code and set up an entry in LocalDeclMap for a /// variable declaration with auto, register, or no storage class specifier. /// These turn into simple stack objects, or GlobalValues depending on target. void CodeGenFunction::EmitLocalBlockVarDecl(const VarDecl &D) { QualType Ty = D.getType(); + bool isByRef = D.getAttr(); llvm::Value *DeclPtr; if (Ty->isConstantSizeType()) { if (!Target.useGlobalsForAutomaticVariables()) { // A normal fixed sized variable becomes an alloca in the entry block. const llvm::Type *LTy = ConvertType(Ty); + if (isByRef) + LTy = BuildByRefType(Ty, getContext().getDeclAlignInBytes(&D)); llvm::AllocaInst *Alloc = CreateTempAlloca(LTy, CGM.getMangledName(&D)); - Alloc->setAlignment(getContext().getDeclAlignInBytes(&D)); + if (isByRef) + Alloc->setAlignment(std::max(getContext().getDeclAlignInBytes(&D), + getContext().getTypeAlign(getContext().VoidPtrTy) / 8)); + else + Alloc->setAlignment(getContext().getDeclAlignInBytes(&D)); DeclPtr = Alloc; } else { // Targets that don't support recursion emit locals as globals. @@ -259,18 +301,77 @@ void CodeGenFunction::EmitLocalBlockVarDecl(const VarDecl &D) { // Emit debug info for local var declaration. if (CGDebugInfo *DI = getDebugInfo()) { DI->setLocation(D.getLocation()); - DI->EmitDeclareOfAutoVariable(&D, DeclPtr, Builder); + if (isByRef) { + llvm::Value *Loc; + bool needsCopyDispose = BlockRequiresCopying(Ty); + // FIXME: I think we need to indirect through the forwarding pointer first + Loc = Builder.CreateStructGEP(DeclPtr, needsCopyDispose*2+4, "x"); + DI->EmitDeclareOfAutoVariable(&D, Loc, Builder); + } else + DI->EmitDeclareOfAutoVariable(&D, DeclPtr, Builder); } // If this local has an initializer, emit it now. if (const Expr *Init = D.getInit()) { + llvm::Value *Loc = DeclPtr; + if (isByRef) { + bool needsCopyDispose = BlockRequiresCopying(Ty); + Loc = Builder.CreateStructGEP(DeclPtr, needsCopyDispose*2+4, "x"); + } if (!hasAggregateLLVMType(Init->getType())) { llvm::Value *V = EmitScalarExpr(Init); - Builder.CreateStore(V, DeclPtr, D.getType().isVolatileQualified()); + Builder.CreateStore(V, Loc, D.getType().isVolatileQualified()); } else if (Init->getType()->isAnyComplexType()) { - EmitComplexExprIntoAddr(Init, DeclPtr, D.getType().isVolatileQualified()); + EmitComplexExprIntoAddr(Init, Loc, D.getType().isVolatileQualified()); } else { - EmitAggExpr(Init, DeclPtr, D.getType().isVolatileQualified()); + EmitAggExpr(Init, Loc, D.getType().isVolatileQualified()); + } + } + if (isByRef) { + const llvm::PointerType *PtrToInt8Ty + = llvm::PointerType::getUnqual(llvm::Type::Int8Ty); + + llvm::Value *isa_field = Builder.CreateStructGEP(DeclPtr, 0); + llvm::Value *forwarding_field = Builder.CreateStructGEP(DeclPtr, 1); + llvm::Value *flags_field = Builder.CreateStructGEP(DeclPtr, 2); + llvm::Value *size_field = Builder.CreateStructGEP(DeclPtr, 3); + llvm::Value *V; + int flag = 0; + int flags = 0; + + if (Ty->isBlockPointerType()) { + flag |= BLOCK_FIELD_IS_BLOCK; + flags |= BLOCK_HAS_COPY_DISPOSE; + } else if (BlockRequiresCopying(Ty)) { + flags |= BLOCK_HAS_COPY_DISPOSE; + flag |= BLOCK_FIELD_IS_OBJECT; + } + // FIXME: Need to set BLOCK_FIELD_IS_WEAK as appropriate. + + int isa = 0; + if (flag&BLOCK_FIELD_IS_WEAK) + isa = 1; + V = llvm::ConstantInt::get(llvm::Type::Int32Ty, isa); + V = Builder.CreateIntToPtr(V, PtrToInt8Ty, "tmp"); + Builder.CreateStore(V, isa_field); + + V = Builder.CreateBitCast(DeclPtr, PtrToInt8Ty, "tmp"); + Builder.CreateStore(V, forwarding_field); + + V = llvm::ConstantInt::get(llvm::Type::Int32Ty, flags); + Builder.CreateStore(V, flags_field); + + const llvm::Type *V1 = cast(DeclPtr->getType())->getElementType(); + V = llvm::ConstantInt::get(llvm::Type::Int32Ty, CGM.getTargetData().getTypeStoreSizeInBits(V1) / 8); + Builder.CreateStore(V, size_field); + + if (flags & BLOCK_HAS_COPY_DISPOSE) { + llvm::Value *copy_helper = Builder.CreateStructGEP(DeclPtr, 4); + llvm::Value *destroy_helper = Builder.CreateStructGEP(DeclPtr, 5); + + Builder.CreateStore(BuildCopyHelper(flag), copy_helper); + + Builder.CreateStore(BuildDestroyHelper(flag), destroy_helper); } } diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 5485e9eb1f6d..d54c6954fb26 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -636,6 +636,17 @@ LValue CodeGenFunction::EmitDeclRefLValue(const DeclRefExpr *E) { // local static? if (!VD->hasLocalStorage()) attr = getContext().getObjCGCAttrKind(E->getType()); + if (VD->getAttr()) { + bool needsCopyDispose = BlockRequiresCopying(VD->getType()); + const llvm::Type *PtrStructTy = V->getType(); + const llvm::Type *Ty = PtrStructTy; + Ty = llvm::PointerType::get(Ty, 0); + V = Builder.CreateStructGEP(V, 1, "forwarding"); + V = Builder.CreateBitCast(V, Ty); + V = Builder.CreateLoad(V, false); + V = Builder.CreateBitCast(V, PtrStructTy); + V = Builder.CreateStructGEP(V, needsCopyDispose*2 + 4, "x"); + } LV = LValue::MakeAddr(V, E->getType().getCVRQualifiers(), attr); } LValue::SetObjCNonGC(LV, VD->hasLocalStorage()); @@ -667,45 +678,6 @@ LValue CodeGenFunction::EmitBlockDeclRefLValue(const BlockDeclRefExpr *E) { return LValue::MakeAddr(GetAddrOfBlockDecl(E), 0); } -llvm::Value *CodeGenFunction::GetAddrOfBlockDecl(const BlockDeclRefExpr *E) { - // FIXME: ensure we don't need copy/dispose. - uint64_t &offset = BlockDecls[E->getDecl()]; - - const llvm::Type *Ty; - Ty = CGM.getTypes().ConvertType(E->getDecl()->getType()); - - if (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"); - Ty = llvm::PointerType::get(Ty, 0); - if (E->isByRef()) - Ty = llvm::PointerType::get(Ty, 0); - V = Builder.CreateBitCast(V, Ty); - if (E->isByRef()) - V = Builder.CreateLoad(V, false, "tmp"); - - return V; -} - LValue CodeGenFunction::EmitUnaryOpLValue(const UnaryOperator *E) { // __extension__ doesn't affect lvalue-ness. if (E->getOpcode() == UnaryOperator::Extension) diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index bf9c4e5ba5cb..f2b7ed4795c7 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -264,6 +264,30 @@ public: // Block Bits //===--------------------------------------------------------------------===// + enum { + BLOCK_FIELD_IS_OBJECT = 3, /* id, NSObject, __attribute__((NSObject)), + block, ... */ + BLOCK_FIELD_IS_BLOCK = 7, /* a block variable */ + BLOCK_FIELD_IS_BYREF = 8, /* the on stack structure holding the __block + variable */ + BLOCK_FIELD_IS_WEAK = 16, /* declared __weak, only used in byref copy + helpers */ + BLOCK_BYREF_CALLER = 128 /* called from __block (byref) copy/dispose + support routines */ + }; + + enum { + BLOCK_NEEDS_FREE = (1 << 24), + BLOCK_HAS_COPY_DISPOSE = (1 << 25), + BLOCK_HAS_CXX_OBJ = (1 << 26), + BLOCK_IS_GC = (1 << 27), + BLOCK_IS_GLOBAL = (1 << 28), + BLOCK_HAS_DESCRIPTOR = (1 << 29) + }; + + llvm::Value *BuildCopyHelper(int flag); + llvm::Value *BuildDestroyHelper(int flag); + llvm::Value *BuildBlockLiteralTmp(const BlockExpr *); llvm::Constant *BuildDescriptorBlockDecl(uint64_t Size); @@ -319,6 +343,15 @@ public: llvm::Value *GetAddrOfBlockDecl(const BlockDeclRefExpr *E); + const llvm::Type *BuildByRefType(QualType Ty, uint64_t Align); + bool BlockRequiresCopying(QualType Ty) { + if (Ty->isBlockPointerType()) + return true; + if (getContext().isObjCNSObjectType(Ty)) + return true; + return false; + } + void GenerateCode(const FunctionDecl *FD, llvm::Function *Fn); void StartFunction(const Decl *D, QualType RetTy, diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index 093a9b12f76b..21faaa6a09a7 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -139,6 +139,15 @@ class CodeGenModule { /// strings. This value has type int * but is actually an Obj-C class pointer. llvm::Constant *CFConstantStringClassRef; + enum { + BLOCK_NEEDS_FREE = (1 << 24), + BLOCK_HAS_COPY_DISPOSE = (1 << 25), + BLOCK_HAS_CXX_OBJ = (1 << 26), + BLOCK_IS_GC = (1 << 27), + BLOCK_IS_GLOBAL = (1 << 28), + BLOCK_HAS_DESCRIPTOR = (1 << 29) + }; + /// NSConcreteGlobalBlock - Cached reference to the class pointer for global /// blocks. llvm::Constant *NSConcreteGlobalBlock;