diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 189e67e4eed0d..5760f382b59ed 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -1727,6 +1727,11 @@ ASTContext::PointerAuthContent ASTContext::findPointerAuthContent(QualType T) { T = T.getCanonicalType(); if (T.hasAddressDiscriminatedPointerAuth()) return PointerAuthContent::AddressDiscriminatedData; + + T = getBaseElementType(T).getCanonicalType(); + if (T.hasAddressDiscriminatedPointerAuth()) + return PointerAuthContent::AddressDiscriminatedData; + const RecordDecl *RD = T->getAsRecordDecl(); if (!RD) return PointerAuthContent::None; diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index 1f69274351676..dea0f214f6806 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -4467,17 +4467,25 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, case Builtin::BI__builtin_trivially_relocate: case Builtin::BImemmove: case Builtin::BI__builtin_memmove: { + QualType CopiedType = E->getArg(0)->getType()->getPointeeType(); Address Dest = EmitPointerWithAlignment(E->getArg(0)); Address Src = EmitPointerWithAlignment(E->getArg(1)); Value *SizeVal = EmitScalarExpr(E->getArg(2)); - if (BuiltinIDIfNoAsmLabel == Builtin::BI__builtin_trivially_relocate) + if (BuiltinIDIfNoAsmLabel == Builtin::BI__builtin_trivially_relocate) { + QualType BaseElementType = getContext().getBaseElementType(CopiedType); + if (getContext().containsAddressDiscriminatedPointerAuth( + BaseElementType)) { + llvm::Instruction *LastFixupInstruction = + EmitPointerAuthRelocationFixup(CopiedType, Dest, Src, SizeVal); + addInstToNewSourceAtom(LastFixupInstruction, nullptr); + return RValue::get(Dest, *this); + } SizeVal = Builder.CreateMul( SizeVal, ConstantInt::get( SizeVal->getType(), - getContext() - .getTypeSizeInChars(E->getArg(0)->getType()->getPointeeType()) - .getQuantity())); + getContext().getTypeSizeInChars(CopiedType).getQuantity())); + } EmitArgCheck(TCK_Store, Dest, E->getArg(0), 0); EmitArgCheck(TCK_Load, Src, E->getArg(1), 1); auto *I = Builder.CreateMemMove(Dest, Src, SizeVal, false); diff --git a/clang/lib/CodeGen/CGPointerAuth.cpp b/clang/lib/CodeGen/CGPointerAuth.cpp index dcef01a5eb6d3..bbfc19ca501a3 100644 --- a/clang/lib/CodeGen/CGPointerAuth.cpp +++ b/clang/lib/CodeGen/CGPointerAuth.cpp @@ -420,6 +420,219 @@ void CodeGenFunction::EmitPointerAuthCopy(PointerAuthQualifier Qual, QualType T, Builder.CreateStore(Value, DestAddress); } +static const ConstantArrayType *tryGetTypeAsConstantArrayType(QualType T) { + if (!T->isConstantArrayType()) + return nullptr; + return cast(T->castAsArrayTypeUnsafe()); +} + +using FixupErrorTy = std::pair; +class CodeGenFunction::FixupFinder { +public: + using FixupVectorTy = CodeGenFunction::FixupVectorTy; + static FixupVectorTy findFixups(CodeGenFunction &CGF, QualType T) { + FixupFinder Finder(CGF); + FixupVectorTy Result; + Finder.findFixups(Result, T, CharUnits::Zero()); + std::sort(Result.begin(), Result.end(), + [](const auto &L, const auto &R) { return L.Offset < R.Offset; }); + return Result; + } + +private: + explicit FixupFinder(CodeGenFunction &CGF) + : CGF(CGF), Context(CGF.getContext()) {} + + void findVTablePointerFixups(FixupVectorTy &Output, CXXRecordDecl *RD, + CharUnits Offset) { + CodeGenFunction::VPtrsVector VPtrs = CGF.getVTablePointers(RD); + for (auto VPtr : VPtrs) { + std::optional PointerAuth = + CGF.CGM.getVTablePointerAuthentication(VPtr.Base.getBase()); + if (PointerAuth && PointerAuth->isAddressDiscriminated()) + Output.push_back( + {Offset + VPtr.Base.getBaseOffset(), KnownNonNull, *PointerAuth}); + } + } + void findObjectFixups(FixupVectorTy &Output, CXXRecordDecl *RD, + CharUnits Offset) { + if (RD->isPolymorphic()) + findVTablePointerFixups(Output, RD, Offset); + findFixups(Output, RD, Offset, /*SubobjectIsBase=*/true); + } + + void findFixups(FixupVectorTy &Output, CXXRecordDecl *RD, + CharUnits SubobjectOffset, bool SubobjectIsBase) { + // If we've found a union it by definition cannot contain + // address discriminated fields. + if (RD->isUnion()) + return; + const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD); + if (Layout.hasOwnVFPtr() && RD == Layout.getPrimaryBase()) + findVTablePointerFixups(Output, RD, SubobjectOffset); + + for (auto Base : RD->bases()) { + CXXRecordDecl *BaseDecl = + Base.getType()->getAsCXXRecordDecl()->getDefinition(); + assert(!Base.isVirtual()); + CharUnits BaseOffset = Layout.getBaseClassOffset(BaseDecl); + findFixups(Output, BaseDecl, SubobjectOffset + BaseOffset, + /*SubobjectIsBase=*/true); + } + + for (const FieldDecl *Field : RD->fields()) { + if (Field->isBitField()) + continue; + unsigned FieldBitOffset = Layout.getFieldOffset(Field->getFieldIndex()); + CharUnits FieldOffset = Context.toCharUnitsFromBits(FieldBitOffset); + findFixups(Output, Field->getType(), SubobjectOffset + FieldOffset); + } + } + void findFixups(FixupVectorTy &Output, QualType T, CharUnits Offset) { + T = T.getCanonicalType(); + if (!Context.containsAddressDiscriminatedPointerAuth(T)) + return; + + if (const ConstantArrayType *CAT = tryGetTypeAsConstantArrayType(T)) { + if (CAT->getSize() == 0) + return; + Output.push_back({Offset, CAT}); + return; + } + + if (PointerAuthQualifier Q = T.getPointerAuth(); + Q && Q.isAddressDiscriminated()) { + // FIXME: Would it be reasonable to consider nullability? + Output.push_back({Offset, NotKnownNonNull, Q}); + return; + } + + CXXRecordDecl *RD = T->getAsCXXRecordDecl(); + if (!RD) + return; + findObjectFixups(Output, RD, Offset); + } + CodeGenFunction &CGF; + ASTContext &Context; +}; + +void CodeGenFunction::EmitSingleObjectPointerAuthRelocationFixup( + const FixupVectorTy &Fixups, QualType ElementType, Address Dst, + Address Src) { + auto GetFixupAddress = [&](Address BaseAddress, CharUnits Offset, + KnownNonNull_t IsKnownNonNull) { + llvm::Value *BasePtr = BaseAddress.emitRawPointer(*this); + llvm::Value *OffsetValue = + llvm::ConstantInt::get(PtrDiffTy, Offset.getQuantity()); + llvm::Value *FixupAddress = + Builder.CreateInBoundsGEP(Int8Ty, BasePtr, OffsetValue); + return Address(FixupAddress, VoidPtrPtrTy, + BaseAddress.getAlignment().alignmentAtOffset(Offset), + IsKnownNonNull); + }; + for (auto &Fixup : Fixups) { + if (const ConstantArrayType *CAT = Fixup.getAsConstantArrayType()) { + llvm::Value *CountValue = llvm::ConstantInt::get(SizeTy, 1); + EmitArrayPointerAuthRelocationFixup(QualType(CAT, 0), Dst, Src, + CountValue); + continue; + } + auto [IsKnownNonNull, Qualifier] = Fixup.getValueFixup(); + + // We don't use the existing copy helpers as we'll be resigning a + // value in place assuming the old address for the read. + Address FixupDst = GetFixupAddress(Dst, Fixup.Offset, IsKnownNonNull); + CGPointerAuthInfo DstPtrAuth = EmitPointerAuthInfo(Qualifier, FixupDst); + + Address FixupSrc = GetFixupAddress(Src, Fixup.Offset, IsKnownNonNull); + CGPointerAuthInfo SrcPtrAuth = EmitPointerAuthInfo(Qualifier, FixupSrc); + + // We're loading from the destination here as we've already performed the + // copy from src to dst, and as relocation has memmove semantics, the src + // address may have been overwritten. + llvm::Value *Value = Builder.CreateLoad(FixupDst); + Value = emitPointerAuthResign(Value, QualType(), SrcPtrAuth, DstPtrAuth, + IsKnownNonNull); + Builder.CreateStore(Value, FixupDst); + } +} + +llvm::Instruction *CodeGenFunction::EmitArrayPointerAuthRelocationFixup( + QualType ElementType, Address Dst, Address Src, llvm::Value *Count) { + // Preemptively flatten array types so we don't end up with multiple levels + // of loops unnecessarily + if (const ConstantArrayType *CAT = + tryGetTypeAsConstantArrayType(ElementType)) { + uint64_t ElementCount = getContext().getConstantArrayElementCount(CAT); + llvm::Value *ElementCountValue = + llvm::ConstantInt::get(SizeTy, ElementCount); + Count = Builder.CreateMul(Count, ElementCountValue); + ElementType = getContext().getBaseElementType(QualType(CAT, 0)); + } + + FixupVectorTy *Fixups; + if (const auto Existing = FixupLists.find(ElementType); + Existing != FixupLists.end()) + Fixups = Existing->second.get(); + else { + auto FoundFixups = FixupFinder::findFixups(*this, ElementType); + auto [EntryPoint, Inserted] = FixupLists.try_emplace( + ElementType, std::make_unique(std::move(FoundFixups))); + (void)Inserted; + Fixups = EntryPoint->second.get(); + } + + CharUnits ElementSize = getContext().getTypeSizeInChars(ElementType); + CharUnits ElementAlign = + Src.getAlignment().alignmentOfArrayElement(ElementSize); + llvm::Type *LLVMElemType = ConvertTypeForMem(ElementType); + + llvm::BasicBlock *RelocationFixupEntry = Builder.GetInsertBlock(); + llvm::BasicBlock *RelocationFixupBody = + createBasicBlock("relocation_ptrauth_fixup.body"); + EmitBlock(RelocationFixupBody); + llvm::Value *Zero = llvm::ConstantInt::get(SizeTy, 0); + llvm::PHINode *Index = + Builder.CreatePHI(SizeTy, 2, "relocation_ptrauth_fixup.index"); + Index->addIncoming(Zero, RelocationFixupEntry); + llvm::Value *DstElement = + Builder.CreateInBoundsGEP(LLVMElemType, Dst.emitRawPointer(*this), Index, + "relocation_ptrauth_fixup.dstobject"); + Address DstElementAddress = Address(DstElement, LLVMElemType, ElementAlign); + llvm::Value *SrcElement = + Builder.CreateInBoundsGEP(LLVMElemType, Src.emitRawPointer(*this), Index, + "relocation_ptrauth_fixup.srcobject"); + Address SrcElementAddress = Address(SrcElement, LLVMElemType, ElementAlign); + + // Do the fixup + EmitSingleObjectPointerAuthRelocationFixup( + *Fixups, ElementType, DstElementAddress, SrcElementAddress); + + llvm::Value *NextIndex = + Builder.CreateNUWAdd(Index, llvm::ConstantInt::get(Index->getType(), 1), + "relocation_ptrauth_fixup.next_index"); + Index->addIncoming(NextIndex, Builder.GetInsertBlock()); + llvm::Value *IsComplete = Builder.CreateICmpEQ( + NextIndex, Count, "relocation_ptrauth_fixup.is_complete"); + llvm::BasicBlock *RelocationFixupFinished = + createBasicBlock("relocation_ptrauth_fixup.end"); + Builder.CreateCondBr(IsComplete, RelocationFixupFinished, + RelocationFixupBody); + EmitBlock(RelocationFixupFinished); + return RelocationFixupFinished->getTerminator(); +} + +llvm::Instruction *CodeGenFunction::EmitPointerAuthRelocationFixup( + QualType ElementType, Address Dst, Address Src, llvm::Value *Count) { + size_t ElementSize = + getContext().getTypeSizeInChars(ElementType).getQuantity(); + llvm::Value *ElementSizeValue = + llvm::ConstantInt::get(Count->getType(), ElementSize); + llvm::Value *Size = Builder.CreateMul(Count, ElementSizeValue); + Builder.CreateMemMove(Dst, Src, Size, false); + return EmitArrayPointerAuthRelocationFixup(ElementType, Dst, Src, Count); +} + llvm::Constant * CodeGenModule::getConstantSignedPointer(llvm::Constant *Pointer, unsigned Key, llvm::Constant *StorageAddress, diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index a5ab9df01dba9..16098e83f0cd6 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -4610,6 +4610,10 @@ class CodeGenFunction : public CodeGenTypeCache { void EmitPointerAuthCopy(PointerAuthQualifier Qualifier, QualType Type, Address DestField, Address SrcField); + llvm::Instruction *EmitPointerAuthRelocationFixup(QualType ElementType, + Address Dst, Address Src, + llvm::Value *SrcEnd); + std::pair EmitOrigPointerRValue(const Expr *E); @@ -4618,6 +4622,46 @@ class CodeGenFunction : public CodeGenTypeCache { Address authPointerToPointerCast(Address Ptr, QualType SourceType, QualType DestType); +private: + llvm::Instruction *EmitArrayPointerAuthRelocationFixup(QualType ElementType, + Address Dst, + Address Src, + llvm::Value *Count); + + struct RelocatedAddressDiscriminatedPointerAuthFixup { + using FieldFixup = std::pair; + CharUnits Offset; + RelocatedAddressDiscriminatedPointerAuthFixup( + CharUnits Offset, KnownNonNull_t IsKnownNonNull, + PointerAuthQualifier Qualifier) + : Offset(Offset), Info(FieldFixup{IsKnownNonNull, Qualifier}) {} + RelocatedAddressDiscriminatedPointerAuthFixup( + CharUnits Offset, const ConstantArrayType *ArrayType) + : Offset(Offset), Info(ArrayType) {} + const ConstantArrayType *getAsConstantArrayType() const { + if (!std::holds_alternative(Info)) + return nullptr; + return std::get(Info); + } + std::pair getValueFixup() const { + assert(std::holds_alternative(Info)); + return std::get(Info); + } + + private: + std::variant Info; + }; + + using FixupVectorTy = + llvm::SmallVector; + class FixupFinder; + llvm::DenseMap> FixupLists; + + void EmitSingleObjectPointerAuthRelocationFixup(const FixupVectorTy &Fixups, + QualType ElementType, + Address Dst, Address Src); + +public: Address getAsNaturalAddressOf(Address Addr, QualType PointeeTy); llvm::Value *getAsNaturalPointerTo(Address Addr, QualType PointeeType) { diff --git a/clang/test/CodeGenCXX/cxx2c-trivially-relocatable-ptrauth.cpp b/clang/test/CodeGenCXX/cxx2c-trivially-relocatable-ptrauth.cpp new file mode 100644 index 0000000000000..168d9eac666b4 --- /dev/null +++ b/clang/test/CodeGenCXX/cxx2c-trivially-relocatable-ptrauth.cpp @@ -0,0 +1,354 @@ +// RUN: %clang_cc1 -std=c++26 -triple aarch64-linux-gnu -fptrauth-intrinsics -fptrauth-calls -emit-llvm -o - %s | FileCheck %s + +typedef __SIZE_TYPE__ size_t; + +#define vtable_ptrauth(...) [[clang::ptrauth_vtable_pointer(__VA_ARGS__)]] +#define ADDR_AND_TYPE_DISC vtable_ptrauth(process_independent, address_discrimination, type_discrimination) +#define TYPE_DISC_ONLY vtable_ptrauth(process_independent, no_address_discrimination, type_discrimination) + +struct TYPE_DISC_ONLY NoAddrDiscPoly trivially_relocatable_if_eligible { + NoAddrDiscPoly(const NoAddrDiscPoly&); + virtual ~NoAddrDiscPoly(); + int *__ptrauth(1,0,1) no_addr_disc; + int b; +}; + +// A simple test to ensure that we don't do anything more than the memmove +// if there's no actual reason to do so, despite being in a configuration +// where in principle such work _could_ be required +// CHECK-LABEL: define internal void @_ZL4testI14NoAddrDiscPolyEvPvS1_m( +// CHECK: call void @llvm.memmove.p0.p0.i64(ptr align 8 %2, ptr align 8 %3, i64 %5, i1 false) +// CHECK-NEXT: ret void + +struct ADDR_AND_TYPE_DISC AddrDiscPoly trivially_relocatable_if_eligible { + AddrDiscPoly(const AddrDiscPoly&); + virtual ~AddrDiscPoly(); + int *__ptrauth(1,0,1) no_addr_disc; + int b; +}; + +// CHECK-LABEL: define internal void @_ZL4testI12AddrDiscPolyEvPvS1_m( +// CHECK: [[DST_PTR:%.*]] = load ptr, ptr %dest, align 8 +// CHECK: [[SRC_PTR:%.*]] = load ptr, ptr %source, align 8 +// CHECK: [[COUNT:%.*]] = load i64, ptr %count.addr, align 8 +// CHECK: [[SIZE:%.*]] = mul i64 [[COUNT]], 24 +// CHECK: call void @llvm.memmove.p0.p0.i64(ptr{{.*}}[[DST_PTR]], ptr{{.*}}[[SRC_PTR]], i64 [[SIZE]], i1 false) +// CHECK: br label %[[COPY_BODY:[a-zA-Z._]+]] +// CHECK: [[COPY_BODY]]: +// CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, %entry ], [ [[NEXT_INDEX:%.*]], %[[COPY_BODY]] ] +// CHECK: [[DST_OBJ:%.*]] = getelementptr inbounds %struct.AddrDiscPoly, ptr [[DST_PTR]], i64 [[INDEX]] +// CHECK: [[SRC_OBJ:%.*]] = getelementptr inbounds %struct.AddrDiscPoly, ptr [[SRC_PTR]], i64 [[INDEX]] +// CHECK: [[FIXUP_DST_ADDR:%.*]] = getelementptr inbounds i8, ptr [[DST_OBJ]], i64 0 +// CHECK: [[FIXUP_DST_ADDR_INT:%.*]] = ptrtoint ptr [[FIXUP_DST_ADDR]] to i64 +// CHECK: [[FIXUP_DST_DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[FIXUP_DST_ADDR_INT]], i64 [[TYPE_DISC:49645]]) +// CHECK: [[FIXUP_SRC_ADDR:%.*]] = getelementptr inbounds i8, ptr [[SRC_OBJ]], i64 0 +// CHECK: [[FIXUP_SRC_ADDR_INT:%.*]] = ptrtoint ptr [[FIXUP_SRC_ADDR]] to i64 +// CHECK: [[FIXUP_SRC_DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[FIXUP_SRC_ADDR_INT]], i64 [[TYPE_DISC]]) +// CHECK: [[PREFIXUP_VALUE:%.*]] = load ptr, ptr [[FIXUP_DST_ADDR]] +// CHECK: [[PREFIXUP_VALUE_INT:%.*]] = ptrtoint ptr [[PREFIXUP_VALUE]] to i64 +// CHECK: [[FIXEDUP_VALUE_INT:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[PREFIXUP_VALUE_INT]], i32 2, i64 [[FIXUP_SRC_DISC]], i32 2, i64 [[FIXUP_DST_DISC]]) +// CHECK: [[FIXEDUP_VALUE:%.*]] = inttoptr i64 [[FIXEDUP_VALUE_INT]] to ptr +// CHECK: store ptr [[FIXEDUP_VALUE]], ptr [[FIXUP_DST_ADDR]] +// CHECK: [[NEXT_INDEX:%.*]] = add nuw i64 [[INDEX]], 1 +// CHECK: [[IS_COMPLETE:%.*]] = icmp eq i64 [[NEXT_INDEX]], [[COUNT]] +// CHECK: br i1 [[IS_COMPLETE]], label %[[FIXUP_END:[A-Za-z._]*]], label %[[COPY_BODY]] +// CHECK: [[FIXUP_END]]: +// CHECK-NEXT: ret void + +struct ADDR_AND_TYPE_DISC A trivially_relocatable_if_eligible { + virtual ~A(); + int i; +}; + +struct ADDR_AND_TYPE_DISC B trivially_relocatable_if_eligible { + virtual ~B(); + int j; +}; + +struct ADDR_AND_TYPE_DISC C trivially_relocatable_if_eligible { + virtual ~C(); + int k; +}; + +struct ADDR_AND_TYPE_DISC D trivially_relocatable_if_eligible { + virtual ~D(); + int l; +}; + +// Though different types, the structure of MultipleBaseClasses1 +// and MultipleBaseClasses1 is actually identical +struct MultipleBaseClasses1 trivially_relocatable_if_eligible : A, B { + C c; + D d; +}; + +// CHECK-LABEL: define internal void @_ZL4testI20MultipleBaseClasses1EvPvS1_m( +// CHECK: [[DST_PTR:%.*]] = load ptr, ptr %dest, align 8 +// CHECK: [[SRC_PTR:%.*]] = load ptr, ptr %source, align 8 +// CHECK: [[COUNT:%.*]] = load i64, ptr %count.addr, align 8 +// CHECK: [[SIZE:%.*]] = mul i64 [[COUNT]], 64 +// CHECK: call void @llvm.memmove.p0.p0.i64(ptr{{.*}}[[DST_PTR]], ptr{{.*}}[[SRC_PTR]], i64 [[SIZE]], i1 false) +// CHECK: br label %[[COPY_BODY:[a-zA-Z._]+]] +// CHECK: [[COPY_BODY]]: +// CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, %entry ], [ [[NEXT_INDEX:%.*]], %[[COPY_BODY]] ] +// CHECK: [[DST_OBJ:%.*]] = getelementptr inbounds %struct.MultipleBaseClasses1, ptr [[DST_PTR]], i64 [[INDEX]] +// CHECK: [[SRC_OBJ:%.*]] = getelementptr inbounds %struct.MultipleBaseClasses1, ptr [[SRC_PTR]], i64 [[INDEX]] + +// Fixup 1: MultipleBaseClasses1::A vtable pointer +// CHECK: [[FIXUP_DST_ADDR:%.*]] = getelementptr inbounds i8, ptr [[DST_OBJ]], i64 0 +// CHECK: [[FIXUP_DST_ADDR_INT:%.*]] = ptrtoint ptr [[FIXUP_DST_ADDR]] to i64 +// CHECK: [[FIXUP_DST_DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[FIXUP_DST_ADDR_INT]], i64 [[TYPE_DISC:62866]]) +// CHECK: [[FIXUP_SRC_ADDR:%.*]] = getelementptr inbounds i8, ptr [[SRC_OBJ]], i64 0 +// CHECK: [[FIXUP_SRC_ADDR_INT:%.*]] = ptrtoint ptr [[FIXUP_SRC_ADDR]] to i64 +// CHECK: [[FIXUP_SRC_DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[FIXUP_SRC_ADDR_INT]], i64 [[TYPE_DISC]]) +// CHECK: [[PREFIXUP_VALUE:%.*]] = load ptr, ptr [[FIXUP_DST_ADDR]] +// CHECK: [[PREFIXUP_VALUE_INT:%.*]] = ptrtoint ptr [[PREFIXUP_VALUE]] to i64 +// CHECK: [[FIXEDUP_VALUE_INT:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[PREFIXUP_VALUE_INT]], i32 2, i64 [[FIXUP_SRC_DISC]], i32 2, i64 [[FIXUP_DST_DISC]]) +// CHECK: [[FIXEDUP_VALUE:%.*]] = inttoptr i64 [[FIXEDUP_VALUE_INT]] to ptr +// CHECK: store ptr [[FIXEDUP_VALUE]], ptr [[FIXUP_DST_ADDR]] + +// Fixup 2: MultipleBaseClasses1::B vtable pointer +// CHECK: [[FIXUP_DST_ADDR:%.*]] = getelementptr inbounds i8, ptr [[DST_OBJ]], i64 16 +// CHECK: [[FIXUP_DST_ADDR_INT:%.*]] = ptrtoint ptr [[FIXUP_DST_ADDR]] to i64 +// CHECK: [[FIXUP_DST_DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[FIXUP_DST_ADDR_INT]], i64 [[TYPE_DISC:28965]]) +// CHECK: [[FIXUP_SRC_ADDR:%.*]] = getelementptr inbounds i8, ptr [[SRC_OBJ]], i64 16 +// CHECK: [[FIXUP_SRC_ADDR_INT:%.*]] = ptrtoint ptr [[FIXUP_SRC_ADDR]] to i64 +// CHECK: [[FIXUP_SRC_DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[FIXUP_SRC_ADDR_INT]], i64 [[TYPE_DISC]]) +// CHECK: [[PREFIXUP_VALUE:%.*]] = load ptr, ptr [[FIXUP_DST_ADDR]] +// CHECK: [[PREFIXUP_VALUE_INT:%.*]] = ptrtoint ptr [[PREFIXUP_VALUE]] to i64 +// CHECK: [[FIXEDUP_VALUE_INT:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[PREFIXUP_VALUE_INT]], i32 2, i64 [[FIXUP_SRC_DISC]], i32 2, i64 [[FIXUP_DST_DISC]]) +// CHECK: [[FIXEDUP_VALUE:%.*]] = inttoptr i64 [[FIXEDUP_VALUE_INT]] to ptr +// CHECK: store ptr [[FIXEDUP_VALUE]], ptr [[FIXUP_DST_ADDR]] + +// Fixup 3: MultipleBaseClasses1::c vtable pointer +// CHECK: [[FIXUP_DST_ADDR:%.*]] = getelementptr inbounds i8, ptr [[DST_OBJ]], i64 32 +// CHECK: [[FIXUP_DST_ADDR_INT:%.*]] = ptrtoint ptr [[FIXUP_DST_ADDR]] to i64 +// CHECK: [[FIXUP_DST_DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[FIXUP_DST_ADDR_INT]], i64 [[TYPE_DISC:20692]]) +// CHECK: [[FIXUP_SRC_ADDR:%.*]] = getelementptr inbounds i8, ptr [[SRC_OBJ]], i64 32 +// CHECK: [[FIXUP_SRC_ADDR_INT:%.*]] = ptrtoint ptr [[FIXUP_SRC_ADDR]] to i64 +// CHECK: [[FIXUP_SRC_DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[FIXUP_SRC_ADDR_INT]], i64 [[TYPE_DISC]]) +// CHECK: [[PREFIXUP_VALUE:%.*]] = load ptr, ptr [[FIXUP_DST_ADDR]] +// CHECK: [[PREFIXUP_VALUE_INT:%.*]] = ptrtoint ptr [[PREFIXUP_VALUE]] to i64 +// CHECK: [[FIXEDUP_VALUE_INT:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[PREFIXUP_VALUE_INT]], i32 2, i64 [[FIXUP_SRC_DISC]], i32 2, i64 [[FIXUP_DST_DISC]]) +// CHECK: [[FIXEDUP_VALUE:%.*]] = inttoptr i64 [[FIXEDUP_VALUE_INT]] to ptr +// CHECK: store ptr [[FIXEDUP_VALUE]], ptr [[FIXUP_DST_ADDR]] + +// Fixup 4: MultipleBaseClasses1::d vtable pointer +// CHECK: [[FIXUP_DST_ADDR:%.*]] = getelementptr inbounds i8, ptr [[DST_OBJ]], i64 48 +// CHECK: [[FIXUP_DST_ADDR_INT:%.*]] = ptrtoint ptr [[FIXUP_DST_ADDR]] to i64 +// CHECK: [[FIXUP_DST_DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[FIXUP_DST_ADDR_INT]], i64 [[TYPE_DISC:46475]]) +// CHECK: [[FIXUP_SRC_ADDR:%.*]] = getelementptr inbounds i8, ptr [[SRC_OBJ]], i64 48 +// CHECK: [[FIXUP_SRC_ADDR_INT:%.*]] = ptrtoint ptr [[FIXUP_SRC_ADDR]] to i64 +// CHECK: [[FIXUP_SRC_DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[FIXUP_SRC_ADDR_INT]], i64 [[TYPE_DISC]]) +// CHECK: [[PREFIXUP_VALUE:%.*]] = load ptr, ptr [[FIXUP_DST_ADDR]] +// CHECK: [[PREFIXUP_VALUE_INT:%.*]] = ptrtoint ptr [[PREFIXUP_VALUE]] to i64 +// CHECK: [[FIXEDUP_VALUE_INT:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[PREFIXUP_VALUE_INT]], i32 2, i64 [[FIXUP_SRC_DISC]], i32 2, i64 [[FIXUP_DST_DISC]]) +// CHECK: [[FIXEDUP_VALUE:%.*]] = inttoptr i64 [[FIXEDUP_VALUE_INT]] to ptr +// CHECK: store ptr [[FIXEDUP_VALUE]], ptr [[FIXUP_DST_ADDR]] + +// CHECK: [[NEXT_INDEX:%.*]] = add nuw i64 [[INDEX]], 1 +// CHECK: [[IS_COMPLETE:%.*]] = icmp eq i64 [[NEXT_INDEX]], [[COUNT]] +// CHECK: br i1 [[IS_COMPLETE]], label %[[FIXUP_END:[A-Za-z._]*]], label %[[COPY_BODY]] +// CHECK: [[FIXUP_END]]: +// CHECK-NEXT: ret void + +struct MultipleBaseClasses2 trivially_relocatable_if_eligible : A, B, C, D { +}; + +// An exact copy of the above with the only change being MultipleBaseClass1->MultipleBaseClass2 + +// CHECK-LABEL: define internal void @_ZL4testI20MultipleBaseClasses2EvPvS1_m( +// CHECK: [[DST_PTR:%.*]] = load ptr, ptr %dest, align 8 +// CHECK: [[SRC_PTR:%.*]] = load ptr, ptr %source, align 8 +// CHECK: [[COUNT:%.*]] = load i64, ptr %count.addr, align 8 +// CHECK: [[SIZE:%.*]] = mul i64 [[COUNT]], 64 +// CHECK: call void @llvm.memmove.p0.p0.i64(ptr{{.*}}[[DST_PTR]], ptr{{.*}}[[SRC_PTR]], i64 [[SIZE]], i1 false) +// CHECK: br label %[[COPY_BODY:[a-zA-Z._]+]] +// CHECK: [[COPY_BODY]]: +// CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, %entry ], [ [[NEXT_INDEX:%.*]], %[[COPY_BODY]] ] +// CHECK: [[DST_OBJ:%.*]] = getelementptr inbounds %struct.MultipleBaseClasses2, ptr [[DST_PTR]], i64 [[INDEX]] +// CHECK: [[SRC_OBJ:%.*]] = getelementptr inbounds %struct.MultipleBaseClasses2, ptr [[SRC_PTR]], i64 [[INDEX]] + +// Fixup 1: MultipleBaseClasses1::A vtable pointer +// CHECK: [[FIXUP_DST_ADDR:%.*]] = getelementptr inbounds i8, ptr [[DST_OBJ]], i64 0 +// CHECK: [[FIXUP_DST_ADDR_INT:%.*]] = ptrtoint ptr [[FIXUP_DST_ADDR]] to i64 +// CHECK: [[FIXUP_DST_DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[FIXUP_DST_ADDR_INT]], i64 [[TYPE_DISC:62866]]) +// CHECK: [[FIXUP_SRC_ADDR:%.*]] = getelementptr inbounds i8, ptr [[SRC_OBJ]], i64 0 +// CHECK: [[FIXUP_SRC_ADDR_INT:%.*]] = ptrtoint ptr [[FIXUP_SRC_ADDR]] to i64 +// CHECK: [[FIXUP_SRC_DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[FIXUP_SRC_ADDR_INT]], i64 [[TYPE_DISC]]) +// CHECK: [[PREFIXUP_VALUE:%.*]] = load ptr, ptr [[FIXUP_DST_ADDR]] +// CHECK: [[PREFIXUP_VALUE_INT:%.*]] = ptrtoint ptr [[PREFIXUP_VALUE]] to i64 +// CHECK: [[FIXEDUP_VALUE_INT:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[PREFIXUP_VALUE_INT]], i32 2, i64 [[FIXUP_SRC_DISC]], i32 2, i64 [[FIXUP_DST_DISC]]) +// CHECK: [[FIXEDUP_VALUE:%.*]] = inttoptr i64 [[FIXEDUP_VALUE_INT]] to ptr +// CHECK: store ptr [[FIXEDUP_VALUE]], ptr [[FIXUP_DST_ADDR]] + +// Fixup 2: MultipleBaseClasses1::B vtable pointer +// CHECK: [[FIXUP_DST_ADDR:%.*]] = getelementptr inbounds i8, ptr [[DST_OBJ]], i64 16 +// CHECK: [[FIXUP_DST_ADDR_INT:%.*]] = ptrtoint ptr [[FIXUP_DST_ADDR]] to i64 +// CHECK: [[FIXUP_DST_DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[FIXUP_DST_ADDR_INT]], i64 [[TYPE_DISC:28965]]) +// CHECK: [[FIXUP_SRC_ADDR:%.*]] = getelementptr inbounds i8, ptr [[SRC_OBJ]], i64 16 +// CHECK: [[FIXUP_SRC_ADDR_INT:%.*]] = ptrtoint ptr [[FIXUP_SRC_ADDR]] to i64 +// CHECK: [[FIXUP_SRC_DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[FIXUP_SRC_ADDR_INT]], i64 [[TYPE_DISC]]) +// CHECK: [[PREFIXUP_VALUE:%.*]] = load ptr, ptr [[FIXUP_DST_ADDR]] +// CHECK: [[PREFIXUP_VALUE_INT:%.*]] = ptrtoint ptr [[PREFIXUP_VALUE]] to i64 +// CHECK: [[FIXEDUP_VALUE_INT:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[PREFIXUP_VALUE_INT]], i32 2, i64 [[FIXUP_SRC_DISC]], i32 2, i64 [[FIXUP_DST_DISC]]) +// CHECK: [[FIXEDUP_VALUE:%.*]] = inttoptr i64 [[FIXEDUP_VALUE_INT]] to ptr +// CHECK: store ptr [[FIXEDUP_VALUE]], ptr [[FIXUP_DST_ADDR]] + +// Fixup 3: MultipleBaseClasses1::C vtable pointer +// CHECK: [[FIXUP_DST_ADDR:%.*]] = getelementptr inbounds i8, ptr [[DST_OBJ]], i64 32 +// CHECK: [[FIXUP_DST_ADDR_INT:%.*]] = ptrtoint ptr [[FIXUP_DST_ADDR]] to i64 +// CHECK: [[FIXUP_DST_DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[FIXUP_DST_ADDR_INT]], i64 [[TYPE_DISC:20692]]) +// CHECK: [[FIXUP_SRC_ADDR:%.*]] = getelementptr inbounds i8, ptr [[SRC_OBJ]], i64 32 +// CHECK: [[FIXUP_SRC_ADDR_INT:%.*]] = ptrtoint ptr [[FIXUP_SRC_ADDR]] to i64 +// CHECK: [[FIXUP_SRC_DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[FIXUP_SRC_ADDR_INT]], i64 [[TYPE_DISC]]) +// CHECK: [[PREFIXUP_VALUE:%.*]] = load ptr, ptr [[FIXUP_DST_ADDR]] +// CHECK: [[PREFIXUP_VALUE_INT:%.*]] = ptrtoint ptr [[PREFIXUP_VALUE]] to i64 +// CHECK: [[FIXEDUP_VALUE_INT:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[PREFIXUP_VALUE_INT]], i32 2, i64 [[FIXUP_SRC_DISC]], i32 2, i64 [[FIXUP_DST_DISC]]) +// CHECK: [[FIXEDUP_VALUE:%.*]] = inttoptr i64 [[FIXEDUP_VALUE_INT]] to ptr +// CHECK: store ptr [[FIXEDUP_VALUE]], ptr [[FIXUP_DST_ADDR]] + +// Fixup 4: MultipleBaseClasses1::D vtable pointer +// CHECK: [[FIXUP_DST_ADDR:%.*]] = getelementptr inbounds i8, ptr [[DST_OBJ]], i64 48 +// CHECK: [[FIXUP_DST_ADDR_INT:%.*]] = ptrtoint ptr [[FIXUP_DST_ADDR]] to i64 +// CHECK: [[FIXUP_DST_DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[FIXUP_DST_ADDR_INT]], i64 [[TYPE_DISC:46475]]) +// CHECK: [[FIXUP_SRC_ADDR:%.*]] = getelementptr inbounds i8, ptr [[SRC_OBJ]], i64 48 +// CHECK: [[FIXUP_SRC_ADDR_INT:%.*]] = ptrtoint ptr [[FIXUP_SRC_ADDR]] to i64 +// CHECK: [[FIXUP_SRC_DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[FIXUP_SRC_ADDR_INT]], i64 [[TYPE_DISC]]) +// CHECK: [[PREFIXUP_VALUE:%.*]] = load ptr, ptr [[FIXUP_DST_ADDR]] +// CHECK: [[PREFIXUP_VALUE_INT:%.*]] = ptrtoint ptr [[PREFIXUP_VALUE]] to i64 +// CHECK: [[FIXEDUP_VALUE_INT:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[PREFIXUP_VALUE_INT]], i32 2, i64 [[FIXUP_SRC_DISC]], i32 2, i64 [[FIXUP_DST_DISC]]) +// CHECK: [[FIXEDUP_VALUE:%.*]] = inttoptr i64 [[FIXEDUP_VALUE_INT]] to ptr +// CHECK: store ptr [[FIXEDUP_VALUE]], ptr [[FIXUP_DST_ADDR]] + +// CHECK: [[NEXT_INDEX:%.*]] = add nuw i64 [[INDEX]], 1 +// CHECK: [[IS_COMPLETE:%.*]] = icmp eq i64 [[NEXT_INDEX]], [[COUNT]] +// CHECK: br i1 [[IS_COMPLETE]], label %[[FIXUP_END:[A-Za-z._]*]], label %[[COPY_BODY]] +// CHECK: [[FIXUP_END]]: +// CHECK-NEXT: ret void + + +struct ADDR_AND_TYPE_DISC Foo trivially_relocatable_if_eligible { + int buffer; + virtual ~Foo(); +}; + +struct ADDR_AND_TYPE_DISC ArrayMember { + Foo buffer[100]; + virtual void bar(); +}; + +// CHECK-LABEL: define internal void @_ZL4testI11ArrayMemberEvPvS1_m( +// CHECK: [[DST_PTR:%.*]] = load ptr, ptr %dest, align 8 +// CHECK: [[SRC_PTR:%.*]] = load ptr, ptr %source, align 8 +// CHECK: [[COUNT:%.*]] = load i64, ptr %count.addr, align 8 +// CHECK: [[SIZE:%.*]] = mul i64 [[COUNT]], 1608 +// CHECK: call void @llvm.memmove.p0.p0.i64(ptr{{.*}}[[DST_PTR]], ptr{{.*}}[[SRC_PTR]], i64 [[SIZE]], i1 false) +// CHECK: br label %[[COPY_BODY:[a-zA-Z._]+]] +// CHECK: [[COPY_BODY]]: +// CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, %entry ], [ [[NEXT_INDEX:%.*]], %[[RELOCATION_SUBOBJECT_END:[a-zA-Z._]+]] ] +// CHECK: [[DST_OBJ:%.*]] = getelementptr inbounds %struct.ArrayMember, ptr [[DST_PTR]], i64 [[INDEX]] +// CHECK: [[SRC_OBJ:%.*]] = getelementptr inbounds %struct.ArrayMember, ptr [[SRC_PTR]], i64 [[INDEX]] +// CHECK: [[FIXUP_DST_ADDR:%.*]] = getelementptr inbounds i8, ptr [[DST_OBJ]], i64 0 +// CHECK: [[FIXUP_DST_ADDR_INT:%.*]] = ptrtoint ptr [[FIXUP_DST_ADDR]] to i64 +// CHECK: [[FIXUP_DST_DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[FIXUP_DST_ADDR_INT]], i64 [[TYPE_DISC:9693]]) +// CHECK: [[FIXUP_SRC_ADDR:%.*]] = getelementptr inbounds i8, ptr [[SRC_OBJ]], i64 0 +// CHECK: [[FIXUP_SRC_ADDR_INT:%.*]] = ptrtoint ptr [[FIXUP_SRC_ADDR]] to i64 +// CHECK: [[FIXUP_SRC_DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[FIXUP_SRC_ADDR_INT]], i64 [[TYPE_DISC]]) +// CHECK: [[PREFIXUP_VALUE:%.*]] = load ptr, ptr [[FIXUP_DST_ADDR]] +// CHECK: [[PREFIXUP_VALUE_INT:%.*]] = ptrtoint ptr [[PREFIXUP_VALUE]] to i64 +// CHECK: [[FIXEDUP_VALUE_INT:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[PREFIXUP_VALUE_INT]], i32 2, i64 [[FIXUP_SRC_DISC]], i32 2, i64 [[FIXUP_DST_DISC]]) +// CHECK: [[FIXEDUP_VALUE:%.*]] = inttoptr i64 [[FIXEDUP_VALUE_INT]] to ptr +// CHECK: store ptr [[FIXEDUP_VALUE]], ptr [[FIXUP_DST_ADDR]] +// CHECK: br label %[[SUBOBJECT_COPY_BODY:[a-zA-Z._0-9]+]] +// CHECK: [[SUBOBJECT_COPY_BODY]]: +// CHECK-NEXT: [[SUBOBJECT_INDEX:%.*]] = phi i64 [ 0, %[[COPY_BODY]] ], [ [[SUBOBJECT_NEXT_INDEX:%.*]], %[[SUBOBJECT_COPY_BODY]] ] +// CHECK: [[SUBOBJECT_DST_OBJ:%.*]] = getelementptr inbounds %struct.Foo, ptr [[DST_OBJ]], i64 [[SUBOBJECT_INDEX]] +// CHECK: [[SUBOBJECT_SRC_OBJ:%.*]] = getelementptr inbounds %struct.Foo, ptr [[SRC_OBJ]], i64 [[SUBOBJECT_INDEX]] +// CHECK: [[SUBOBJECT_FIXUP_DST_ADDR:%.*]] = getelementptr inbounds i8, ptr [[SUBOBJECT_DST_OBJ]], i64 0 +// CHECK: [[SUBOBJECT_FIXUP_DST_ADDR_INT:%.*]] = ptrtoint ptr [[SUBOBJECT_FIXUP_DST_ADDR]] to i64 +// CHECK: [[SUBOBJECT_FIXUP_DST_DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[SUBOBJECT_FIXUP_DST_ADDR_INT]], i64 [[SUBOBJECT_TYPE_DISC:31380]]) +// CHECK: [[SUBOBJECT_FIXUP_SRC_ADDR:%.*]] = getelementptr inbounds i8, ptr [[SUBOBJECT_SRC_OBJ]], i64 0 +// CHECK: [[SUBOBJECT_FIXUP_SRC_ADDR_INT:%.*]] = ptrtoint ptr [[SUBOBJECT_FIXUP_SRC_ADDR]] to i64 +// CHECK: [[SUBOBJECT_FIXUP_SRC_DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[SUBOBJECT_FIXUP_SRC_ADDR_INT]], i64 [[SUBOBJECT_TYPE_DISC]]) +// CHECK: [[SUBOBJECT_PREFIXUP_VALUE:%.*]] = load ptr, ptr [[SUBOBJECT_FIXUP_DST_ADDR]] +// CHECK: [[SUBOBJECT_PREFIXUP_VALUE_INT:%.*]] = ptrtoint ptr [[SUBOBJECT_PREFIXUP_VALUE]] to i64 +// CHECK: [[SUBOBJECT_FIXEDUP_VALUE_INT:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[SUBOBJECT_PREFIXUP_VALUE_INT]], i32 2, i64 [[SUBOBJECT_FIXUP_SRC_DISC]], i32 2, i64 [[SUBOBJECT_FIXUP_DST_DISC]]) +// CHECK: [[SUBOBJECT_FIXEDUP_VALUE:%.*]] = inttoptr i64 [[SUBOBJECT_FIXEDUP_VALUE_INT]] to ptr +// CHECK: store ptr [[SUBOBJECT_FIXEDUP_VALUE]], ptr [[SUBOBJECT_FIXUP_DST_ADDR]] + +// Copying Foo[11], verifying behaviour of array copies +// CHECK-LABEL: define internal void @_ZL4testIA11_3FooEvPvS2_m( +// CHECK: [[DST_PTR:%.*]] = load ptr, ptr %dest, align 8 +// CHECK: [[SRC_PTR:%.*]] = load ptr, ptr %source, align 8 +// CHECK: [[INIT_COUNT:%.*]] = load i64, ptr %count.addr, align 8 +// CHECK: [[SIZE:%.*]] = mul i64 [[INIT_COUNT]], 176 +// CHECK: call void @llvm.memmove.p0.p0.i64(ptr{{.*}}[[DST_PTR]], ptr{{.*}}[[SRC_PTR]], i64 [[SIZE]], i1 false) +// CHECK: [[COUNT:%.*]] = mul i64 [[INIT_COUNT]], 11 +// CHECK: br label %[[COPY_BODY:[a-zA-Z._]+]] +// CHECK: [[COPY_BODY]]: +// CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, %entry ], [ [[NEXT_INDEX:%.*]], %[[COPY_BODY]] ] +// CHECK: [[DST_OBJ:%.*]] = getelementptr inbounds %struct.Foo, ptr [[DST_PTR]], i64 [[INDEX]] +// CHECK: [[SRC_OBJ:%.*]] = getelementptr inbounds %struct.Foo, ptr [[SRC_PTR]], i64 [[INDEX]] +// CHECK: [[FIXUP_DST_ADDR:%.*]] = getelementptr inbounds i8, ptr [[DST_OBJ]], i64 0 +// CHECK: [[FIXUP_DST_ADDR_INT:%.*]] = ptrtoint ptr [[FIXUP_DST_ADDR]] to i64 +// CHECK: [[FIXUP_DST_DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[FIXUP_DST_ADDR_INT]], i64 [[TYPE_DISC:31380]]) +// CHECK: [[FIXUP_SRC_ADDR:%.*]] = getelementptr inbounds i8, ptr [[SRC_OBJ]], i64 0 +// CHECK: [[FIXUP_SRC_ADDR_INT:%.*]] = ptrtoint ptr [[FIXUP_SRC_ADDR]] to i64 +// CHECK: [[FIXUP_SRC_DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[FIXUP_SRC_ADDR_INT]], i64 [[TYPE_DISC]]) +// CHECK: [[PREFIXUP_VALUE:%.*]] = load ptr, ptr [[FIXUP_DST_ADDR]] +// CHECK: [[PREFIXUP_VALUE_INT:%.*]] = ptrtoint ptr [[PREFIXUP_VALUE]] to i64 +// CHECK: [[FIXEDUP_VALUE_INT:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[PREFIXUP_VALUE_INT]], i32 2, i64 [[FIXUP_SRC_DISC]], i32 2, i64 [[FIXUP_DST_DISC]]) +// CHECK: [[FIXEDUP_VALUE:%.*]] = inttoptr i64 [[FIXEDUP_VALUE_INT]] to ptr +// CHECK: store ptr [[FIXEDUP_VALUE]], ptr [[FIXUP_DST_ADDR]] +// CHECK: [[NEXT_INDEX:%.*]] = add nuw i64 [[INDEX]], 1 +// CHECK: [[IS_COMPLETE:%.*]] = icmp eq i64 [[NEXT_INDEX]], [[COUNT]] +// CHECK: br i1 [[IS_COMPLETE]], label %[[FIXUP_END:[A-Za-z._]*]], label %[[COPY_BODY]] +// CHECK: [[FIXUP_END]]: +// CHECK-NEXT: ret void + +// Copying Foo[13][17], verifying behaviour of multidimensional array copies +// CHECK-LABEL: define internal void @_ZL4testIA13_A17_3FooEvPvS3_m( +// CHECK: [[DST_PTR:%.*]] = load ptr, ptr %dest, align 8 +// CHECK: [[SRC_PTR:%.*]] = load ptr, ptr %source, align 8 +// CHECK: [[INIT_COUNT:%.*]] = load i64, ptr %count.addr, align 8 +// CHECK: [[SIZE:%.*]] = mul i64 [[INIT_COUNT]], 3536 +// CHECK: call void @llvm.memmove.p0.p0.i64(ptr{{.*}}[[DST_PTR]], ptr{{.*}}[[SRC_PTR]], i64 [[SIZE]], i1 false) +// CHECK: [[COUNT:%.*]] = mul i64 [[INIT_COUNT]], 221 +// CHECK: br label %[[COPY_BODY:[a-zA-Z._]+]] +// CHECK: [[COPY_BODY]]: +// CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, %entry ], [ [[NEXT_INDEX:%.*]], %[[COPY_BODY]] ] +// CHECK: [[DST_OBJ:%.*]] = getelementptr inbounds %struct.Foo, ptr [[DST_PTR]], i64 [[INDEX]] +// CHECK: [[SRC_OBJ:%.*]] = getelementptr inbounds %struct.Foo, ptr [[SRC_PTR]], i64 [[INDEX]] +// CHECK: [[FIXUP_DST_ADDR:%.*]] = getelementptr inbounds i8, ptr [[DST_OBJ]], i64 0 +// CHECK: [[FIXUP_DST_ADDR_INT:%.*]] = ptrtoint ptr [[FIXUP_DST_ADDR]] to i64 +// CHECK: [[FIXUP_DST_DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[FIXUP_DST_ADDR_INT]], i64 [[TYPE_DISC:31380]]) +// CHECK: [[FIXUP_SRC_ADDR:%.*]] = getelementptr inbounds i8, ptr [[SRC_OBJ]], i64 0 +// CHECK: [[FIXUP_SRC_ADDR_INT:%.*]] = ptrtoint ptr [[FIXUP_SRC_ADDR]] to i64 +// CHECK: [[FIXUP_SRC_DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[FIXUP_SRC_ADDR_INT]], i64 [[TYPE_DISC]]) +// CHECK: [[PREFIXUP_VALUE:%.*]] = load ptr, ptr [[FIXUP_DST_ADDR]] +// CHECK: [[PREFIXUP_VALUE_INT:%.*]] = ptrtoint ptr [[PREFIXUP_VALUE]] to i64 +// CHECK: [[FIXEDUP_VALUE_INT:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[PREFIXUP_VALUE_INT]], i32 2, i64 [[FIXUP_SRC_DISC]], i32 2, i64 [[FIXUP_DST_DISC]]) +// CHECK: [[FIXEDUP_VALUE:%.*]] = inttoptr i64 [[FIXEDUP_VALUE_INT]] to ptr +// CHECK: store ptr [[FIXEDUP_VALUE]], ptr [[FIXUP_DST_ADDR]] +// CHECK: [[NEXT_INDEX:%.*]] = add nuw i64 [[INDEX]], 1 +// CHECK: [[IS_COMPLETE:%.*]] = icmp eq i64 [[NEXT_INDEX]], [[COUNT]] +// CHECK: br i1 [[IS_COMPLETE]], label %[[FIXUP_END:[A-Za-z._]*]], label %[[COPY_BODY]] +// CHECK: [[FIXUP_END]]: +// CHECK-NEXT: ret void + +template __attribute__((noinline)) static void test(void* vDest, void* vSource, size_t count) { + T* dest = (T*)vDest; + T* source = (T*)vSource; + __builtin_trivially_relocate(dest, source, count); +}; + +void do_tests(void *Dst, void *Src) { + test(Dst, Src, 10); + test(Dst, Src, 10); + test(Dst, Src, 10); + test(Dst, Src, 10); + test(Dst, Src, 10); + test(Dst, Src, 10); + test(Dst, Src, 10); +} diff --git a/clang/test/SemaCXX/trivially-relocatable-ptrauth.cpp b/clang/test/SemaCXX/trivially-relocatable-ptrauth.cpp index b38499a634fcf..8f5bfc4514c1b 100644 --- a/clang/test/SemaCXX/trivially-relocatable-ptrauth.cpp +++ b/clang/test/SemaCXX/trivially-relocatable-ptrauth.cpp @@ -107,3 +107,9 @@ struct UnionOfAddressDisriminatedPtrauth { }; static_assert(!__builtin_is_cpp_trivially_relocatable(UnionOfAddressDisriminatedPtrauth)); + +struct AddressDiscArrayMember { + struct { void *__ptrauth(1,1,1) field; } array_field[10]; +}; + +static_assert(!__builtin_is_cpp_trivially_relocatable(AddressDiscArrayMember));