Enter the cleanups for a block outside the enclosing

full-expression.  Naturally they're inactive before we enter
the block literal expression.  This restores the intended
behavior that blocks belong to their enclosing scope.

There's a useful -O0 / compile-time optimization that we're
missing here with activating cleanups following straight-line
code from their inactive beginnings.



git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@144268 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
John McCall 2011-11-10 08:15:53 +00:00
Родитель 180f47959a
Коммит 1a343ebbf4
13 изменённых файлов: 243 добавлений и 83 удалений

Просмотреть файл

@ -25,13 +25,14 @@
using namespace clang;
using namespace CodeGen;
CGBlockInfo::CGBlockInfo(const BlockExpr *blockExpr, const char *N)
: Name(N), CXXThisIndex(0), CanBeGlobal(false), NeedsCopyDispose(false),
HasCXXObject(false), UsesStret(false), StructureType(0), Block(blockExpr) {
CGBlockInfo::CGBlockInfo(const BlockDecl *block, StringRef name)
: Name(name), CXXThisIndex(0), CanBeGlobal(false), NeedsCopyDispose(false),
HasCXXObject(false), UsesStret(false), StructureType(0), Block(block) {
// Skip asm prefix, if any.
if (Name && Name[0] == '\01')
++Name;
// Skip asm prefix, if any. 'name' is usually taken directly from
// the mangled name of the enclosing function.
if (!name.empty() && name[0] == '\01')
name = name.substr(1);
}
// Anchor the vtable to this translation unit.
@ -483,15 +484,137 @@ static void computeBlockInfo(CodeGenModule &CGM, CGBlockInfo &info) {
llvm::StructType::get(CGM.getLLVMContext(), elementTypes, true);
}
/// Enter the scope of a block. This should be run at the entrance to
/// a full-expression so that the block's cleanups are pushed at the
/// right place in the stack.
static void enterBlockScope(CodeGenFunction &CGF, BlockDecl *block) {
// Allocate the block info and place it at the head of the list.
CGBlockInfo &blockInfo =
*new CGBlockInfo(block, CGF.CurFn->getName());
blockInfo.NextBlockInfo = CGF.FirstBlockInfo;
CGF.FirstBlockInfo = &blockInfo;
// Compute information about the layout, etc., of this block,
// pushing cleanups as necessary.
computeBlockInfo(CGF.CGM, blockInfo);
// Nothing else to do if it can be global.
if (blockInfo.CanBeGlobal) return;
// Make the allocation for the block.
blockInfo.Address =
CGF.CreateTempAlloca(blockInfo.StructureType, "block");
blockInfo.Address->setAlignment(blockInfo.BlockAlign.getQuantity());
// If there are cleanups to emit, enter them (but inactive).
if (!blockInfo.NeedsCopyDispose) return;
// Walk through the captures (in order) and find the ones not
// captured by constant.
for (BlockDecl::capture_const_iterator ci = block->capture_begin(),
ce = block->capture_end(); ci != ce; ++ci) {
// Ignore __block captures; there's nothing special in the
// on-stack block that we need to do for them.
if (ci->isByRef()) continue;
// Ignore variables that are constant-captured.
const VarDecl *variable = ci->getVariable();
CGBlockInfo::Capture &capture = blockInfo.getCapture(variable);
if (capture.isConstant()) continue;
// Ignore objects that aren't destructed.
QualType::DestructionKind dtorKind =
variable->getType().isDestructedType();
if (dtorKind == QualType::DK_none) continue;
CodeGenFunction::Destroyer *destroyer;
// Block captures count as local values and have imprecise semantics.
// They also can't be arrays, so need to worry about that.
if (dtorKind == QualType::DK_objc_strong_lifetime) {
destroyer = &CodeGenFunction::destroyARCStrongImprecise;
} else {
destroyer = &CGF.getDestroyer(dtorKind);
}
// GEP down to the address.
llvm::Value *addr = CGF.Builder.CreateStructGEP(blockInfo.Address,
capture.getIndex());
CleanupKind cleanupKind = InactiveNormalCleanup;
bool useArrayEHCleanup = CGF.needsEHCleanup(dtorKind);
if (useArrayEHCleanup)
cleanupKind = InactiveNormalAndEHCleanup;
CGF.pushDestroy(cleanupKind, addr, variable->getType(),
*destroyer, useArrayEHCleanup);
// Remember where that cleanup was.
capture.setCleanup(CGF.EHStack.stable_begin());
}
}
/// Enter a full-expression with a non-trivial number of objects to
/// clean up. This is in this file because, at the moment, the only
/// kind of cleanup object is a BlockDecl*.
void CodeGenFunction::enterNonTrivialFullExpression(const ExprWithCleanups *E) {
assert(E->getNumObjects() != 0);
ArrayRef<ExprWithCleanups::CleanupObject> cleanups = E->getObjects();
for (ArrayRef<ExprWithCleanups::CleanupObject>::iterator
i = cleanups.begin(), e = cleanups.end(); i != e; ++i) {
enterBlockScope(*this, *i);
}
}
/// Find the layout for the given block in a linked list and remove it.
static CGBlockInfo *findAndRemoveBlockInfo(CGBlockInfo **head,
const BlockDecl *block) {
while (true) {
assert(head && *head);
CGBlockInfo *cur = *head;
// If this is the block we're looking for, splice it out of the list.
if (cur->getBlockDecl() == block) {
*head = cur->NextBlockInfo;
return cur;
}
head = &cur->NextBlockInfo;
}
}
/// Destroy a chain of block layouts.
void CodeGenFunction::destroyBlockInfos(CGBlockInfo *head) {
assert(head && "destroying an empty chain");
do {
CGBlockInfo *cur = head;
head = cur->NextBlockInfo;
delete cur;
} while (head != 0);
}
/// Emit a block literal expression in the current function.
llvm::Value *CodeGenFunction::EmitBlockLiteral(const BlockExpr *blockExpr) {
std::string Name = CurFn->getName();
CGBlockInfo blockInfo(blockExpr, Name.c_str());
// If the block has no captures, we won't have a pre-computed
// layout for it.
if (!blockExpr->getBlockDecl()->hasCaptures()) {
CGBlockInfo blockInfo(blockExpr->getBlockDecl(), CurFn->getName());
computeBlockInfo(CGM, blockInfo);
blockInfo.BlockExpression = blockExpr;
return EmitBlockLiteral(blockInfo);
}
// Compute information about the layout, etc., of this block.
computeBlockInfo(CGM, blockInfo);
// Find the block info for this block and take ownership of it.
llvm::OwningPtr<CGBlockInfo> blockInfo;
blockInfo.reset(findAndRemoveBlockInfo(&FirstBlockInfo,
blockExpr->getBlockDecl()));
// Using that metadata, generate the actual block function.
blockInfo->BlockExpression = blockExpr;
return EmitBlockLiteral(*blockInfo);
}
llvm::Value *CodeGenFunction::EmitBlockLiteral(const CGBlockInfo &blockInfo) {
// Using the computed layout, generate the actual block function.
llvm::Constant *blockFn
= CodeGenFunction(CGM).GenerateBlockFunction(CurGD, blockInfo,
CurFuncDecl, LocalDeclMap);
@ -509,11 +632,8 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const BlockExpr *blockExpr) {
// Build the block descriptor.
llvm::Constant *descriptor = buildBlockDescriptor(CGM, blockInfo);
llvm::Type *intTy = ConvertType(getContext().IntTy);
llvm::AllocaInst *blockAddr =
CreateTempAlloca(blockInfo.StructureType, "block");
blockAddr->setAlignment(blockInfo.BlockAlign.getQuantity());
llvm::AllocaInst *blockAddr = blockInfo.Address;
assert(blockAddr && "block has no address!");
// Compute the initial on-stack block flags.
BlockFlags flags = BLOCK_HAS_SIGNATURE;
@ -523,9 +643,9 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const BlockExpr *blockExpr) {
// Initialize the block literal.
Builder.CreateStore(isa, Builder.CreateStructGEP(blockAddr, 0, "block.isa"));
Builder.CreateStore(llvm::ConstantInt::get(intTy, flags.getBitMask()),
Builder.CreateStore(llvm::ConstantInt::get(IntTy, flags.getBitMask()),
Builder.CreateStructGEP(blockAddr, 1, "block.flags"));
Builder.CreateStore(llvm::ConstantInt::get(intTy, 0),
Builder.CreateStore(llvm::ConstantInt::get(IntTy, 0),
Builder.CreateStructGEP(blockAddr, 2, "block.reserved"));
Builder.CreateStore(blockFn, Builder.CreateStructGEP(blockAddr, 3,
"block.invoke"));
@ -625,28 +745,11 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const BlockExpr *blockExpr) {
/*captured by init*/ false);
}
// Push a destructor if necessary. The semantics for when this
// actually gets run are really obscure.
// Activate the cleanup if layout pushed one.
if (!ci->isByRef()) {
switch (QualType::DestructionKind dtorKind = type.isDestructedType()) {
case QualType::DK_none:
break;
// Block captures count as local values and have imprecise semantics.
// They also can't be arrays, so need to worry about that.
case QualType::DK_objc_strong_lifetime: {
// This local is a GCC and MSVC compiler workaround.
Destroyer *destroyer = &destroyARCStrongImprecise;
pushDestroy(getCleanupKind(dtorKind), blockField, type,
*destroyer, /*useEHCleanupForArray*/ false);
break;
}
case QualType::DK_objc_weak_lifetime:
case QualType::DK_cxx_destructor:
pushDestroy(dtorKind, blockField, type);
break;
}
EHScopeStack::stable_iterator cleanup = capture.getCleanup();
if (cleanup.isValid())
ActivateCleanupBlock(cleanup);
}
}
@ -800,7 +903,8 @@ llvm::Value *CodeGenFunction::GetAddrOfBlockDecl(const VarDecl *variable,
llvm::Constant *
CodeGenModule::GetAddrOfGlobalBlock(const BlockExpr *blockExpr,
const char *name) {
CGBlockInfo blockInfo(blockExpr, name);
CGBlockInfo blockInfo(blockExpr->getBlockDecl(), name);
blockInfo.BlockExpression = blockExpr;
// Compute information about the layout, etc., of this block.
computeBlockInfo(*this, blockInfo);

Просмотреть файл

@ -23,6 +23,7 @@
#include "clang/AST/ExprCXX.h"
#include "clang/AST/ExprObjC.h"
#include "CodeGenFunction.h"
#include "CGBuilder.h"
#include "CGCall.h"
#include "CGValue.h"
@ -128,13 +129,14 @@ inline BlockFieldFlags operator|(BlockFieldFlag_t l, BlockFieldFlag_t r) {
class CGBlockInfo {
public:
/// Name - The name of the block, kindof.
const char *Name;
llvm::StringRef Name;
/// The field index of 'this' within the block, if there is one.
unsigned CXXThisIndex;
class Capture {
uintptr_t Data;
EHScopeStack::stable_iterator Cleanup;
public:
bool isIndex() const { return (Data & 1) != 0; }
@ -144,6 +146,14 @@ public:
assert(isConstant());
return reinterpret_cast<llvm::Value*>(Data);
}
EHScopeStack::stable_iterator getCleanup() const {
assert(isIndex());
return Cleanup;
}
void setCleanup(EHScopeStack::stable_iterator cleanup) {
assert(isIndex());
Cleanup = cleanup;
}
static Capture makeIndex(unsigned index) {
Capture v;
@ -158,9 +168,6 @@ public:
}
};
/// 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;
@ -176,22 +183,35 @@ public:
/// because it gets set later in the block-creation process.
mutable bool UsesStret : 1;
/// The mapping of allocated indexes within the block.
llvm::DenseMap<const VarDecl*, Capture> Captures;
llvm::AllocaInst *Address;
llvm::StructType *StructureType;
const BlockExpr *Block;
const BlockDecl *Block;
const BlockExpr *BlockExpression;
CharUnits BlockSize;
CharUnits BlockAlign;
CGBlockInfo *NextBlockInfo;
const Capture &getCapture(const VarDecl *var) const {
llvm::DenseMap<const VarDecl*, Capture>::const_iterator
return const_cast<CGBlockInfo*>(this)->getCapture(var);
}
Capture &getCapture(const VarDecl *var) {
llvm::DenseMap<const VarDecl*, Capture>::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; }
const BlockDecl *getBlockDecl() const { return Block; }
const BlockExpr *getBlockExpr() const {
assert(BlockExpression);
assert(BlockExpression->getBlockDecl() == Block);
return BlockExpression;
}
CGBlockInfo(const BlockExpr *blockExpr, const char *Name);
CGBlockInfo(const BlockDecl *blockDecl, llvm::StringRef Name);
};
} // end namespace CodeGen

Просмотреть файл

@ -496,9 +496,11 @@ void CodeGenFunction::EmitScalarInit(const Expr *init,
// If we're emitting a value with lifetime, we have to do the
// initialization *before* we leave the cleanup scopes.
CodeGenFunction::RunCleanupsScope Scope(*this);
if (const ExprWithCleanups *ewc = dyn_cast<ExprWithCleanups>(init))
if (const ExprWithCleanups *ewc = dyn_cast<ExprWithCleanups>(init)) {
enterFullExpression(ewc);
init = ewc->getSubExpr();
}
CodeGenFunction::RunCleanupsScope Scope(*this);
// We have to maintain the illusion that the variable is
// zero-initialized. If the variable might be accessed in its

Просмотреть файл

@ -229,10 +229,11 @@ EmitExprForReferenceBinding(CodeGenFunction &CGF, const Expr *E,
if (const CXXDefaultArgExpr *DAE = dyn_cast<CXXDefaultArgExpr>(E))
E = DAE->getExpr();
if (const ExprWithCleanups *TE = dyn_cast<ExprWithCleanups>(E)) {
if (const ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(E)) {
CGF.enterFullExpression(EWC);
CodeGenFunction::RunCleanupsScope Scope(CGF);
return EmitExprForReferenceBinding(CGF, TE->getSubExpr(),
return EmitExprForReferenceBinding(CGF, EWC->getSubExpr(),
ReferenceTemporary,
ReferenceTemporaryDtor,
ObjCARCReferenceLifetimeType,
@ -677,8 +678,14 @@ LValue CodeGenFunction::EmitLValue(const Expr *E) {
return EmitCXXConstructLValue(cast<CXXConstructExpr>(E));
case Expr::CXXBindTemporaryExprClass:
return EmitCXXBindTemporaryLValue(cast<CXXBindTemporaryExpr>(E));
case Expr::ExprWithCleanupsClass:
return EmitExprWithCleanupsLValue(cast<ExprWithCleanups>(E));
case Expr::ExprWithCleanupsClass: {
const ExprWithCleanups *cleanups = cast<ExprWithCleanups>(E);
enterFullExpression(cleanups);
RunCleanupsScope Scope(*this);
return EmitLValue(cleanups->getSubExpr());
}
case Expr::CXXScalarValueInitExprClass:
return EmitNullInitializationLValue(cast<CXXScalarValueInitExpr>(E));
case Expr::CXXDefaultArgExprClass:

Просмотреть файл

@ -536,7 +536,9 @@ AggExprEmitter::VisitCXXConstructExpr(const CXXConstructExpr *E) {
}
void AggExprEmitter::VisitExprWithCleanups(ExprWithCleanups *E) {
CGF.EmitExprWithCleanups(E, Dest);
CGF.enterFullExpression(E);
CodeGenFunction::RunCleanupsScope cleanups(CGF);
Visit(E->getSubExpr());
}
void AggExprEmitter::VisitCXXScalarValueInitExpr(CXXScalarValueInitExpr *E) {

Просмотреть файл

@ -179,7 +179,9 @@ public:
return Visit(DAE->getExpr());
}
ComplexPairTy VisitExprWithCleanups(ExprWithCleanups *E) {
return CGF.EmitExprWithCleanups(E).getComplexVal();
CGF.enterFullExpression(E);
CodeGenFunction::RunCleanupsScope Scope(CGF);
return Visit(E->getSubExpr());
}
ComplexPairTy VisitCXXScalarValueInitExpr(CXXScalarValueInitExpr *E) {
assert(E->getType()->isAnyComplexType() && "Expected complex type!");

Просмотреть файл

@ -353,7 +353,9 @@ public:
}
Value *VisitExprWithCleanups(ExprWithCleanups *E) {
return CGF.EmitExprWithCleanups(E).getScalarVal();
CGF.enterFullExpression(E);
CodeGenFunction::RunCleanupsScope Scope(CGF);
return Visit(E->getSubExpr());
}
Value *VisitCXXNewExpr(const CXXNewExpr *E) {
return CGF.EmitCXXNewExpr(E);

Просмотреть файл

@ -2177,6 +2177,7 @@ static TryEmitResult
tryEmitARCRetainScalarExpr(CodeGenFunction &CGF, const Expr *e) {
// Look through cleanups.
if (const ExprWithCleanups *cleanups = dyn_cast<ExprWithCleanups>(e)) {
CGF.enterFullExpression(cleanups);
CodeGenFunction::RunCleanupsScope scope(CGF);
return tryEmitARCRetainScalarExpr(CGF, cleanups->getSubExpr());
}
@ -2376,10 +2377,12 @@ llvm::Value *CodeGenFunction::EmitObjCThrowOperand(const Expr *expr) {
// @throw A().foo;
// where a full retain+autorelease is required and would
// otherwise happen after the destructor for the temporary.
CodeGenFunction::RunCleanupsScope cleanups(*this);
if (const ExprWithCleanups *ewc = dyn_cast<ExprWithCleanups>(expr))
if (const ExprWithCleanups *ewc = dyn_cast<ExprWithCleanups>(expr)) {
enterFullExpression(ewc);
expr = ewc->getSubExpr();
}
CodeGenFunction::RunCleanupsScope cleanups(*this);
return EmitARCRetainAutoreleaseScalarExpr(expr);
}

Просмотреть файл

@ -35,15 +35,3 @@ void CodeGenFunction::EmitCXXTemporary(const CXXTemporary *Temporary,
Temporary->getDestructor(),
Ptr);
}
RValue
CodeGenFunction::EmitExprWithCleanups(const ExprWithCleanups *E,
AggValueSlot Slot) {
RunCleanupsScope Scope(*this);
return EmitAnyExpr(E->getSubExpr(), Slot);
}
LValue CodeGenFunction::EmitExprWithCleanupsLValue(const ExprWithCleanups *E) {
RunCleanupsScope Scope(*this);
return EmitLValue(E->getSubExpr());
}

Просмотреть файл

@ -33,7 +33,7 @@ CodeGenFunction::CodeGenFunction(CodeGenModule &cgm)
: CodeGenTypeCache(cgm), CGM(cgm),
Target(CGM.getContext().getTargetInfo()), Builder(cgm.getModule().getContext()),
AutoreleaseResult(false), BlockInfo(0), BlockPointer(0),
NormalCleanupDest(0), NextCleanupDestIndex(1),
NormalCleanupDest(0), NextCleanupDestIndex(1), FirstBlockInfo(0),
EHResumeBlock(0), ExceptionSlot(0), EHSelectorSlot(0),
DebugInfo(0), DisableDebugInfo(false), DidCallStackSave(false),
IndirectBranch(0), SwitchInsn(0), CaseRangeBlock(0), UnreachableBlock(0),
@ -45,6 +45,14 @@ CodeGenFunction::CodeGenFunction(CodeGenModule &cgm)
CGM.getCXXABI().getMangleContext().startNewFunction();
}
CodeGenFunction::~CodeGenFunction() {
// If there are any unclaimed block infos, go ahead and destroy them
// now. This can happen if IR-gen gets clever and skips evaluating
// something.
if (FirstBlockInfo)
destroyBlockInfos(FirstBlockInfo);
}
llvm::Type *CodeGenFunction::ConvertTypeForMem(QualType T) {
return CGM.getTypes().ConvertTypeForMem(T);

Просмотреть файл

@ -45,6 +45,7 @@ namespace llvm {
namespace clang {
class APValue;
class ASTContext;
class BlockDecl;
class CXXDestructorDecl;
class CXXForRangeStmt;
class CXXTryStmt;
@ -610,6 +611,9 @@ public:
unsigned NextCleanupDestIndex;
/// FirstBlockInfo - The head of a singly-linked-list of block layouts.
CGBlockInfo *FirstBlockInfo;
/// EHResumeBlock - Unified block containing a call to llvm.eh.resume.
llvm::BasicBlock *EHResumeBlock;
@ -1169,6 +1173,7 @@ private:
public:
CodeGenFunction(CodeGenModule &cgm);
~CodeGenFunction();
CodeGenTypes &getTypes() const { return CGM.getTypes(); }
ASTContext &getContext() const { return CGM.getContext(); }
@ -1297,6 +1302,8 @@ public:
//===--------------------------------------------------------------------===//
llvm::Value *EmitBlockLiteral(const BlockExpr *);
llvm::Value *EmitBlockLiteral(const CGBlockInfo &Info);
static void destroyBlockInfos(CGBlockInfo *info);
llvm::Constant *BuildDescriptorBlockDecl(const BlockExpr *,
const CGBlockInfo &Info,
llvm::StructType *,
@ -2074,7 +2081,6 @@ public:
LValue EmitCXXConstructLValue(const CXXConstructExpr *E);
LValue EmitCXXBindTemporaryLValue(const CXXBindTemporaryExpr *E);
LValue EmitExprWithCleanupsLValue(const ExprWithCleanups *E);
LValue EmitCXXTypeidLValue(const CXXTypeidExpr *E);
LValue EmitObjCMessageExprLValue(const ObjCMessageExpr *E);
@ -2348,8 +2354,11 @@ public:
void EmitSynthesizedCXXCopyCtor(llvm::Value *Dest, llvm::Value *Src,
const Expr *Exp);
RValue EmitExprWithCleanups(const ExprWithCleanups *E,
AggValueSlot Slot =AggValueSlot::ignored());
void enterFullExpression(const ExprWithCleanups *E) {
if (E->getNumObjects() == 0) return;
enterNonTrivialFullExpression(E);
}
void enterNonTrivialFullExpression(const ExprWithCleanups *E);
void EmitCXXThrowExpr(const CXXThrowExpr *E);

Просмотреть файл

@ -25,15 +25,19 @@ void test2(id x) {
// CHECK: define void @test2(
// CHECK: [[X:%.*]] = alloca i8*,
// CHECK-NEXT: [[BLOCK:%.*]] = alloca [[BLOCK_T:<{.*}>]],
// CHECK-NEXT: alloca i1
// CHECK-NEXT: store i1 false
// CHECK-NEXT: [[PARM:%.*]] = call i8* @objc_retain(i8* {{%.*}})
// CHECK-NEXT: store i8* [[PARM]], i8** [[X]]
// CHECK-NEXT: [[SLOTREL:%.*]] = getelementptr inbounds [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5
// CHECK: [[SLOT:%.*]] = getelementptr inbounds [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5
// CHECK-NEXT: [[T0:%.*]] = load i8** [[X]],
// CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retain(i8* [[T0]])
// CHECK-NEXT: store i8* [[T1]], i8** [[SLOT]],
// CHECK-NEXT: store i1 true
// CHECK-NEXT: bitcast
// CHECK-NEXT: call void @test2_helper(
// CHECK-NEXT: [[T0:%.*]] = load i8** [[SLOT]]
// CHECK-NEXT: [[T0:%.*]] = load i8** [[SLOTREL]]
// CHECK-NEXT: call void @objc_release(i8* [[T0]]) nounwind, !clang.imprecise_release
// CHECK-NEXT: [[T0:%.*]] = load i8** [[X]]
// CHECK-NEXT: call void @objc_release(i8* [[T0]]) nounwind, !clang.imprecise_release
@ -233,8 +237,8 @@ void test7(void) {
// CHECK-NEXT: [[T0:%.*]] = call i8* @objc_loadWeak(i8** [[VAR]])
// CHECK-NEXT: call i8* @objc_initWeak(i8** [[SLOT]], i8* [[T0]])
// CHECK: call void @test7_helper(
// CHECK-NEXT: call void @objc_destroyWeak(i8** [[SLOT]])
// CHECK-NEXT: call void @objc_destroyWeak(i8** [[VAR]])
// CHECK-NEXT: call void @objc_destroyWeak(i8** {{%.*}})
// CHECK: call void @objc_destroyWeak(i8** [[VAR]])
// CHECK-NEXT: ret void
// CHECK: define internal void @__test7_block_invoke_
@ -262,15 +266,17 @@ void test7(void) {
// CHECK-NEXT: [[BLOCK:%.*]] = alloca [[BLOCK_T:<{.*}>]],
// CHECK: store
// CHECK-NEXT: store
// CHECK: [[D0:%.*]] = getelementptr inbounds [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5
// CHECK: [[T0:%.*]] = getelementptr inbounds [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5
// CHECK-NEXT: [[T1:%.*]] = load [[TEST8]]** [[SELF]],
// CHECK-NEXT: [[T2:%.*]] = bitcast [[TEST8]]* [[T1]] to i8*
// CHECK-NEXT: [[T3:%.*]] = call i8* @objc_retain(i8* [[T2]])
// CHECK-NEXT: [[T4:%.*]] = bitcast i8* [[T3]] to [[TEST8]]*
// CHECK-NEXT: store [[TEST8]]* [[T4]], [[TEST8]]** [[T0]]
// CHECK-NEXT: store i1 true,
// CHECK-NEXT: bitcast [[BLOCK_T]]* [[BLOCK]] to
// CHECK: call void @test8_helper(
// CHECK-NEXT: [[T1:%.*]] = load [[TEST8]]** [[T0]]
// CHECK-NEXT: [[T1:%.*]] = load [[TEST8]]** [[D0]]
// CHECK-NEXT: [[T2:%.*]] = bitcast [[TEST8]]* [[T1]] to i8*
// CHECK-NEXT: call void @objc_release(i8* [[T2]])
// CHECK-NEXT: ret void

Просмотреть файл

@ -29,6 +29,9 @@ void test0(NSArray *array) {
// CHECK-LP64-NEXT: [[BUFFER:%.*]] = alloca [16 x i8*], align 8
// CHECK-LP64-NEXT: [[BLOCK:%.*]] = alloca [[BLOCK_T:<{.*}>]],
// CHECK-LP64-NEXT: [[CAP_ACTIVE:%.*]] = alloca i1
// CHECK-LP64-NEXT: store i1 false, i1* [[CAP_ACTIVE]]
// Initialize 'array'.
// CHECK-LP64-NEXT: [[T0:%.*]] = bitcast [[ARRAY_T:%.*]]* {{%.*}} to i8*
// CHECK-LP64-NEXT: [[T1:%.*]] = call i8* @objc_retain(i8* [[T0]])
@ -60,13 +63,15 @@ void test0(NSArray *array) {
// CHECK-LP64-NEXT: [[T3:%.*]] = load i8** [[T2]]
// CHECK-LP64-NEXT: store i8* [[T3]], i8** [[X]]
// CHECK-LP64: [[D0:%.*]] = getelementptr inbounds [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5
// CHECK-LP64: [[T0:%.*]] = getelementptr inbounds [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5
// CHECK-LP64-NEXT: [[T1:%.*]] = load i8** [[X]]
// CHECK-LP64-NEXT: [[T2:%.*]] = call i8* @objc_retain(i8* [[T1]])
// CHECK-LP64-NEXT: store i8* [[T2]], i8** [[T0]]
// CHECK-LP64-NEXT: store i1 true, i1* [[CAP_ACTIVE]]
// CHECK-LP64-NEXT: [[T1:%.*]] = bitcast [[BLOCK_T]]* [[BLOCK]]
// CHECK-LP64: call void @use_block(
// CHECK-LP64-NEXT: [[T1:%.*]] = load i8** [[T0]]
// CHECK-LP64-NEXT: [[T1:%.*]] = load i8** [[D0]]
// CHECK-LP64-NEXT: call void @objc_release(i8* [[T1]])
// CHECK-LP64: [[T0:%.*]] = load i8** @"\01L_OBJC_SELECTOR_REFERENCES_
@ -108,13 +113,15 @@ void test1(NSArray *array) {
// CHECK-LP64-NEXT: [[T3:%.*]] = load i8** [[T2]]
// CHECK-LP64-NEXT: call i8* @objc_initWeak(i8** [[X]], i8* [[T3]])
// CHECK-LP64: [[D0:%.*]] = getelementptr inbounds [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5
// CHECK-LP64: [[T0:%.*]] = getelementptr inbounds [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5
// CHECK-LP64-NEXT: [[T1:%.*]] = call i8* @objc_loadWeak(i8** [[X]])
// CHECK-LP64-NEXT: call i8* @objc_initWeak(i8** [[T0]], i8* [[T1]])
// CHECK-LP64-NEXT: store i1 true,
// CHECK-LP64-NEXT: [[T1:%.*]] = bitcast [[BLOCK_T]]* [[BLOCK]] to
// CHECK-LP64: call void @use_block
// CHECK-LP64-NEXT: call void @objc_destroyWeak(i8** [[T0]])
// CHECK-LP64-NEXT: call void @objc_destroyWeak(i8** [[X]])
// CHECK-LP64: call void @objc_destroyWeak(i8** [[D0]])
// CHECK-LP64: call void @objc_destroyWeak(i8** [[X]])
// rdar://problem/9817306
@interface Test2