From e0c1168ec7910a1a7ed08df4d4f0c58c2fa2ecd1 Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 2 Jul 2012 23:58:38 +0000 Subject: [PATCH] Significantly simplify CGExprAgg's logic about ignored results: if we want to ignore a result, the Dest will be null. Otherwise, we must copy into it. This means we need to ensure a slot when loading from a volatile l-value. With all that in place, fix a bug with chained assignments into __block variables of aggregate type where we were losing insight into the actual source of the value during the second assignment. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@159630 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/CGExpr.cpp | 15 +- lib/CodeGen/CGExprAgg.cpp | 250 +++++++++++++++++++++----------- lib/CodeGen/CGValue.h | 3 +- lib/CodeGen/CodeGenFunction.h | 9 +- test/CodeGen/block-byref-aggr.c | 71 +++++++-- 5 files changed, 242 insertions(+), 106 deletions(-) diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp index cb446dfeb4..5f708d7a73 100644 --- a/lib/CodeGen/CGExpr.cpp +++ b/lib/CodeGen/CGExpr.cpp @@ -109,15 +109,18 @@ void CodeGenFunction::EmitIgnoredExpr(const Expr *E) { /// can have any type. The result is returned as an RValue struct. /// If this is an aggregate expression, AggSlot indicates where the /// result should be returned. -RValue CodeGenFunction::EmitAnyExpr(const Expr *E, AggValueSlot AggSlot, - bool IgnoreResult) { +RValue CodeGenFunction::EmitAnyExpr(const Expr *E, + AggValueSlot aggSlot, + bool ignoreResult) { if (!hasAggregateLLVMType(E->getType())) - return RValue::get(EmitScalarExpr(E, IgnoreResult)); + return RValue::get(EmitScalarExpr(E, ignoreResult)); else if (E->getType()->isAnyComplexType()) - return RValue::getComplex(EmitComplexExpr(E, IgnoreResult, IgnoreResult)); + return RValue::getComplex(EmitComplexExpr(E, ignoreResult, ignoreResult)); - EmitAggExpr(E, AggSlot, IgnoreResult); - return AggSlot.asRValue(); + if (!ignoreResult && aggSlot.isIgnored()) + aggSlot = CreateAggTemp(E->getType(), "agg-temp"); + EmitAggExpr(E, aggSlot); + return aggSlot.asRValue(); } /// EmitAnyExprToTemp - Similary to EmitAnyExpr(), however, the result will diff --git a/lib/CodeGen/CGExprAgg.cpp b/lib/CodeGen/CGExprAgg.cpp index 9616af633d..61f7362eca 100644 --- a/lib/CodeGen/CGExprAgg.cpp +++ b/lib/CodeGen/CGExprAgg.cpp @@ -34,7 +34,6 @@ class AggExprEmitter : public StmtVisitor { CodeGenFunction &CGF; CGBuilderTy &Builder; AggValueSlot Dest; - bool IgnoreResult; /// We want to use 'dest' as the return slot except under two /// conditions: @@ -56,12 +55,14 @@ class AggExprEmitter : public StmtVisitor { if (!Dest.isIgnored()) return Dest; return CGF.CreateAggTemp(T, "agg.tmp.ensured"); } + void EnsureDest(QualType T) { + if (!Dest.isIgnored()) return; + Dest = CGF.CreateAggTemp(T, "agg.tmp.ensured"); + } public: - AggExprEmitter(CodeGenFunction &cgf, AggValueSlot Dest, - bool ignore) - : CGF(cgf), Builder(CGF.Builder), Dest(Dest), - IgnoreResult(ignore) { + AggExprEmitter(CodeGenFunction &cgf, AggValueSlot Dest) + : CGF(cgf), Builder(CGF.Builder), Dest(Dest) { } //===--------------------------------------------------------------------===// @@ -74,9 +75,11 @@ public: void EmitAggLoadOfLValue(const Expr *E); /// EmitFinalDestCopy - Perform the final copy to DestPtr, if desired. - void EmitFinalDestCopy(const Expr *E, LValue Src, bool Ignore = false); - void EmitFinalDestCopy(const Expr *E, RValue Src, bool Ignore = false, - unsigned Alignment = 0); + void EmitFinalDestCopy(QualType type, const LValue &src); + void EmitFinalDestCopy(QualType type, RValue src, + CharUnits srcAlignment = CharUnits::Zero()); + void EmitCopy(QualType type, const AggValueSlot &dest, + const AggValueSlot &src); void EmitMoveFromReturnSlot(const Expr *E, RValue Src); @@ -119,7 +122,7 @@ public: if (E->getDecl()->getType()->isReferenceType()) { if (CodeGenFunction::ConstantEmission result = CGF.tryEmitAsConstant(E)) { - EmitFinalDestCopy(E, result.getReferenceLValue(CGF, E)); + EmitFinalDestCopy(E->getType(), result.getReferenceLValue(CGF, E)); return; } } @@ -171,7 +174,7 @@ public: void VisitPseudoObjectExpr(PseudoObjectExpr *E) { if (E->isGLValue()) { LValue LV = CGF.EmitPseudoObjectLValue(E); - return EmitFinalDestCopy(E, LV); + return EmitFinalDestCopy(E->getType(), LV); } CGF.EmitPseudoObjectRValue(E, EnsureSlot(E->getType())); @@ -198,7 +201,7 @@ public: /// then loads the result into DestPtr. void AggExprEmitter::EmitAggLoadOfLValue(const Expr *E) { LValue LV = CGF.EmitLValue(E); - EmitFinalDestCopy(E, LV); + EmitFinalDestCopy(E->getType(), LV); } /// \brief True if the given aggregate type requires special GC API calls. @@ -228,7 +231,7 @@ bool AggExprEmitter::TypeRequiresGCollection(QualType T) { /// If nothing interferes, this will cause the result to be emitted /// directly into the return value slot. Otherwise, a final move /// will be performed. -void AggExprEmitter::EmitMoveFromReturnSlot(const Expr *E, RValue Src) { +void AggExprEmitter::EmitMoveFromReturnSlot(const Expr *E, RValue src) { if (shouldUseDestForReturnSlot()) { // Logically, Dest.getAddr() should equal Src.getAggregateAddr(). // The possibility of undef rvalues complicates that a lot, @@ -236,61 +239,58 @@ void AggExprEmitter::EmitMoveFromReturnSlot(const Expr *E, RValue Src) { return; } - // Otherwise, do a final copy, - assert(Dest.getAddr() != Src.getAggregateAddr()); - std::pair TypeInfo = + // Otherwise, copy from there to the destination. + assert(Dest.getAddr() != src.getAggregateAddr()); + std::pair typeInfo = CGF.getContext().getTypeInfoInChars(E->getType()); - CharUnits Alignment = std::min(TypeInfo.second, Dest.getAlignment()); - EmitFinalDestCopy(E, Src, /*Ignore*/ true, Alignment.getQuantity()); + EmitFinalDestCopy(E->getType(), src, typeInfo.second); } /// EmitFinalDestCopy - Perform the final copy to DestPtr, if desired. -void AggExprEmitter::EmitFinalDestCopy(const Expr *E, RValue Src, bool Ignore, - unsigned Alignment) { - assert(Src.isAggregate() && "value must be aggregate value!"); +void AggExprEmitter::EmitFinalDestCopy(QualType type, RValue src, + CharUnits srcAlign) { + assert(src.isAggregate() && "value must be aggregate value!"); + LValue srcLV = CGF.MakeAddrLValue(src.getAggregateAddr(), type, srcAlign); + EmitFinalDestCopy(type, srcLV); +} +/// EmitFinalDestCopy - Perform the final copy to DestPtr, if desired. +void AggExprEmitter::EmitFinalDestCopy(QualType type, const LValue &src) { // If Dest is ignored, then we're evaluating an aggregate expression - // in a context (like an expression statement) that doesn't care - // about the result. C says that an lvalue-to-rvalue conversion is - // performed in these cases; C++ says that it is not. In either - // case, we don't actually need to do anything unless the value is - // volatile. - if (Dest.isIgnored()) { - if (!Src.isVolatileQualified() || - CGF.CGM.getLangOpts().CPlusPlus || - (IgnoreResult && Ignore)) - return; + // in a context that doesn't care about the result. Note that loads + // from volatile l-values force the existence of a non-ignored + // destination. + if (Dest.isIgnored()) + return; - // If the source is volatile, we must read from it; to do that, we need - // some place to put it. - Dest = CGF.CreateAggTemp(E->getType(), "agg.tmp"); - } + AggValueSlot srcAgg = + AggValueSlot::forLValue(src, AggValueSlot::IsDestructed, + needsGC(type), AggValueSlot::IsAliased); + EmitCopy(type, Dest, srcAgg); +} - if (Dest.requiresGCollection()) { - CharUnits size = CGF.getContext().getTypeSizeInChars(E->getType()); - llvm::Type *SizeTy = CGF.ConvertType(CGF.getContext().getSizeType()); - llvm::Value *SizeVal = llvm::ConstantInt::get(SizeTy, size.getQuantity()); +/// Perform a copy from the source into the destination. +/// +/// \param type - the type of the aggregate being copied; qualifiers are +/// ignored +void AggExprEmitter::EmitCopy(QualType type, const AggValueSlot &dest, + const AggValueSlot &src) { + if (dest.requiresGCollection()) { + CharUnits sz = CGF.getContext().getTypeSizeInChars(type); + llvm::Value *size = llvm::ConstantInt::get(CGF.SizeTy, sz.getQuantity()); CGF.CGM.getObjCRuntime().EmitGCMemmoveCollectable(CGF, - Dest.getAddr(), - Src.getAggregateAddr(), - SizeVal); + dest.getAddr(), + src.getAddr(), + size); return; } - // If the result of the assignment is used, copy the LHS there also. - // FIXME: Pass VolatileDest as well. I think we also need to merge volatile - // from the source as well, as we can't eliminate it if either operand - // is volatile, unless copy has volatile for both source and destination.. - CGF.EmitAggregateCopy(Dest.getAddr(), Src.getAggregateAddr(), E->getType(), - Dest.isVolatile()|Src.isVolatileQualified(), - Alignment); -} - -/// EmitFinalDestCopy - Perform the final copy to DestPtr, if desired. -void AggExprEmitter::EmitFinalDestCopy(const Expr *E, LValue Src, bool Ignore) { - assert(Src.isSimple() && "Can't have aggregate bitfield, vector, etc"); - CharUnits Alignment = std::min(Src.getAlignment(), Dest.getAlignment()); - EmitFinalDestCopy(E, Src.asAggregateRValue(), Ignore, Alignment.getQuantity()); + // If the result of the assignment is used, copy the LHS there also. + // It's volatile if either side is. Use the minimum alignment of + // the two sides. + CGF.EmitAggregateCopy(dest.getAddr(), src.getAddr(), type, + dest.isVolatile() || src.isVolatile(), + std::min(dest.getAlignment(), src.getAlignment())); } static QualType GetStdInitializerListElementType(QualType T) { @@ -526,7 +526,7 @@ void AggExprEmitter::VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *E){ } void AggExprEmitter::VisitOpaqueValueExpr(OpaqueValueExpr *e) { - EmitFinalDestCopy(e, CGF.getOpaqueLValueMapping(e)); + EmitFinalDestCopy(e->getType(), CGF.getOpaqueLValueMapping(e)); } void @@ -582,7 +582,15 @@ void AggExprEmitter::VisitCastExpr(CastExpr *E) { "should have been unpacked before we got here"); } - case CK_LValueToRValue: // hope for downstream optimization + case CK_LValueToRValue: + // If we're loading from a volatile type, force the destination + // into existence. + if (E->getSubExpr()->getType().isVolatileQualified()) { + EnsureDest(E->getType()); + return Visit(E->getSubExpr()); + } + // fallthrough + case CK_NoOp: case CK_AtomicToNonAtomic: case CK_NonAtomicToAtomic: @@ -676,7 +684,73 @@ void AggExprEmitter::VisitBinaryOperator(const BinaryOperator *E) { void AggExprEmitter::VisitPointerToDataMemberBinaryOperator( const BinaryOperator *E) { LValue LV = CGF.EmitPointerToDataMemberBinaryExpr(E); - EmitFinalDestCopy(E, LV); + EmitFinalDestCopy(E->getType(), LV); +} + +/// Is the value of the given expression possibly a reference to or +/// into a __block variable? +static bool isBlockVarRef(const Expr *E) { + // Make sure we look through parens. + E = E->IgnoreParens(); + + // Check for a direct reference to a __block variable. + if (const DeclRefExpr *DRE = dyn_cast(E)) { + const VarDecl *var = dyn_cast(DRE->getDecl()); + return (var && var->hasAttr()); + } + + // More complicated stuff. + + // Binary operators. + if (const BinaryOperator *op = dyn_cast(E)) { + // For an assignment or pointer-to-member operation, just care + // about the LHS. + if (op->isAssignmentOp() || op->isPtrMemOp()) + return isBlockVarRef(op->getLHS()); + + // For a comma, just care about the RHS. + if (op->getOpcode() == BO_Comma) + return isBlockVarRef(op->getRHS()); + + // FIXME: pointer arithmetic? + return false; + + // Check both sides of a conditional operator. + } else if (const AbstractConditionalOperator *op + = dyn_cast(E)) { + return isBlockVarRef(op->getTrueExpr()) + || isBlockVarRef(op->getFalseExpr()); + + // OVEs are required to support BinaryConditionalOperators. + } else if (const OpaqueValueExpr *op + = dyn_cast(E)) { + if (const Expr *src = op->getSourceExpr()) + return isBlockVarRef(src); + + // Casts are necessary to get things like (*(int*)&var) = foo(). + // We don't really care about the kind of cast here, except + // we don't want to look through l2r casts, because it's okay + // to get the *value* in a __block variable. + } else if (const CastExpr *cast = dyn_cast(E)) { + if (cast->getCastKind() == CK_LValueToRValue) + return false; + return isBlockVarRef(cast->getSubExpr()); + + // Handle unary operators. Again, just aggressively look through + // it, ignoring the operation. + } else if (const UnaryOperator *uop = dyn_cast(E)) { + return isBlockVarRef(uop->getSubExpr()); + + // Look into the base of a field access. + } else if (const MemberExpr *mem = dyn_cast(E)) { + return isBlockVarRef(mem->getBase()); + + // Look into the base of a subscript. + } else if (const ArraySubscriptExpr *sub = dyn_cast(E)) { + return isBlockVarRef(sub->getBase()); + } + + return false; } void AggExprEmitter::VisitBinAssign(const BinaryOperator *E) { @@ -686,20 +760,26 @@ void AggExprEmitter::VisitBinAssign(const BinaryOperator *E) { E->getRHS()->getType()) && "Invalid assignment"); - if (const DeclRefExpr *DRE = dyn_cast(E->getLHS())) - if (const VarDecl *VD = dyn_cast(DRE->getDecl())) - if (VD->hasAttr() && - E->getRHS()->HasSideEffects(CGF.getContext())) { - // When __block variable on LHS, the RHS must be evaluated first - // as it may change the 'forwarding' field via call to Block_copy. - LValue RHS = CGF.EmitLValue(E->getRHS()); - LValue LHS = CGF.EmitLValue(E->getLHS()); - Dest = AggValueSlot::forLValue(LHS, AggValueSlot::IsDestructed, - needsGC(E->getLHS()->getType()), - AggValueSlot::IsAliased); - EmitFinalDestCopy(E, RHS, true); - return; - } + // If the LHS might be a __block variable, and the RHS can + // potentially cause a block copy, we need to evaluate the RHS first + // so that the assignment goes the right place. + // This is pretty semantically fragile. + if (isBlockVarRef(E->getLHS()) && + E->getRHS()->HasSideEffects(CGF.getContext())) { + // Ensure that we have a destination, and evaluate the RHS into that. + EnsureDest(E->getRHS()->getType()); + Visit(E->getRHS()); + + // Now emit the LHS and copy into it. + LValue LHS = CGF.EmitLValue(E->getLHS()); + + EmitCopy(E->getLHS()->getType(), + AggValueSlot::forLValue(LHS, AggValueSlot::IsDestructed, + needsGC(E->getLHS()->getType()), + AggValueSlot::IsAliased), + Dest); + return; + } LValue LHS = CGF.EmitLValue(E->getLHS()); @@ -708,8 +788,10 @@ void AggExprEmitter::VisitBinAssign(const BinaryOperator *E) { AggValueSlot::forLValue(LHS, AggValueSlot::IsDestructed, needsGC(E->getLHS()->getType()), AggValueSlot::IsAliased); - CGF.EmitAggExpr(E->getRHS(), LHSSlot, false); - EmitFinalDestCopy(E, LHS, true); + CGF.EmitAggExpr(E->getRHS(), LHSSlot); + + // Copy into the destination if the assignment isn't ignored. + EmitFinalDestCopy(E->getType(), LHS); } void AggExprEmitter:: @@ -762,14 +844,14 @@ void AggExprEmitter::VisitVAArgExpr(VAArgExpr *VE) { return; } - EmitFinalDestCopy(VE, CGF.MakeAddrLValue(ArgPtr, VE->getType())); + EmitFinalDestCopy(VE->getType(), CGF.MakeAddrLValue(ArgPtr, VE->getType())); } void AggExprEmitter::VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *E) { // Ensure that we have a slot, but if we already do, remember // whether it was externally destructed. bool wasExternallyDestructed = Dest.isExternallyDestructed(); - Dest = EnsureSlot(E->getType()); + EnsureDest(E->getType()); // We're going to push a destructor if there isn't already one. Dest.setExternallyDestructed(); @@ -904,7 +986,7 @@ void AggExprEmitter::VisitInitListExpr(InitListExpr *E) { llvm::GlobalVariable* GV = new llvm::GlobalVariable(CGF.CGM.getModule(), C->getType(), true, llvm::GlobalValue::InternalLinkage, C, ""); - EmitFinalDestCopy(E, CGF.MakeAddrLValue(GV, E->getType())); + EmitFinalDestCopy(E->getType(), CGF.MakeAddrLValue(GV, E->getType())); return; } #endif @@ -1164,8 +1246,7 @@ static void CheckAggExprForMemSetUse(AggValueSlot &Slot, const Expr *E, /// type. The result is computed into DestPtr. Note that if DestPtr is null, /// the value of the aggregate expression is not needed. If VolatileDest is /// true, DestPtr cannot be 0. -void CodeGenFunction::EmitAggExpr(const Expr *E, AggValueSlot Slot, - bool IgnoreResult) { +void CodeGenFunction::EmitAggExpr(const Expr *E, AggValueSlot Slot) { assert(E && hasAggregateLLVMType(E->getType()) && "Invalid aggregate expression to emit"); assert((Slot.getAddr() != 0 || Slot.isIgnored()) && @@ -1174,7 +1255,7 @@ void CodeGenFunction::EmitAggExpr(const Expr *E, AggValueSlot Slot, // Optimize the slot if possible. CheckAggExprForMemSetUse(Slot, E, *this); - AggExprEmitter(*this, Slot, IgnoreResult).Visit(const_cast(E)); + AggExprEmitter(*this, Slot).Visit(const_cast(E)); } LValue CodeGenFunction::EmitAggExprToLValue(const Expr *E) { @@ -1189,7 +1270,8 @@ LValue CodeGenFunction::EmitAggExprToLValue(const Expr *E) { void CodeGenFunction::EmitAggregateCopy(llvm::Value *DestPtr, llvm::Value *SrcPtr, QualType Ty, - bool isVolatile, unsigned Alignment) { + bool isVolatile, + CharUnits alignment) { assert(!Ty->isAnyComplexType() && "Shouldn't happen for complex"); if (getContext().getLangOpts().CPlusPlus) { @@ -1222,8 +1304,8 @@ void CodeGenFunction::EmitAggregateCopy(llvm::Value *DestPtr, std::pair TypeInfo = getContext().getTypeInfoInChars(Ty); - if (!Alignment) - Alignment = TypeInfo.second.getQuantity(); + if (alignment.isZero()) + alignment = TypeInfo.second; // FIXME: Handle variable sized types. @@ -1281,7 +1363,7 @@ void CodeGenFunction::EmitAggregateCopy(llvm::Value *DestPtr, Builder.CreateMemCpy(DestPtr, SrcPtr, llvm::ConstantInt::get(IntPtrTy, TypeInfo.first.getQuantity()), - Alignment, isVolatile); + alignment.getQuantity(), isVolatile); } void CodeGenFunction::MaybeEmitStdInitializerListCleanup(llvm::Value *loc, diff --git a/lib/CodeGen/CGValue.h b/lib/CodeGen/CGValue.h index dd9208f913..a46f313f1f 100644 --- a/lib/CodeGen/CGValue.h +++ b/lib/CodeGen/CGValue.h @@ -389,7 +389,8 @@ public: return AV; } - static AggValueSlot forLValue(LValue LV, IsDestructed_t isDestructed, + static AggValueSlot forLValue(const LValue &LV, + IsDestructed_t isDestructed, NeedsGCBarriers_t needsGC, IsAliased_t isAliased, IsZeroed_t isZeroed = IsNotZeroed) { diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index ab02431636..a3d1acf591 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/lib/CodeGen/CodeGenFunction.h @@ -1566,6 +1566,7 @@ public: return LValue::MakeAddr(V, T, Alignment, getContext(), CGM.getTBAAInfo(T)); } + LValue MakeNaturalAlignAddrLValue(llvm::Value *V, QualType T) { CharUnits Alignment; if (!T->isIncompleteType()) @@ -1622,8 +1623,8 @@ public: /// /// \param IgnoreResult - True if the resulting value isn't used. RValue EmitAnyExpr(const Expr *E, - AggValueSlot AggSlot = AggValueSlot::ignored(), - bool IgnoreResult = false); + AggValueSlot aggSlot = AggValueSlot::ignored(), + bool ignoreResult = false); // EmitVAListRef - Emit a "reference" to a va_list; this is either the address // or the value of the expression, depending on how va_list is defined. @@ -1649,7 +1650,7 @@ public: /// volatile. void EmitAggregateCopy(llvm::Value *DestPtr, llvm::Value *SrcPtr, QualType EltTy, bool isVolatile=false, - unsigned Alignment = 0); + CharUnits Alignment = CharUnits::Zero()); /// StartBlock - Start new block named N. If insert block is a dummy block /// then reuse it. @@ -2363,7 +2364,7 @@ public: /// EmitAggExpr - Emit the computation of the specified expression /// of aggregate type. The result is computed into the given slot, /// which may be null to indicate that the value is not needed. - void EmitAggExpr(const Expr *E, AggValueSlot AS, bool IgnoreResult = false); + void EmitAggExpr(const Expr *E, AggValueSlot AS); /// EmitAggExprToLValue - Emit the computation of the specified expression of /// aggregate type into a temporary LValue. diff --git a/test/CodeGen/block-byref-aggr.c b/test/CodeGen/block-byref-aggr.c index 3027df0486..eb342b856e 100644 --- a/test/CodeGen/block-byref-aggr.c +++ b/test/CodeGen/block-byref-aggr.c @@ -1,17 +1,66 @@ // RUN: %clang_cc1 %s -emit-llvm -o - -fblocks -triple x86_64-apple-darwin10 | FileCheck %s -// rdar://9309454 -typedef struct { int v; } RetType; +// CHECK: [[AGG:%.*]] = type { i32 } +typedef struct { int v; } Agg; +Agg makeAgg(void); -RetType func(); +// When assigning into a __block variable, ensure that we compute that +// address *after* evaluating the RHS when the RHS has the capacity to +// cause a block copy. rdar://9309454 +void test0() { + __block Agg a = {100}; -int main () { - __attribute__((__blocks__(byref))) RetType a = {100}; + a = makeAgg(); +} +// CHECK: define void @test0() +// CHECK: [[A:%.*]] = alloca [[BYREF:%.*]], align 8 +// CHECK-NEXT: [[TEMP:%.*]] = alloca [[AGG]], align 4 +// CHECK: [[RESULT:%.*]] = call i32 @makeAgg() +// CHECK-NEXT: [[T0:%.*]] = getelementptr [[AGG]]* [[TEMP]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[RESULT]], i32* [[T0]] +// Check that we properly assign into the forwarding pointer. +// CHECK-NEXT: [[A_FORWARDING:%.*]] = getelementptr inbounds [[BYREF]]* [[A]], i32 0, i32 1 +// CHECK-NEXT: [[T0:%.*]] = load [[BYREF]]** [[A_FORWARDING]] +// CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds [[BYREF]]* [[T0]], i32 0, i32 4 +// CHECK-NEXT: [[T2:%.*]] = bitcast [[AGG]]* [[T1]] to i8* +// CHECK-NEXT: [[T3:%.*]] = bitcast [[AGG]]* [[TEMP]] to i8* +// CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* [[T2]], i8* [[T3]], i64 4, i32 4, i1 false) +// Verify that there's nothing else significant in the function. +// CHECK-NEXT: [[T0:%.*]] = bitcast [[BYREF]]* [[A]] to i8* +// CHECK-NEXT: call void @_Block_object_dispose(i8* [[T0]], i32 8) +// CHECK-NEXT: ret void - a = func(); +// When chaining assignments into __block variables, make sure we +// propagate the actual value into the outer variable. +// rdar://11757470 +void test1() { + __block Agg a, b; + a = b = makeAgg(); } -// CHECK: [[C1:%.*]] = call i32 (...)* @func() -// CHECK-NEXT: [[CO:%.*]] = getelementptr -// CHECK-NEXT: store i32 [[C1]], i32* [[CO]] -// CHECK-NEXT: [[FORWARDING:%.*]] = getelementptr inbounds [[BR:%.*]]* [[A:%.*]], i32 0, i32 1 -// CHECK-NEXT: [[O:%.*]] = load [[BR]]** [[FORWARDING]] +// CHECK: define void @test1() +// CHECK: [[A:%.*]] = alloca [[A_BYREF:%.*]], align 8 +// CHECK-NEXT: [[B:%.*]] = alloca [[B_BYREF:%.*]], align 8 +// CHECK-NEXT: [[TEMP:%.*]] = alloca [[AGG]], align 4 +// CHECK: [[RESULT:%.*]] = call i32 @makeAgg() +// CHECK-NEXT: [[T0:%.*]] = getelementptr [[AGG]]* [[TEMP]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[RESULT]], i32* [[T0]] +// Check that we properly assign into the forwarding pointer, first for b: +// CHECK-NEXT: [[B_FORWARDING:%.*]] = getelementptr inbounds [[B_BYREF]]* [[B]], i32 0, i32 1 +// CHECK-NEXT: [[T0:%.*]] = load [[B_BYREF]]** [[B_FORWARDING]] +// CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds [[B_BYREF]]* [[T0]], i32 0, i32 4 +// CHECK-NEXT: [[T2:%.*]] = bitcast [[AGG]]* [[T1]] to i8* +// CHECK-NEXT: [[T3:%.*]] = bitcast [[AGG]]* [[TEMP]] to i8* +// CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* [[T2]], i8* [[T3]], i64 4, i32 4, i1 false) +// Then for 'a': +// CHECK-NEXT: [[A_FORWARDING:%.*]] = getelementptr inbounds [[A_BYREF]]* [[A]], i32 0, i32 1 +// CHECK-NEXT: [[T0:%.*]] = load [[A_BYREF]]** [[A_FORWARDING]] +// CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds [[A_BYREF]]* [[T0]], i32 0, i32 4 +// CHECK-NEXT: [[T2:%.*]] = bitcast [[AGG]]* [[T1]] to i8* +// CHECK-NEXT: [[T3:%.*]] = bitcast [[AGG]]* [[TEMP]] to i8* +// CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* [[T2]], i8* [[T3]], i64 4, i32 4, i1 false) +// Verify that there's nothing else significant in the function. +// CHECK-NEXT: [[T0:%.*]] = bitcast [[B_BYREF]]* [[B]] to i8* +// CHECK-NEXT: call void @_Block_object_dispose(i8* [[T0]], i32 8) +// CHECK-NEXT: [[T0:%.*]] = bitcast [[A_BYREF]]* [[A]] to i8* +// CHECK-NEXT: call void @_Block_object_dispose(i8* [[T0]], i32 8) +// CHECK-NEXT: ret void -- 2.40.0