Index: include/clang/AST/Decl.h =================================================================== --- include/clang/AST/Decl.h +++ include/clang/AST/Decl.h @@ -3537,6 +3537,7 @@ bool NonTrivialToPrimitiveDefaultInitialize : 1; bool NonTrivialToPrimitiveCopy : 1; bool NonTrivialToPrimitiveDestroy : 1; + bool PassedIndirectly : 1; protected: RecordDecl(Kind DK, TagKind TK, const ASTContext &C, DeclContext *DC, @@ -3621,6 +3622,14 @@ NonTrivialToPrimitiveDestroy = true; } + bool isPassedIndirectly() const { + return PassedIndirectly; + } + + void setPassedIndirectly() { + PassedIndirectly = true; + } + /// \brief Determines whether this declaration represents the /// injected class name. /// Index: include/clang/AST/Type.h =================================================================== --- include/clang/AST/Type.h +++ include/clang/AST/Type.h @@ -1097,6 +1097,10 @@ /// with the ARC __strong qualifier. PDIK_ARCStrong, + /// The type is an Objective-C retainable pointer type that is qualified + /// with the ARC __weak qualifier. + PDIK_ARCWeak, + /// The type is a struct containing a field whose type is not PCK_Trivial. PDIK_Struct }; @@ -1124,6 +1128,10 @@ /// with the ARC __strong qualifier. PCK_ARCStrong, + /// The type is an Objective-C retainable pointer type that is qualified + /// with the ARC __weak qualifier. + PCK_ARCWeak, + /// The type is a struct containing a field whose type is neither /// PCK_Trivial nor PCK_VolatileTrivial. /// Note that a C++ struct type does not necessarily match this; C++ copying @@ -1146,6 +1154,14 @@ /// source object is placed in an uninitialized state. PrimitiveCopyKind isNonTrivialToPrimitiveDestructiveMove() const; + enum ArgPassingKind { + APK_Direct, + APK_ARCWeak, + APK_Struct + }; + + ArgPassingKind isPassedIndirectly() const; + enum DestructionKind { DK_none, DK_cxx_destructor, Index: lib/AST/Decl.cpp =================================================================== --- lib/AST/Decl.cpp +++ lib/AST/Decl.cpp @@ -3931,7 +3931,8 @@ HasObjectMember(false), HasVolatileMember(false), LoadedFieldsFromExternalStorage(false), NonTrivialToPrimitiveDefaultInitialize(false), - NonTrivialToPrimitiveCopy(false), NonTrivialToPrimitiveDestroy(false) { + NonTrivialToPrimitiveCopy(false), NonTrivialToPrimitiveDestroy(false), + PassedIndirectly(false) { assert(classof(static_cast(this)) && "Invalid Kind!"); } Index: lib/AST/Type.cpp =================================================================== --- lib/AST/Type.cpp +++ lib/AST/Type.cpp @@ -2215,11 +2215,14 @@ if (RT->getDecl()->isNonTrivialToPrimitiveDefaultInitialize()) return PDIK_Struct; - Qualifiers::ObjCLifetime Lifetime = getQualifiers().getObjCLifetime(); - if (Lifetime == Qualifiers::OCL_Strong) + switch (getQualifiers().getObjCLifetime()) { + case Qualifiers::OCL_Strong: return PDIK_ARCStrong; - - return PDIK_Trivial; + case Qualifiers::OCL_Weak: + return PDIK_ARCWeak; + default: + return PDIK_Trivial; + } } QualType::PrimitiveCopyKind QualType::isNonTrivialToPrimitiveCopy() const { @@ -2229,10 +2232,14 @@ return PCK_Struct; Qualifiers Qs = getQualifiers(); - if (Qs.getObjCLifetime() == Qualifiers::OCL_Strong) + switch (Qs.getObjCLifetime()) { + case Qualifiers::OCL_Strong: return PCK_ARCStrong; - - return Qs.hasVolatile() ? PCK_VolatileTrivial : PCK_Trivial; + case Qualifiers::OCL_Weak: + return PCK_ARCWeak; + default: + return Qs.hasVolatile() ? PCK_VolatileTrivial : PCK_Trivial; + } } QualType::PrimitiveCopyKind @@ -2240,6 +2247,18 @@ return isNonTrivialToPrimitiveCopy(); } +QualType::ArgPassingKind QualType::isPassedIndirectly() const { + if (const auto *RT = + getTypePtr()->getBaseElementTypeUnsafe()->getAs()) + if (RT->getDecl()->isPassedIndirectly()) + return APK_Struct; + + if (getQualifiers().getObjCLifetime() == Qualifiers::OCL_Weak) + return APK_ARCWeak; + + return APK_Direct; +} + bool Type::isLiteralType(const ASTContext &Ctx) const { if (isDependentType()) return false; Index: lib/CodeGen/CGBlocks.cpp =================================================================== --- lib/CodeGen/CGBlocks.cpp +++ lib/CodeGen/CGBlocks.cpp @@ -1565,6 +1565,9 @@ case QualType::PCK_Struct: return std::make_pair(BlockCaptureEntityKind::NonTrivialCStruct, BlockFieldFlags()); + case QualType::PCK_ARCWeak: + // We need to register __weak direct captures with the runtime. + return std::make_pair(BlockCaptureEntityKind::ARCWeak, Flags); case QualType::PCK_ARCStrong: // We need to retain the copied value for __strong direct captures. // If it's a block pointer, we have to copy the block and assign that to @@ -1582,10 +1585,6 @@ // Special rules for ARC captures: Qualifiers QS = T.getQualifiers(); - // We need to register __weak direct captures with the runtime. - if (QS.getObjCLifetime() == Qualifiers::OCL_Weak) - return std::make_pair(BlockCaptureEntityKind::ARCWeak, Flags); - // Non-ARC captures of retainable pointers are strong and // therefore require a call to _Block_object_assign. if (!QS.getObjCLifetime() && !LangOpts.ObjCAutoRefCount) Index: lib/CodeGen/CGNonTrivialStruct.cpp =================================================================== --- lib/CodeGen/CGNonTrivialStruct.cpp +++ lib/CodeGen/CGNonTrivialStruct.cpp @@ -77,6 +77,8 @@ switch (PDIK) { case QualType::PDIK_ARCStrong: return asDerived().visitARCStrong(FT, std::forward(Args)...); + case QualType::PDIK_ARCWeak: + return asDerived().visitARCWeak(FT, std::forward(Args)...); case QualType::PDIK_Struct: return asDerived().visitStruct(FT, std::forward(Args)...); case QualType::PDIK_Trivial: @@ -108,6 +110,8 @@ switch (PCK) { case QualType::PCK_ARCStrong: return asDerived().visitARCStrong(FT, std::forward(Args)...); + case QualType::PCK_ARCWeak: + return asDerived().visitARCWeak(FT, std::forward(Args)...); case QualType::PCK_Struct: return asDerived().visitStruct(FT, std::forward(Args)...); case QualType::PCK_Trivial: @@ -141,11 +145,6 @@ template void visitTrivial(Ts... Args) {} - template void visitARCWeak(Ts... Args) { - // FIXME: remove this when visitARCWeak is implemented in the subclasses. - llvm_unreachable("weak field is not expected"); - } - template void visitCXXDestructor(Ts... Args) { llvm_unreachable("field of a C++ struct type is not expected"); } @@ -245,6 +244,13 @@ appendStr(getVolatileOffsetStr(FT.isVolatileQualified(), FieldOffset)); } + void visitARCWeak(QualType FT, const FieldDecl *FD, + CharUnits CurStructOffset) { + appendStr("_w"); + CharUnits FieldOffset = CurStructOffset + asDerived().getFieldOffset(FD); + appendStr(getVolatileOffsetStr(FT.isVolatileQualified(), FieldOffset)); + } + void visitStruct(QualType QT, const FieldDecl *FD, CharUnits CurStructOffset) { CharUnits FieldOffset = CurStructOffset + asDerived().getFieldOffset(FD); @@ -615,6 +621,12 @@ *CGF, getAddrWithOffset(Addrs[DstIdx], CurStackOffset, FD), QT); } + void visitARCWeak(QualType QT, const FieldDecl *FD, CharUnits CurStackOffset, + std::array Addrs) { + CGF->destroyARCWeak( + *CGF, getAddrWithOffset(Addrs[DstIdx], CurStackOffset, FD), QT); + } + void callSpecialFunction(QualType FT, CharUnits Offset, std::array Addrs) { CGF->callCStructDestructor( @@ -636,6 +648,12 @@ getAddrWithOffset(Addrs[DstIdx], CurStackOffset, FD), QT); } + void visitARCWeak(QualType QT, const FieldDecl *FD, CharUnits CurStackOffset, + std::array Addrs) { + CGF->EmitNullInitialization( + getAddrWithOffset(Addrs[DstIdx], CurStackOffset, FD), QT); + } + template void visitArray(FieldKind FK, QualType QT, const FieldDecl *FD, CharUnits CurStackOffset, std::array Addrs) { @@ -678,6 +696,14 @@ llvm::Value *Val = CGF->EmitARCRetain(QT, SrcVal); CGF->EmitStoreOfScalar(Val, CGF->MakeAddrLValue(Addrs[DstIdx], QT), true); } + + void visitARCWeak(QualType QT, const FieldDecl *FD, CharUnits CurStackOffset, + std::array Addrs) { + Addrs[DstIdx] = getAddrWithOffset(Addrs[DstIdx], CurStackOffset, FD); + Addrs[SrcIdx] = getAddrWithOffset(Addrs[SrcIdx], CurStackOffset, FD); + CGF->EmitARCCopyWeak(Addrs[DstIdx], Addrs[SrcIdx]); + } + void callSpecialFunction(QualType FT, CharUnits Offset, std::array Addrs) { CGF->callCStructCopyConstructor(CGF->MakeAddrLValue(Addrs[DstIdx], FT), @@ -700,6 +726,14 @@ CGF->EmitStoreOfScalar(SrcVal, CGF->MakeAddrLValue(Addrs[DstIdx], QT), /* isInitialization */ true); } + + void visitARCWeak(QualType QT, const FieldDecl *FD, CharUnits CurStackOffset, + std::array Addrs) { + Addrs[DstIdx] = getAddrWithOffset(Addrs[DstIdx], CurStackOffset, FD); + Addrs[SrcIdx] = getAddrWithOffset(Addrs[SrcIdx], CurStackOffset, FD); + CGF->EmitARCMoveWeak(Addrs[DstIdx], Addrs[SrcIdx]); + } + void callSpecialFunction(QualType FT, CharUnits Offset, std::array Addrs) { CGF->callCStructMoveConstructor(CGF->MakeAddrLValue(Addrs[DstIdx], FT), @@ -720,6 +754,16 @@ CGF->EmitARCStoreStrong(CGF->MakeAddrLValue(Addrs[DstIdx], QT), SrcVal, false); } + + void visitARCWeak(QualType QT, const FieldDecl *FD, CharUnits CurStackOffset, + std::array Addrs) { + Addrs[DstIdx] = getAddrWithOffset(Addrs[DstIdx], CurStackOffset, FD); + Addrs[SrcIdx] = getAddrWithOffset(Addrs[SrcIdx], CurStackOffset, FD); + llvm::Value *Object = CGF->EmitARCLoadWeakRetained(Addrs[SrcIdx]); + Object = CGF->EmitObjCConsumeObject(QT, Object); + CGF->EmitARCStoreWeak(Addrs[DstIdx], Object, true); + } + void callSpecialFunction(QualType FT, CharUnits Offset, std::array Addrs) { CGF->callCStructCopyAssignmentOperator( @@ -747,6 +791,16 @@ CGF->EmitARCRelease(DstVal, ARCImpreciseLifetime); } + void visitARCWeak(QualType QT, const FieldDecl *FD, CharUnits CurStackOffset, + std::array Addrs) { + Addrs[DstIdx] = getAddrWithOffset(Addrs[DstIdx], CurStackOffset, FD); + Addrs[SrcIdx] = getAddrWithOffset(Addrs[SrcIdx], CurStackOffset, FD); + llvm::Value *Object = CGF->EmitARCLoadWeakRetained(Addrs[SrcIdx]); + Object = CGF->EmitObjCConsumeObject(QT, Object); + CGF->EmitARCStoreWeak(Addrs[DstIdx], Object, false); + CGF->EmitARCDestroyWeak(Addrs[SrcIdx]); + } + void callSpecialFunction(QualType FT, CharUnits Offset, std::array Addrs) { CGF->callCStructMoveAssignmentOperator( Index: lib/CodeGen/TargetInfo.cpp =================================================================== --- lib/CodeGen/TargetInfo.cpp +++ lib/CodeGen/TargetInfo.cpp @@ -1322,6 +1322,9 @@ if (RetTy->isVoidType()) return ABIArgInfo::getIgnore(); + if (RetTy.isPassedIndirectly() == QualType::APK_Struct) + return getNaturalAlignIndirect(RetTy); + const Type *Base = nullptr; uint64_t NumElts = 0; if ((State.CC == llvm::CallingConv::X86_VectorCall || @@ -1576,6 +1579,9 @@ Ty = useFirstFieldIfTransparentUnion(Ty); + if (Ty.isPassedIndirectly() == QualType::APK_Struct) + return getNaturalAlignIndirect(Ty, /*ByVal=*/false); + // Check with the C++ ABI first. const RecordType *RT = Ty->getAs(); if (RT) { @@ -3213,6 +3219,9 @@ ABIArgInfo X86_64ABIInfo:: classifyReturnType(QualType RetTy) const { + if (RetTy.isPassedIndirectly() == QualType::APK_Struct) + return getNaturalAlignIndirect(RetTy); + // AMD64-ABI 3.2.3p4: Rule 1. Classify the return type with the // classification algorithm. X86_64ABIInfo::Class Lo, Hi; @@ -3582,6 +3591,11 @@ unsigned ArgNo = 0; for (CGFunctionInfo::arg_iterator it = FI.arg_begin(), ie = FI.arg_end(); it != ie; ++it, ++ArgNo) { + if (static_cast(it->type).isPassedIndirectly() == QualType::APK_Struct) { + it->info = getNaturalAlignIndirect(it->type, /*ByVal=*/false); + continue; + } + bool IsNamedArg = ArgNo < NumRequiredArgs; if (IsRegCall && it->type->isStructureOrClassType()) @@ -5031,6 +5045,9 @@ llvm::ArrayType::get(CGT.ConvertType(QualType(Base, 0)), Members)); } + if (Ty.isPassedIndirectly() == QualType::APK_Struct) + return getNaturalAlignIndirect(Ty, /*ByVal=*/false); + // Aggregates <= 16 bytes are passed directly in registers or on the stack. if (Size <= 128) { // On RenderScript, coerce Aggregates <= 16 bytes to an integer array of @@ -5081,6 +5098,9 @@ // Homogeneous Floating-point Aggregates (HFAs) are returned directly. return ABIArgInfo::getDirect(); + if (RetTy.isPassedIndirectly() == QualType::APK_Struct) + return getNaturalAlignIndirect(RetTy); + // Aggregates <= 16 bytes are returned directly in registers or on the stack. if (Size <= 128) { // On RenderScript, coerce Aggregates <= 16 bytes to an integer array of @@ -5750,6 +5770,9 @@ if (isEmptyRecord(getContext(), Ty, true)) return ABIArgInfo::getIgnore(); + if (Ty.isPassedIndirectly() == QualType::APK_Struct) + return getNaturalAlignIndirect(Ty, /*ByVal=*/false); + if (IsEffectivelyAAPCS_VFP) { // Homogeneous Aggregates need to be expanded when we can fit the aggregate // into VFP registers. @@ -5940,6 +5963,9 @@ : ABIArgInfo::getDirect(); } + if (RetTy.isPassedIndirectly() == QualType::APK_Struct) + return getNaturalAlignIndirect(RetTy); + // Are we following APCS? if (getABIKind() == APCS) { if (isEmptyRecord(getContext(), RetTy, false)) Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -15217,7 +15217,6 @@ // Get the type for the field. const Type *FDTy = FD->getType().getTypePtr(); - Qualifiers QS = FD->getType().getQualifiers(); if (!FD->isAnonymousStructOrUnion()) { // Remember all fields written by the user. @@ -15358,10 +15357,7 @@ QualType T = Context.getObjCObjectPointerType(FD->getType()); FD->setType(T); } else if (getLangOpts().allowsNonTrivialObjCLifetimeQualifiers() && - Record && !ObjCFieldLifetimeErrReported && - ((!getLangOpts().CPlusPlus && - QS.getObjCLifetime() == Qualifiers::OCL_Weak) || - Record->isUnion())) { + Record && !ObjCFieldLifetimeErrReported && Record->isUnion()) { // It's an error in ARC or Weak if a field has lifetime. // We don't want to report this in a system header, though, // so we just make the field unavailable. @@ -15407,6 +15403,8 @@ Record->setNonTrivialToPrimitiveCopy(); if (FT.isDestructedType()) Record->setNonTrivialToPrimitiveDestroy(); + if (FT.isPassedIndirectly()) + Record->setPassedIndirectly(); } if (Record && FD->getType().isVolatileQualified()) Index: test/CodeGenObjC/nontrivial-c-struct-exception.m =================================================================== --- test/CodeGenObjC/nontrivial-c-struct-exception.m +++ test/CodeGenObjC/nontrivial-c-struct-exception.m @@ -1,12 +1,18 @@ -// RUN: %clang_cc1 -triple arm64-apple-ios11 -fobjc-arc -fblocks -fobjc-runtime=ios-11.0 -fobjc-exceptions -fexceptions -fobjc-arc-exceptions -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-ios11 -fobjc-arc -fblocks -fobjc-runtime=ios-11.0 -fobjc-exceptions -fexceptions -emit-llvm -o - %s | FileCheck %s // CHECK: %[[STRUCT_STRONG:.*]] = type { i32, i8* } +// CHECK: %[[STRUCT_WEAK:.*]] = type { i32, i8* } typedef struct { int i; id f1; } Strong; +typedef struct { + int i; + __weak id f1; +} Weak; + // CHECK: define void @testStrongException() // CHECK: %[[AGG_TMP:.*]] = alloca %[[STRUCT_STRONG]], align 8 // CHECK: %[[AGG_TMP1:.*]] = alloca %[[STRUCT_STRONG]], align 8 @@ -31,3 +37,26 @@ void testStrongException(void) { calleeStrong(genStrong(), genStrong()); } + +// CHECK: define void @testWeakException() +// CHECK: %[[AGG_TMP:.*]] = alloca %[[STRUCT_WEAK]], align 8 +// CHECK: %[[AGG_TMP1:.*]] = alloca %[[STRUCT_WEAK]], align 8 +// CHECK: call void @genWeak(%[[STRUCT_WEAK]]* sret %[[AGG_TMP]]) +// CHECK: invoke void @genWeak(%[[STRUCT_WEAK]]* sret %[[AGG_TMP1]]) + +// CHECK: call void @calleeWeak(%[[STRUCT_WEAK]]* %[[AGG_TMP]], %[[STRUCT_WEAK]]* %[[AGG_TMP1]]) +// CHECK: ret void + +// CHECK: landingpad { i8*, i32 } +// CHECK: %[[V3:.*]] = bitcast %[[STRUCT_WEAK]]* %[[AGG_TMP]] to i8** +// CHECK: call void @__destructor_8_w8(i8** %[[V3]]) +// CHECK: br label + +// CHECK: resume + +Weak genWeak(void); +void calleeWeak(Weak, Weak); + +void testWeakException(void) { + calleeWeak(genWeak(), genWeak()); +} Index: test/CodeGenObjC/weak-in-c-struct.m =================================================================== --- /dev/null +++ test/CodeGenObjC/weak-in-c-struct.m @@ -0,0 +1,193 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios11 -fobjc-arc -fblocks -fobjc-runtime=ios-11.0 -emit-llvm -o - %s | FileCheck -check-prefix=ARM64 -check-prefix=COMMON %s +// RUN: %clang_cc1 -triple thumbv7-apple-ios10 -fobjc-arc -fblocks -fobjc-runtime=ios-10.0 -emit-llvm -o - %s | FileCheck -check-prefix=COMMON %s +// RUN: %clang_cc1 -triple x86_64-apple-macosx10.13 -fobjc-arc -fblocks -fobjc-runtime=macosx-10.13.0 -emit-llvm -o - %s | FileCheck -check-prefix=COMMON %s +// RUN: %clang_cc1 -triple i386-apple-macosx10.13.0 -fobjc-arc -fblocks -fobjc-runtime=macosx-fragile-10.13.0 -emit-llvm -o - %s | FileCheck -check-prefix=COMMON %s + +typedef void (^BlockTy)(void); + +// COMMON: %[[STRUCT_WEAK:.*]] = type { i32, i8* } + +typedef struct { + int f0; + __weak id f1; +} Weak; + +Weak getWeak(void); +void calleeWeak(Weak); + +// ARM64: define void @test_constructor_destructor_Weak() +// ARM64: %[[T:.*]] = alloca %[[STRUCT_WEAK]], align 8 +// ARM64: %[[V0:.*]] = bitcast %[[STRUCT_WEAK]]* %[[T]] to i8** +// ARM64: call void @__default_constructor_8_w8(i8** %[[V0]]) +// ARM64: %[[V1:.*]] = bitcast %[[STRUCT_WEAK]]* %[[T]] to i8** +// ARM64: call void @__destructor_8_w8(i8** %[[V1]]) +// ARM64: ret void + +// ARM64: define linkonce_odr hidden void @__default_constructor_8_w8(i8** %[[DST:.*]]) +// ARM64: %[[DST_ADDR:.*]] = alloca i8**, align 8 +// ARM64: store i8** %[[DST]], i8*** %[[DST_ADDR]], align 8 +// ARM64: %[[V0:.*]] = load i8**, i8*** %[[DST_ADDR]], align 8 +// ARM64: %[[V1]] = bitcast i8** %[[V0]] to i8* +// ARM64: %[[V2:.*]] = getelementptr inbounds i8, i8* %[[V1]], i64 8 +// ARM64: %[[V3:.*]] = bitcast i8* %[[V2]] to i8** +// ARM64: %[[V4:.*]] = bitcast i8** %[[V3]] to i8* +// ARM64: call void @llvm.memset.p0i8.i64(i8* align 8 %[[V4]], i8 0, i64 8, i1 false) + +// ARM64: define linkonce_odr hidden void @__destructor_8_w8(i8** %[[DST:.*]]) +// ARM64: %[[DST_ADDR:.*]] = alloca i8**, align 8 +// ARM64: store i8** %[[DST]], i8*** %[[DST_ADDR]], align 8 +// ARM64: %[[V0:.*]] = load i8**, i8*** %[[DST_ADDR]], align 8 +// ARM64: %[[V1:.*]] = bitcast i8** %[[V0]] to i8* +// ARM64: %[[V2:.*]] = getelementptr inbounds i8, i8* %[[V1]], i64 8 +// ARM64: %[[V3:.*]] = bitcast i8* %[[V2]] to i8** +// ARM64: call void @objc_destroyWeak(i8** %[[V3]]) + +void test_constructor_destructor_Weak(void) { + Weak t; +} + +// ARM64: define void @test_copy_constructor_Weak(%[[STRUCT_WEAK]]* %{{.*}}) +// ARM64: call void @__copy_constructor_8_8_t0w4_w8(i8** %{{.*}}, i8** %{{.*}}) +// ARM64: call void @__destructor_8_w8(i8** %{{.*}}) + +// ARM64: define linkonce_odr hidden void @__copy_constructor_8_8_t0w4_w8(i8** %[[DST:.*]], i8** %[[SRC:.*]]) +// ARM64: %[[DST_ADDR:.*]] = alloca i8**, align 8 +// ARM64: %[[SRC_ADDR:.*]] = alloca i8**, align 8 +// ARM64: store i8** %[[DST]], i8*** %[[DST_ADDR]], align 8 +// ARM64: store i8** %[[SRC]], i8*** %[[SRC_ADDR]], align 8 +// ARM64: %[[V0:.*]] = load i8**, i8*** %[[DST_ADDR]], align 8 +// ARM64: %[[V1:.*]] = load i8**, i8*** %[[SRC_ADDR]], align 8 +// ARM64: %[[V2:.*]] = bitcast i8** %[[V0]] to i32* +// ARM64: %[[V3:.*]] = bitcast i8** %[[V1]] to i32* +// ARM64: %[[V4:.*]] = load i32, i32* %[[V3]], align 8 +// ARM64: store i32 %[[V4]], i32* %[[V2]], align 8 +// ARM64: %[[V5:.*]] = bitcast i8** %[[V0]] to i8* +// ARM64: %[[V6:.*]] = getelementptr inbounds i8, i8* %[[V5]], i64 8 +// ARM64: %[[V7:.*]] = bitcast i8* %[[V6]] to i8** +// ARM64: %[[V8:.*]] = bitcast i8** %[[V1]] to i8* +// ARM64: %[[V9:.*]] = getelementptr inbounds i8, i8* %[[V8]], i64 8 +// ARM64: %[[V10:.*]] = bitcast i8* %[[V9]] to i8** +// ARM64: call void @objc_copyWeak(i8** %[[V7]], i8** %[[V10]]) + +void test_copy_constructor_Weak(Weak *s) { + Weak t = *s; +} + +// ARM64: define void @test_copy_assignment_Weak(%[[STRUCT_WEAK]]* %{{.*}}, %[[STRUCT_WEAK]]* %{{.*}}) +// ARM64: call void @__copy_assignment_8_8_t0w4_w8(i8** %{{.*}}, i8** %{{.*}}) + +// ARM64: define linkonce_odr hidden void @__copy_assignment_8_8_t0w4_w8(i8** %[[DST:.*]], i8** %[[SRC:.*]]) +// ARM64: %[[DST_ADDR:.*]] = alloca i8**, align 8 +// ARM64: %[[SRC_ADDR:.*]] = alloca i8**, align 8 +// ARM64: store i8** %[[DST]], i8*** %[[DST_ADDR]], align 8 +// ARM64: store i8** %[[SRC]], i8*** %[[SRC_ADDR]], align 8 +// ARM64: %[[V0:.*]] = load i8**, i8*** %[[DST_ADDR]], align 8 +// ARM64: %[[V1:.*]] = load i8**, i8*** %[[SRC_ADDR]], align 8 +// ARM64: %[[V2:.*]] = bitcast i8** %[[V0]] to i32* +// ARM64: %[[V3:.*]] = bitcast i8** %[[V1]] to i32* +// ARM64: %[[V4:.*]] = load i32, i32* %[[V3]], align 8 +// ARM64: store i32 %[[V4]], i32* %[[V2]], align 8 +// ARM64: %[[V5:.*]] = bitcast i8** %[[V0]] to i8* +// ARM64: %[[V6:.*]] = getelementptr inbounds i8, i8* %[[V5]], i64 8 +// ARM64: %[[V7:.*]] = bitcast i8* %[[V6]] to i8** +// ARM64: %[[V8:.*]] = bitcast i8** %[[V1]] to i8* +// ARM64: %[[V9:.*]] = getelementptr inbounds i8, i8* %[[V8]], i64 8 +// ARM64: %[[V10:.*]] = bitcast i8* %[[V9]] to i8** +// ARM64: %[[V11:.*]] = call i8* @objc_loadWeakRetained(i8** %[[V10]]) +// ARM64: %[[V12:.*]] = call i8* @objc_storeWeak(i8** %[[V7]], i8* %[[V11]]) +// ARM64: call void @objc_release(i8* %[[V11]]) + +void test_copy_assignment_Weak(Weak *d, Weak *s) { + *d = *s; +} + +// ARM64: define internal void @__Block_byref_object_copy_(i8*, i8*) +// ARM64: call void @__move_constructor_8_8_t0w4_w8(i8** %{{.*}}, i8** %{{.*}}) + +// ARM64: define linkonce_odr hidden void @__move_constructor_8_8_t0w4_w8(i8** %[[DST:.*]], i8** %[[SRC:.*]]) +// ARM64: %[[DST_ADDR:.*]] = alloca i8**, align 8 +// ARM64: %[[SRC_ADDR:.*]] = alloca i8**, align 8 +// ARM64: store i8** %[[DST]], i8*** %[[DST_ADDR]], align 8 +// ARM64: store i8** %[[SRC]], i8*** %[[SRC_ADDR]], align 8 +// ARM64: %[[V0:.*]] = load i8**, i8*** %[[DST_ADDR]], align 8 +// ARM64: %[[V1:.*]] = load i8**, i8*** %[[SRC_ADDR]], align 8 +// ARM64: %[[V2:.*]] = bitcast i8** %[[V0]] to i32* +// ARM64: %[[V3:.*]] = bitcast i8** %[[V1]] to i32* +// ARM64: %[[V4:.*]] = load i32, i32* %[[V3]], align 8 +// ARM64: store i32 %[[V4]], i32* %[[V2]], align 8 +// ARM64: %[[V5:.*]] = bitcast i8** %[[V0]] to i8* +// ARM64: %[[V6:.*]] = getelementptr inbounds i8, i8* %[[V5]], i64 8 +// ARM64: %[[V7:.*]] = bitcast i8* %[[V6]] to i8** +// ARM64: %[[V8:.*]] = bitcast i8** %[[V1]] to i8* +// ARM64: %[[V9:.*]] = getelementptr inbounds i8, i8* %[[V8]], i64 8 +// ARM64: %[[V10:.*]] = bitcast i8* %[[V9]] to i8** +// ARM64: call void @objc_moveWeak(i8** %[[V7]], i8** %[[V10]]) + +void test_move_constructor_Weak(void) { + __block Weak t; + BlockTy b = ^{ (void)t; }; +} + +// ARM64: define void @test_move_assignment_Weak(%[[STRUCT_WEAK]]* %{{.*}}) +// ARM64: call void @__move_assignment_8_8_t0w4_w8(i8** %{{.*}}, i8** %{{.*}}) + +// ARM64: define linkonce_odr hidden void @__move_assignment_8_8_t0w4_w8(i8** %[[DST:.*]], i8** %[[SRC:.*]]) +// ARM64: %[[DST_ADDR:.*]] = alloca i8**, align 8 +// ARM64: %[[SRC_ADDR:.*]] = alloca i8**, align 8 +// ARM64: store i8** %[[DST]], i8*** %[[DST_ADDR]], align 8 +// ARM64: store i8** %[[SRC]], i8*** %[[SRC_ADDR]], align 8 +// ARM64: %[[V0:.*]] = load i8**, i8*** %[[DST_ADDR]], align 8 +// ARM64: %[[V1:.*]] = load i8**, i8*** %[[SRC_ADDR]], align 8 +// ARM64: %[[V2:.*]] = bitcast i8** %[[V0]] to i32* +// ARM64: %[[V3:.*]] = bitcast i8** %[[V1]] to i32* +// ARM64: %[[V4:.*]] = load i32, i32* %[[V3]], align 8 +// ARM64: store i32 %[[V4]], i32* %[[V2]], align 8 +// ARM64: %[[V5:.*]] = bitcast i8** %[[V0]] to i8* +// ARM64: %[[V6:.*]] = getelementptr inbounds i8, i8* %[[V5]], i64 8 +// ARM64: %[[V7:.*]] = bitcast i8* %[[V6]] to i8** +// ARM64: %[[V8:.*]] = bitcast i8** %[[V1]] to i8* +// ARM64: %[[V9:.*]] = getelementptr inbounds i8, i8* %[[V8]], i64 8 +// ARM64: %[[V10:.*]] = bitcast i8* %[[V9]] to i8** +// ARM64: %[[V11:.*]] = call i8* @objc_loadWeakRetained(i8** %[[V10]]) +// ARM64: %[[V12:.*]] = call i8* @objc_storeWeak(i8** %[[V7]], i8* %[[V11]]) +// ARM64: call void @objc_destroyWeak(i8** %[[V10]]) +// ARM64: call void @objc_release(i8* %[[V11]]) + +void test_move_assignment_Weak(Weak *p) { + *p = getWeak(); +} + +// COMMON: define void @test_parameter_Weak(%[[STRUCT_WEAK]]* %[[A:.*]]) +// COMMON: %[[V0:.*]] = bitcast %[[STRUCT_WEAK]]* %[[A]] to i8** +// COMMON: call void @__destructor_{{.*}}(i8** %[[V0]]) + +void test_parameter_Weak(Weak a) { +} + +// COMMON: define void @test_argument_Weak(%[[STRUCT_WEAK]]* %[[A:.*]]) +// COMMON: %[[A_ADDR:.*]] = alloca %[[STRUCT_WEAK]]* +// COMMON: %[[AGG_TMP:.*]] = alloca %[[STRUCT_WEAK]] +// COMMON: store %[[STRUCT_WEAK]]* %[[A]], %[[STRUCT_WEAK]]** %[[A_ADDR]] +// COMMON: %[[V0:.*]] = load %[[STRUCT_WEAK]]*, %[[STRUCT_WEAK]]** %[[A_ADDR]] +// COMMON: %[[V1:.*]] = bitcast %[[STRUCT_WEAK]]* %[[AGG_TMP]] to i8** +// COMMON: %[[V2:.*]] = bitcast %[[STRUCT_WEAK]]* %[[V0]] to i8** +// COMMON: call void @__copy_constructor_{{.*}}(i8** %[[V1]], i8** %[[V2]]) +// COMMON: call void @calleeWeak(%[[STRUCT_WEAK]]* %[[AGG_TMP]]) +// COMMON-NEXT: ret + +void test_argument_Weak(Weak *a) { + calleeWeak(*a); +} + +// COMMON: define void @test_return_Weak(%[[STRUCT_WEAK]]* noalias sret %[[AGG_RESULT:.*]], %[[STRUCT_WEAK]]* %[[A:.*]]) +// COMMON: %[[A_ADDR:.*]] = alloca %[[STRUCT_WEAK]]* +// COMMON: store %[[STRUCT_WEAK]]* %[[A]], %[[STRUCT_WEAK]]** %[[A_ADDR]] +// COMMON: %[[V0:.*]] = load %[[STRUCT_WEAK]]*, %[[STRUCT_WEAK]]** %[[A_ADDR]] +// COMMON: %[[V1:.*]] = bitcast %[[STRUCT_WEAK]]* %[[AGG_RESULT]] to i8** +// COMMON: %[[V2:.*]] = bitcast %[[STRUCT_WEAK]]* %[[V0]] to i8** +// COMMON: call void @__copy_constructor_{{.*}}(i8** %[[V1]], i8** %[[V2]]) +// COMMON: ret void + +Weak test_return_Weak(Weak *a) { + return *a; +}