diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp index 2ae91feb2d9e8e..d0d8b03deab268 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp @@ -17,6 +17,7 @@ #include "clang/Basic/Builtins.h" #include "clang/Basic/TargetBuiltins.h" #include "clang/Basic/TargetInfo.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/Support/SipHash.h" namespace clang { @@ -1837,6 +1838,7 @@ static bool interp__builtin_memcpy(InterpState &S, CodePtr OpPC, assert(Call->getNumArgs() == 3); unsigned ID = Func->getBuiltinID(); Pointer DestPtr = getParam(Frame, 0); + const ASTContext &ASTCtx = S.getASTContext(); const Pointer &SrcPtr = getParam(Frame, 1); const APSInt &Size = peekToAPSInt(S.Stk, *S.getContext().classify(Call->getArg(2))); @@ -1857,34 +1859,55 @@ static bool interp__builtin_memcpy(InterpState &S, CodePtr OpPC, Pointer DiagPtr = (SrcPtr.isZero() ? SrcPtr : DestPtr); S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_memcpy_null) << /*IsMove=*/Move << /*IsWchar=*/false << !SrcPtr.isZero() - << DiagPtr.toDiagnosticString(S.getASTContext()); + << DiagPtr.toDiagnosticString(ASTCtx); return false; } - QualType ElemType; - if (DestPtr.getFieldDesc()->isArray()) - ElemType = DestPtr.getFieldDesc()->getElemQualType(); - else - ElemType = DestPtr.getType(); + QualType DestElemType; + size_t RemainingDestElems; + if (DestPtr.getFieldDesc()->isArray()) { + DestElemType = DestPtr.getFieldDesc()->getElemQualType(); + RemainingDestElems = (DestPtr.getNumElems() - DestPtr.getIndex()); + } else { + DestElemType = DestPtr.getType(); + RemainingDestElems = 1; + } + unsigned DestElemSize = ASTCtx.getTypeSizeInChars(DestElemType).getQuantity(); - unsigned ElemSize = - S.getASTContext().getTypeSizeInChars(ElemType).getQuantity(); - if (Size.urem(ElemSize) != 0) { + if (Size.urem(DestElemSize) != 0) { S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_memcpy_unsupported) - << Move << /*IsWchar=*/false << 0 << ElemType << Size << ElemSize; + << Move << /*IsWchar=*/false << 0 << DestElemType << Size + << DestElemSize; return false; } QualType SrcElemType; - if (SrcPtr.getFieldDesc()->isArray()) + size_t RemainingSrcElems; + if (SrcPtr.getFieldDesc()->isArray()) { SrcElemType = SrcPtr.getFieldDesc()->getElemQualType(); - else + RemainingSrcElems = (SrcPtr.getNumElems() - SrcPtr.getIndex()); + } else { SrcElemType = SrcPtr.getType(); + RemainingSrcElems = 1; + } + unsigned SrcElemSize = ASTCtx.getTypeSizeInChars(SrcElemType).getQuantity(); - if (!S.getASTContext().hasSameUnqualifiedType(ElemType, SrcElemType)) { + if (!ASTCtx.hasSameUnqualifiedType(DestElemType, SrcElemType)) { S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_memcpy_type_pun) - << Move << SrcElemType << ElemType; + << Move << SrcElemType << DestElemType; + return false; + } + + // Check if we have enough elements to read from and write to/ + size_t RemainingDestBytes = RemainingDestElems * DestElemSize; + size_t RemainingSrcBytes = RemainingSrcElems * SrcElemSize; + if (Size.ugt(RemainingDestBytes) || Size.ugt(RemainingSrcBytes)) { + APInt N = Size.udiv(DestElemSize); + S.FFDiag(S.Current->getSource(OpPC), + diag::note_constexpr_memcpy_unsupported) + << Move << /*IsWChar*/ false << (Size.ugt(RemainingSrcBytes) ? 1 : 2) + << DestElemType << toString(N, 10, /*Signed=*/false); return false; } @@ -1905,7 +1928,7 @@ static bool interp__builtin_memcpy(InterpState &S, CodePtr OpPC, // As a last resort, reject dummy pointers. if (DestPtr.isDummy() || SrcPtr.isDummy()) return false; - assert(Size.getZExtValue() % ElemSize == 0); + assert(Size.getZExtValue() % DestElemSize == 0); if (!DoMemcpy(S, OpPC, SrcPtr, DestPtr, Bytes(Size.getZExtValue()).toBits())) return false; diff --git a/clang/test/AST/ByteCode/builtin-functions.cpp b/clang/test/AST/ByteCode/builtin-functions.cpp index c1fd1bc1381503..b0f8ea2e55ee0b 100644 --- a/clang/test/AST/ByteCode/builtin-functions.cpp +++ b/clang/test/AST/ByteCode/builtin-functions.cpp @@ -1244,6 +1244,15 @@ namespace BuiltinMemcpy { } static_assert(cpyptr()); +#ifndef __AVR__ + constexpr int test_memmove(int a, int b, int n) { + int arr[4] = {1, 2, 3, 4}; + __builtin_memmove(arr + a, arr + b, n); // both-note {{destination is not a contiguous array of at least 3 elements of type 'int'}} + return result(arr); + } + static_assert(test_memmove(2, 0, 12) == 4234); // both-error {{constant}} \ + // both-note {{in call}} +#endif } namespace Memcmp {