diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp index d6b33c8aeeaac3..2ae91feb2d9e8e 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp @@ -1862,10 +1862,10 @@ static bool interp__builtin_memcpy(InterpState &S, CodePtr OpPC, } QualType ElemType; - if (SrcPtr.getFieldDesc()->isArray()) - ElemType = SrcPtr.getFieldDesc()->getElemQualType(); + if (DestPtr.getFieldDesc()->isArray()) + ElemType = DestPtr.getFieldDesc()->getElemQualType(); else - ElemType = SrcPtr.getType(); + ElemType = DestPtr.getType(); unsigned ElemSize = S.getASTContext().getTypeSizeInChars(ElemType).getQuantity(); @@ -1876,6 +1876,18 @@ static bool interp__builtin_memcpy(InterpState &S, CodePtr OpPC, return false; } + QualType SrcElemType; + if (SrcPtr.getFieldDesc()->isArray()) + SrcElemType = SrcPtr.getFieldDesc()->getElemQualType(); + else + SrcElemType = SrcPtr.getType(); + + if (!S.getASTContext().hasSameUnqualifiedType(ElemType, SrcElemType)) { + S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_memcpy_type_pun) + << Move << SrcElemType << ElemType; + return false; + } + // Check for overlapping memory regions. if (!Move && Pointer::pointToSameBlock(SrcPtr, DestPtr)) { unsigned SrcIndex = SrcPtr.getIndex() * SrcPtr.elemSize(); @@ -1893,8 +1905,8 @@ static bool interp__builtin_memcpy(InterpState &S, CodePtr OpPC, // As a last resort, reject dummy pointers. if (DestPtr.isDummy() || SrcPtr.isDummy()) return false; - - if (!DoBitCastPtr(S, OpPC, SrcPtr, DestPtr, Size.getZExtValue())) + assert(Size.getZExtValue() % ElemSize == 0); + if (!DoMemcpy(S, OpPC, SrcPtr, DestPtr, Bytes(Size.getZExtValue()).toBits())) return false; S.Stk.push(DestPtr); diff --git a/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp b/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp index 07f76943708216..0fc94e1694822a 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp @@ -448,3 +448,34 @@ bool clang::interp::DoBitCastPtr(InterpState &S, CodePtr OpPC, return Success; } + +bool clang::interp::DoMemcpy(InterpState &S, CodePtr OpPC, + const Pointer &SrcPtr, const Pointer &DestPtr, + Bits Size) { + assert(SrcPtr.isBlockPointer()); + assert(DestPtr.isBlockPointer()); + + unsigned SrcStartOffset = SrcPtr.getByteOffset(); + unsigned DestStartOffset = DestPtr.getByteOffset(); + + enumeratePointerFields(SrcPtr, S.getContext(), Size, + [&](const Pointer &P, PrimType T, Bits BitOffset, + Bits FullBitWidth, bool PackedBools) -> bool { + unsigned SrcOffsetDiff = + P.getByteOffset() - SrcStartOffset; + + Pointer DestP = + Pointer(DestPtr.asBlockPointer().Pointee, + DestPtr.asBlockPointer().Base, + DestStartOffset + SrcOffsetDiff); + + TYPE_SWITCH(T, { + DestP.deref() = P.deref(); + DestP.initialize(); + }); + + return true; + }); + + return true; +} diff --git a/clang/lib/AST/ByteCode/InterpBuiltinBitCast.h b/clang/lib/AST/ByteCode/InterpBuiltinBitCast.h index b45613b2f21e20..a0191bab693c45 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltinBitCast.h +++ b/clang/lib/AST/ByteCode/InterpBuiltinBitCast.h @@ -33,6 +33,10 @@ bool DoBitCastPtr(InterpState &S, CodePtr OpPC, const Pointer &FromPtr, Pointer &ToPtr, size_t Size); bool readPointerToBuffer(const Context &Ctx, const Pointer &FromPtr, BitcastBuffer &Buffer, bool ReturnOnUninit); + +bool DoMemcpy(InterpState &S, CodePtr OpPC, const Pointer &SrcPtr, + const Pointer &DestPtr, Bits Size); + } // namespace interp } // namespace clang diff --git a/clang/lib/AST/ByteCode/Pointer.h b/clang/lib/AST/ByteCode/Pointer.h index 457fe93b278175..0d467c2abf0838 100644 --- a/clang/lib/AST/ByteCode/Pointer.h +++ b/clang/lib/AST/ByteCode/Pointer.h @@ -107,6 +107,7 @@ class Pointer { : Offset(Offset), StorageKind(Storage::Fn) { PointeeStorage.Fn = FunctionPointer(F); } + Pointer(Block *Pointee, unsigned Base, uint64_t Offset); ~Pointer(); void operator=(const Pointer &P); @@ -706,8 +707,6 @@ class Pointer { friend struct InitMap; friend class DynamicAllocator; - Pointer(Block *Pointee, unsigned Base, uint64_t Offset); - /// Returns the embedded descriptor preceding a field. InlineDescriptor *getInlineDesc() const { assert(isBlockPointer()); diff --git a/clang/test/AST/ByteCode/builtin-functions.cpp b/clang/test/AST/ByteCode/builtin-functions.cpp index 5906cb970f06c4..c1fd1bc1381503 100644 --- a/clang/test/AST/ByteCode/builtin-functions.cpp +++ b/clang/test/AST/ByteCode/builtin-functions.cpp @@ -1222,6 +1222,28 @@ namespace BuiltinMemcpy { static_assert(test_memcpy(1, 2, sizeof(int)) == 1334); static_assert(test_memcpy(0, 1, sizeof(int) * 2) == 2334); // both-error {{not an integral constant expression}} \ // both-note {{in call}} + + /// Both memcpy and memmove must support pointers. + constexpr bool moveptr() { + int a = 0; + void *x = &a; + void *z = nullptr; + + __builtin_memmove(&z, &x, sizeof(void*)); + return z == x; + } + static_assert(moveptr()); + + constexpr bool cpyptr() { + int a = 0; + void *x = &a; + void *z = nullptr; + + __builtin_memcpy(&z, &x, sizeof(void*)); + return z == x; + } + static_assert(cpyptr()); + } namespace Memcmp {