Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -8576,6 +8576,8 @@ SourceLocation RBracLoc, MultiExprArg Args); + void ActOnFinishObjCMessageSend(ExprResult Result); + ExprResult BuildObjCBridgedCast(SourceLocation LParenLoc, ObjCBridgeCastKind Kind, SourceLocation BridgeKeywordLoc, Index: clang/lib/CodeGen/CGCall.h =================================================================== --- clang/lib/CodeGen/CGCall.h +++ clang/lib/CodeGen/CGCall.h @@ -361,20 +361,23 @@ /// ReturnValueSlot - Contains the address where the return value of a /// function can be stored, and whether the address is volatile or not. class ReturnValueSlot { - llvm::PointerIntPair Value; + llvm::PointerIntPair Value; CharUnits Alignment; // Return value slot flags enum Flags { IS_VOLATILE = 0x1, IS_UNUSED = 0x2, + REQUIRES_DESTRUCTION = 0x4, }; public: ReturnValueSlot() {} - ReturnValueSlot(Address Addr, bool IsVolatile, bool IsUnused = false) + ReturnValueSlot(Address Addr, bool IsVolatile, bool IsUnused = false, + bool RequiresDestruction = false) : Value(Addr.isValid() ? Addr.getPointer() : nullptr, - (IsVolatile ? IS_VOLATILE : 0) | (IsUnused ? IS_UNUSED : 0)), + (IsVolatile ? IS_VOLATILE : 0) | (IsUnused ? IS_UNUSED : 0) | + (RequiresDestruction ? REQUIRES_DESTRUCTION : 0)), Alignment(Addr.isValid() ? Addr.getAlignment() : CharUnits::Zero()) {} bool isNull() const { return !getValue().isValid(); } @@ -382,6 +385,9 @@ bool isVolatile() const { return Value.getInt() & IS_VOLATILE; } Address getValue() const { return Address(Value.getPointer(), Alignment); } bool isUnused() const { return Value.getInt() & IS_UNUSED; } + bool requiresDestruction() const { + return Value.getInt() & REQUIRES_DESTRUCTION; + } }; } // end namespace CodeGen Index: clang/lib/CodeGen/CGExpr.cpp =================================================================== --- clang/lib/CodeGen/CGExpr.cpp +++ clang/lib/CodeGen/CGExpr.cpp @@ -4487,35 +4487,48 @@ // Expression Emission //===--------------------------------------------------------------------===// -RValue CodeGenFunction::EmitCallExpr(const CallExpr *E, - ReturnValueSlot ReturnValue) { +static RValue emitCallExpr(const CallExpr *E, ReturnValueSlot ReturnValue, + CodeGenFunction &CGF) { // Builtins never have block type. if (E->getCallee()->getType()->isBlockPointerType()) - return EmitBlockCallExpr(E, ReturnValue); + return CGF.EmitBlockCallExpr(E, ReturnValue); if (const auto *CE = dyn_cast(E)) - return EmitCXXMemberCallExpr(CE, ReturnValue); + return CGF.EmitCXXMemberCallExpr(CE, ReturnValue); if (const auto *CE = dyn_cast(E)) - return EmitCUDAKernelCallExpr(CE, ReturnValue); + return CGF.EmitCUDAKernelCallExpr(CE, ReturnValue); if (const auto *CE = dyn_cast(E)) if (const CXXMethodDecl *MD = dyn_cast_or_null(CE->getCalleeDecl())) - return EmitCXXOperatorMemberCallExpr(CE, MD, ReturnValue); + return CGF.EmitCXXOperatorMemberCallExpr(CE, MD, ReturnValue); - CGCallee callee = EmitCallee(E->getCallee()); + CGCallee callee = CGF.EmitCallee(E->getCallee()); - if (callee.isBuiltin()) { - return EmitBuiltinExpr(callee.getBuiltinDecl(), callee.getBuiltinID(), - E, ReturnValue); - } + if (callee.isBuiltin()) + return CGF.EmitBuiltinExpr(callee.getBuiltinDecl(), callee.getBuiltinID(), + E, ReturnValue); + + if (callee.isPseudoDestructor()) + return CGF.EmitCXXPseudoDestructorExpr(callee.getPseudoDestructorExpr()); + + return CGF.EmitCall(E->getCallee()->getType(), callee, E, ReturnValue); +} + +RValue CodeGenFunction::EmitCallExpr(const CallExpr *E, + ReturnValueSlot ReturnValue) { + RValue R = emitCallExpr(E, ReturnValue, *this); - if (callee.isPseudoDestructor()) { - return EmitCXXPseudoDestructorExpr(callee.getPseudoDestructorExpr()); + if (ReturnValue.requiresDestruction()) { + assert(E->getType().isDestructedType() == + QualType::DK_nontrivial_c_struct && + "non-trivial C struct expected"); + pushDestroy(QualType::DK_nontrivial_c_struct, R.getAggregateAddress(), + E->getType()); } - return EmitCall(E->getCallee()->getType(), callee, E, ReturnValue); + return R; } /// Emit a CallExpr without considering whether it might be a subclass. @@ -4637,7 +4650,10 @@ } LValue CodeGenFunction::EmitCallExprLValue(const CallExpr *E) { - RValue RV = EmitCallExpr(E); + bool RequiresDestruction = + E->getType().isDestructedType() == QualType::DK_nontrivial_c_struct; + RValue RV = EmitCallExpr(E, ReturnValueSlot(Address::invalid(), false, false, + RequiresDestruction)); if (!RV.isScalar()) return MakeAddrLValue(RV.getAggregateAddress(), E->getType(), @@ -4688,7 +4704,11 @@ } LValue CodeGenFunction::EmitObjCMessageExprLValue(const ObjCMessageExpr *E) { - RValue RV = EmitObjCMessageExpr(E); + bool RequiresDestruction = + E->getType().isDestructedType() == QualType::DK_nontrivial_c_struct; + RValue RV = + EmitObjCMessageExpr(E, ReturnValueSlot(Address::invalid(), false, false, + RequiresDestruction)); if (!RV.isScalar()) return MakeAddrLValue(RV.getAggregateAddress(), E->getType(), Index: clang/lib/CodeGen/CGExprAgg.cpp =================================================================== --- clang/lib/CodeGen/CGExprAgg.cpp +++ clang/lib/CodeGen/CGExprAgg.cpp @@ -245,7 +245,7 @@ const Expr *E, llvm::function_ref EmitCall) { QualType RetTy = E->getType(); bool RequiresDestruction = - Dest.isIgnored() && + !Dest.isExternallyDestructed() && RetTy.isDestructedType() == QualType::DK_nontrivial_c_struct; // If it makes no observable difference, save a memcpy + temporary. @@ -282,11 +282,8 @@ } } - RValue Src = - EmitCall(ReturnValueSlot(RetAddr, Dest.isVolatile(), IsResultUnused)); - - if (RequiresDestruction) - CGF.pushDestroy(RetTy.isDestructedType(), Src.getAggregateAddress(), RetTy); + RValue Src = EmitCall(ReturnValueSlot(RetAddr, Dest.isVolatile(), + IsResultUnused, RequiresDestruction)); if (!UseTemp) return; @@ -811,7 +808,16 @@ // into existence. if (E->getSubExpr()->getType().isVolatileQualified()) { EnsureDest(E->getType()); - return Visit(E->getSubExpr()); + Visit(E->getSubExpr()); + + if (!Dest.isExternallyDestructed() && + E->getType().isDestructedType() == QualType::DK_nontrivial_c_struct) { + CGF.pushDestroy(QualType::DK_nontrivial_c_struct, Dest.getAddress(), + E->getType()); + Dest.setExternallyDestructed(); + } + + return; } LLVM_FALLTHROUGH; Index: clang/lib/CodeGen/CGObjC.cpp =================================================================== --- clang/lib/CodeGen/CGObjC.cpp +++ clang/lib/CodeGen/CGObjC.cpp @@ -623,6 +623,14 @@ } } + if (Return.requiresDestruction()) { + assert(E->getType().isDestructedType() == + QualType::DK_nontrivial_c_struct && + "non-trivial C struct expected"); + pushDestroy(QualType::DK_nontrivial_c_struct, result.getAggregateAddress(), + E->getType()); + } + // For delegate init calls in ARC, implicitly store the result of // the call back into self. This takes ownership of the value. if (isDelegateInit) { Index: clang/lib/Parse/ParseObjc.cpp =================================================================== --- clang/lib/Parse/ParseObjc.cpp +++ clang/lib/Parse/ParseObjc.cpp @@ -3298,14 +3298,19 @@ } Selector Sel = PP.getSelectorTable().getSelector(nKeys, &KeyIdents[0]); + ExprResult Result; if (SuperLoc.isValid()) - return Actions.ActOnSuperMessage(getCurScope(), SuperLoc, Sel, - LBracLoc, KeyLocs, RBracLoc, KeyExprs); + Result = Actions.ActOnSuperMessage(getCurScope(), SuperLoc, Sel, + LBracLoc, KeyLocs, RBracLoc, KeyExprs); else if (ReceiverType) - return Actions.ActOnClassMessage(getCurScope(), ReceiverType, Sel, - LBracLoc, KeyLocs, RBracLoc, KeyExprs); - return Actions.ActOnInstanceMessage(getCurScope(), ReceiverExpr, Sel, - LBracLoc, KeyLocs, RBracLoc, KeyExprs); + Result = Actions.ActOnClassMessage(getCurScope(), ReceiverType, Sel, + LBracLoc, KeyLocs, RBracLoc, KeyExprs); + else + Result = Actions.ActOnInstanceMessage(getCurScope(), ReceiverExpr, Sel, + LBracLoc, KeyLocs, RBracLoc, + KeyExprs); + Actions.ActOnFinishObjCMessageSend(Result); + return Result; } ExprResult Parser::ParseObjCStringLiteral(SourceLocation AtLoc) { Index: clang/lib/Sema/SemaExpr.cpp =================================================================== --- clang/lib/Sema/SemaExpr.cpp +++ clang/lib/Sema/SemaExpr.cpp @@ -635,6 +635,10 @@ if (E->getType().getObjCLifetime() == Qualifiers::OCL_Weak) Cleanup.setExprNeedsCleanups(true); + if (E->getType().isVolatileQualified() && + E->getType().isDestructedType() == QualType::DK_nontrivial_c_struct) + Cleanup.setExprNeedsCleanups(true); + // C++ [conv.lval]p3: // If T is cv std::nullptr_t, the result is a null pointer constant. CastKind CK = T->isNullPtrType() ? CK_NullToPointer : CK_LValueToRValue; @@ -5574,6 +5578,10 @@ } } + if (Call.get()->getType().isDestructedType() == + QualType::DK_nontrivial_c_struct) + Cleanup.setExprNeedsCleanups(true); + return Call; } Index: clang/lib/Sema/SemaExprObjC.cpp =================================================================== --- clang/lib/Sema/SemaExprObjC.cpp +++ clang/lib/Sema/SemaExprObjC.cpp @@ -3238,6 +3238,12 @@ RBracLoc, Args); } +void Sema::ActOnFinishObjCMessageSend(ExprResult Result) { + if (Result.get()->getType().isDestructedType() == + QualType::DK_nontrivial_c_struct) + Cleanup.setExprNeedsCleanups(true); +} + enum ARCConversionTypeClass { /// int, void, struct A ACTC_none, Index: clang/test/CodeGenObjC/arc.m =================================================================== --- clang/test/CodeGenObjC/arc.m +++ clang/test/CodeGenObjC/arc.m @@ -1536,12 +1536,13 @@ // CHECK-LABEL: define void @test71 void test71(void) { - // FIXME: It would be nice if the __destructor_8_s40 for the first call (and - // the following lifetime.end) came before the second call. - // // CHECK: %[[T:[^ ]+]] = bitcast %struct.AggDtor* %[[TMP1:[^ ]+]] to i8* // CHECK: call void @llvm.lifetime.start.p0i8({{[^,]+}}, i8* %[[T]]) // CHECK: call void @getAggDtor(%struct.AggDtor* sret %[[TMP1]]) + // CHECK: %[[T:[^ ]+]] = bitcast %struct.AggDtor* %[[TMP1]] to i8** + // CHECK: call void @__destructor_8_s40(i8** %[[T]]) + // CHECK: %[[T:[^ ]+]] = bitcast %struct.AggDtor* %[[TMP1:[^ ]+]] to i8* + // CHECK: call void @llvm.lifetime.end.p0i8({{[^,]+}}, i8* %[[T]]) // CHECK: %[[T:[^ ]+]] = bitcast %struct.AggDtor* %[[TMP2:[^ ]+]] to i8* // CHECK: call void @llvm.lifetime.start.p0i8({{[^,]+}}, i8* %[[T]]) // CHECK: call void @getAggDtor(%struct.AggDtor* sret %[[TMP2]]) @@ -1549,10 +1550,6 @@ // CHECK: call void @__destructor_8_s40(i8** %[[T]]) // CHECK: %[[T:[^ ]+]] = bitcast %struct.AggDtor* %[[TMP2:[^ ]+]] to i8* // CHECK: call void @llvm.lifetime.end.p0i8({{[^,]+}}, i8* %[[T]]) - // CHECK: %[[T:[^ ]+]] = bitcast %struct.AggDtor* %[[TMP1]] to i8** - // CHECK: call void @__destructor_8_s40(i8** %[[T]]) - // CHECK: %[[T:[^ ]+]] = bitcast %struct.AggDtor* %[[TMP1:[^ ]+]] to i8* - // CHECK: call void @llvm.lifetime.end.p0i8({{[^,]+}}, i8* %[[T]]) getAggDtor(); getAggDtor(); } Index: clang/test/CodeGenObjC/strong-in-c-struct.m =================================================================== --- clang/test/CodeGenObjC/strong-in-c-struct.m +++ clang/test/CodeGenObjC/strong-in-c-struct.m @@ -90,6 +90,13 @@ void calleeStrongSmall(StrongSmall); void func(Strong *); +@interface C +-(StrongSmall)getStrongSmall; ++(StrongSmall)getStrongSmallClass; +@end + +id g0; + // CHECK: %[[STRUCT_STRONGOUTER:.*]] = type { %[[STRUCT_STRONG:.*]], i8*, double } // CHECK: %[[STRUCT_STRONG]] = type { %[[STRUCT_TRIVIAL:.*]], i8* } // CHECK: %[[STRUCT_TRIVIAL]] = type { [4 x i32] } @@ -477,6 +484,18 @@ getStrongSmall(); } +// CHECK: define void @test_destructor_ignored_result2(%{{.*}}* %[[C:.*]]) +// CHECK: %[[TMP:.*]] = alloca %[[STRUCT_STRONGSMALL]], align 8 +// CHECK: %[[CALL:.*]] = call [2 x i64]{{.*}}@objc_msgSend +// CHECK: %[[V5:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[TMP]] to [2 x i64]* +// CHECK: store [2 x i64] %[[CALL]], [2 x i64]* %[[V5]], align 8 +// CHECK: %[[V6:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[TMP]] to i8** +// CHECK: call void @__destructor_8_s8(i8** %[[V6]]) + +void test_destructor_ignored_result2(C *c) { + [c getStrongSmall]; +} + // CHECK: define void @test_copy_constructor_StrongBlock( // CHECK: call void @__copy_constructor_8_8_sb0( // CHECK: call void @__destructor_8_sb0( @@ -521,7 +540,9 @@ // CHECK: define void @test_copy_constructor_StrongVolatile0( // CHECK: call void @__copy_constructor_8_8_t0w4_sv8( +// CHECK-NOT: call // CHECK: call void @__destructor_8_sv8( +// CHECK-NOT: call // CHECK: define linkonce_odr hidden void @__copy_constructor_8_8_t0w4_sv8( // CHECK: %[[V8:.*]] = load volatile i8*, i8** %{{.*}}, align 8 @@ -718,4 +739,62 @@ VolatileArray t = *a; } +// CHECK: define void @test_member_access( +// CHECK: %[[TMP:.*]] = alloca %[[STRUCT_STRONGSMALL]], +// CHECK: %[[V3:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[TMP]] to i8** +// CHECK: call void @__destructor_8_s8(i8** %[[V3]]) +// CHECK: call void @func( + +void test_member_access(void) { + g0 = getStrongSmall().f1; + func(0); +} + +// CHECK: define void @test_member_access2(%{{.*}}* %[[C:.*]]) +// CHECK: %[[COERCE:.*]] = alloca %[[STRUCT_STRONGSMALL]], align 8 +// CHECK: %[[V8:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[COERCE]] to i8** +// CHECK: call void @__destructor_8_s8(i8** %[[V8]]) +// CHECK: call void @func( + +void test_member_access2(C *c) { + g0 = [c getStrongSmall].f1; + func(0); +} + +// CHECK: define void @test_member_access3( +// CHECK: %[[COERCE:.*]] = alloca %[[STRUCT_STRONGSMALL]], align 8 +// CHECK: %[[V8:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[COERCE]] to i8** +// CHECK: call void @__destructor_8_s8(i8** %[[V8]]) +// CHECK: call void @func( + +void test_member_access3(void) { + g0 = [C getStrongSmallClass].f1; + func(0); +} + +// CHECK: define void @test_member_access4() +// CHECK: %[[COERCE:.*]] = alloca %[[STRUCT_STRONGSMALL]], align 8 +// CHECK: %[[V5:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[COERCE]] to i8** +// CHECK: call void @__destructor_8_s8(i8** %[[V5]]) +// CHECK: call void @func( + +void test_member_access4(void) { + g0 = ^{ StrongSmall s; return s; }().f1; + func(0); +} + +// CHECK: define void @test_volatile_variable_reference( +// CHECK: %[[AGG_TMP_ENSURED:.*]] = alloca %[[STRUCT_STRONGSMALL]], +// CHECK: %[[V1:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[AGG_TMP_ENSURED]] to i8** +// CHECK: %[[V2:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %{{.*}} to i8** +// CHECK: call void @__copy_constructor_8_8_tv0w32_sv8(i8** %[[V1]], i8** %[[V2]]) +// CHECK: %[[V3:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[AGG_TMP_ENSURED]] to i8** +// CHECK: call void @__destructor_8_s8(i8** %[[V3]]) +// CHECK: call void @func( + +void test_volatile_variable_reference(volatile StrongSmall *a) { + (void)*a; + func(0); +} + #endif /* USESTRUCT */