We hit this in Chromium too, see crbug.com/869824 I've reverted in r338602
On Wed, Aug 1, 2018 at 5:26 PM, Benjamin Kramer via cfe-commits <cfe-commits@lists.llvm.org> wrote: > It's pretty easy to make this crash > > $ cat memcpy.c > void foo() { > int a[1], b; > memcpy((char*)a, (const char*)&b, (unsigned long)4); > } > > $ clang memcpy.c > llvm/include/llvm/ADT/SmallVector.h:178: const_reference > llvm::SmallVectorTemplateCommon<clang::APValue::LValuePathEntry, > void>::back() const [T = clang::APValue::LValue > PathEntry]: Assertion `!empty()' failed. > > On Wed, Aug 1, 2018 at 1:35 AM Richard Smith via cfe-commits > <cfe-commits@lists.llvm.org> wrote: >> >> Author: rsmith >> Date: Tue Jul 31 16:35:09 2018 >> New Revision: 338455 >> >> URL: http://llvm.org/viewvc/llvm-project?rev=338455&view=rev >> Log: >> [constexpr] Support for constant evaluation of __builtin_memcpy and >> __builtin_memmove (in non-type-punning cases). >> >> This is intended to permit libc++ to make std::copy etc constexpr >> without sacrificing the optimization that uses memcpy on >> trivially-copyable types. >> >> __builtin_strcpy and __builtin_wcscpy are not handled by this change. >> They'd be straightforward to add, but we haven't encountered a need for >> them just yet. >> >> Modified: >> cfe/trunk/include/clang/Basic/Builtins.def >> cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td >> cfe/trunk/lib/AST/ExprConstant.cpp >> cfe/trunk/test/CodeGen/builtin-memfns.c >> cfe/trunk/test/SemaCXX/constexpr-string.cpp >> >> Modified: cfe/trunk/include/clang/Basic/Builtins.def >> URL: >> http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Builtins.def?rev=338455&r1=338454&r2=338455&view=diff >> >> ============================================================================== >> --- cfe/trunk/include/clang/Basic/Builtins.def (original) >> +++ cfe/trunk/include/clang/Basic/Builtins.def Tue Jul 31 16:35:09 2018 >> @@ -471,6 +471,8 @@ BUILTIN(__builtin_wcslen, "zwC*", "nF") >> BUILTIN(__builtin_wcsncmp, "iwC*wC*z", "nF") >> BUILTIN(__builtin_wmemchr, "w*wC*wz", "nF") >> BUILTIN(__builtin_wmemcmp, "iwC*wC*z", "nF") >> +BUILTIN(__builtin_wmemcpy, "w*w*wC*z", "nF") >> +BUILTIN(__builtin_wmemmove, "w*w*wC*z", "nF") >> BUILTIN(__builtin_return_address, "v*IUi", "n") >> BUILTIN(__builtin_extract_return_addr, "v*v*", "n") >> BUILTIN(__builtin_frame_address, "v*IUi", "n") >> @@ -908,6 +910,8 @@ LIBBUILTIN(wcslen, "zwC*", "f", "wc >> LIBBUILTIN(wcsncmp, "iwC*wC*z", "f", "wchar.h", ALL_LANGUAGES) >> LIBBUILTIN(wmemchr, "w*wC*wz", "f", "wchar.h", ALL_LANGUAGES) >> LIBBUILTIN(wmemcmp, "iwC*wC*z", "f", "wchar.h", ALL_LANGUAGES) >> +LIBBUILTIN(wmemcpy, "w*w*wC*z", "f", "wchar.h", ALL_LANGUAGES) >> +LIBBUILTIN(wmemmove,"w*w*wC*z", "f", "wchar.h", ALL_LANGUAGES) >> >> // C99 >> // In some systems setjmp is a macro that expands to _setjmp. We undefine >> >> Modified: cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td >> URL: >> http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td?rev=338455&r1=338454&r2=338455&view=diff >> >> ============================================================================== >> --- cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td (original) >> +++ cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td Tue Jul 31 >> 16:35:09 2018 >> @@ -163,6 +163,20 @@ def note_constexpr_unsupported_unsized_a >> def note_constexpr_unsized_array_indexed : Note< >> "indexing of array without known bound is not allowed " >> "in a constant expression">; >> +def note_constexpr_memcpy_type_pun : Note< >> + "cannot constant evaluate '%select{memcpy|memmove}0' from object of " >> + "type %1 to object of type %2">; >> +def note_constexpr_memcpy_nontrivial : Note< >> + "cannot constant evaluate '%select{memcpy|memmove}0' between objects of >> " >> + "non-trivially-copyable type %1">; >> +def note_constexpr_memcpy_overlap : Note< >> + "'%select{memcpy|wmemcpy}0' between overlapping memory regions">; >> +def note_constexpr_memcpy_unsupported : Note< >> + "'%select{%select{memcpy|wmemcpy}1|%select{memmove|wmemmove}1}0' " >> + "not supported: %select{" >> + "size to copy (%4) is not a multiple of size of element type %3 (%5)|" >> + "source is not a contiguous array of at least %4 elements of type %3|" >> + "destination is not a contiguous array of at least %4 elements of type >> %3}2">; >> >> def warn_integer_constant_overflow : Warning< >> "overflow in expression; result is %0 with type %1">, >> >> Modified: cfe/trunk/lib/AST/ExprConstant.cpp >> URL: >> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprConstant.cpp?rev=338455&r1=338454&r2=338455&view=diff >> >> ============================================================================== >> --- cfe/trunk/lib/AST/ExprConstant.cpp (original) >> +++ cfe/trunk/lib/AST/ExprConstant.cpp Tue Jul 31 16:35:09 2018 >> @@ -319,6 +319,25 @@ namespace { >> return false; >> } >> >> + /// Get the range of valid index adjustments in the form >> + /// {maximum value that can be subtracted from this pointer, >> + /// maximum value that can be added to this pointer} >> + std::pair<uint64_t, uint64_t> validIndexAdjustments() { >> + if (Invalid || isMostDerivedAnUnsizedArray()) >> + return {0, 0}; >> + >> + // [expr.add]p4: For the purposes of these operators, a pointer to >> a >> + // nonarray object behaves the same as a pointer to the first >> element of >> + // an array of length one with the type of the object as its >> element type. >> + bool IsArray = MostDerivedPathLength == Entries.size() && >> + MostDerivedIsArrayElement; >> + uint64_t ArrayIndex = >> + IsArray ? Entries.back().ArrayIndex : >> (uint64_t)IsOnePastTheEnd; >> + uint64_t ArraySize = >> + IsArray ? getMostDerivedArraySize() : (uint64_t)1; >> + return {ArrayIndex, ArraySize - ArrayIndex}; >> + } >> + >> /// Check that this refers to a valid subobject. >> bool isValidSubobject() const { >> if (Invalid) >> @@ -329,6 +348,13 @@ namespace { >> /// relevant diagnostic and set the designator as invalid. >> bool checkSubobject(EvalInfo &Info, const Expr *E, CheckSubobjectKind >> CSK); >> >> + /// Get the type of the designated object. >> + QualType getType(ASTContext &Ctx) const { >> + return MostDerivedPathLength == Entries.size() >> + ? MostDerivedType >> + : Ctx.getRecordType(getAsBaseClass(Entries.back())); >> + } >> + >> /// Update this designator to refer to the first element within this >> array. >> void addArrayUnchecked(const ConstantArrayType *CAT) { >> PathEntry Entry; >> @@ -1706,6 +1732,54 @@ static bool IsGlobalLValue(APValue::LVal >> } >> } >> >> +static const ValueDecl *GetLValueBaseDecl(const LValue &LVal) { >> + return LVal.Base.dyn_cast<const ValueDecl*>(); >> +} >> + >> +static bool IsLiteralLValue(const LValue &Value) { >> + if (Value.getLValueCallIndex()) >> + return false; >> + const Expr *E = Value.Base.dyn_cast<const Expr*>(); >> + return E && !isa<MaterializeTemporaryExpr>(E); >> +} >> + >> +static bool IsWeakLValue(const LValue &Value) { >> + const ValueDecl *Decl = GetLValueBaseDecl(Value); >> + return Decl && Decl->isWeak(); >> +} >> + >> +static bool isZeroSized(const LValue &Value) { >> + const ValueDecl *Decl = GetLValueBaseDecl(Value); >> + if (Decl && isa<VarDecl>(Decl)) { >> + QualType Ty = Decl->getType(); >> + if (Ty->isArrayType()) >> + return Ty->isIncompleteType() || >> + Decl->getASTContext().getTypeSize(Ty) == 0; >> + } >> + return false; >> +} >> + >> +static bool HasSameBase(const LValue &A, const LValue &B) { >> + if (!A.getLValueBase()) >> + return !B.getLValueBase(); >> + if (!B.getLValueBase()) >> + return false; >> + >> + if (A.getLValueBase().getOpaqueValue() != >> + B.getLValueBase().getOpaqueValue()) { >> + const Decl *ADecl = GetLValueBaseDecl(A); >> + if (!ADecl) >> + return false; >> + const Decl *BDecl = GetLValueBaseDecl(B); >> + if (!BDecl || ADecl->getCanonicalDecl() != BDecl->getCanonicalDecl()) >> + return false; >> + } >> + >> + return IsGlobalLValue(A.getLValueBase()) || >> + (A.getLValueCallIndex() == B.getLValueCallIndex() && >> + A.getLValueVersion() == B.getLValueVersion()); >> +} >> + >> static void NoteLValueLocation(EvalInfo &Info, APValue::LValueBase Base) >> { >> assert(Base && "no location for a null lvalue"); >> const ValueDecl *VD = Base.dyn_cast<const ValueDecl*>(); >> @@ -1917,33 +1991,6 @@ CheckConstantExpression(EvalInfo &Info, >> return true; >> } >> >> -static const ValueDecl *GetLValueBaseDecl(const LValue &LVal) { >> - return LVal.Base.dyn_cast<const ValueDecl*>(); >> -} >> - >> -static bool IsLiteralLValue(const LValue &Value) { >> - if (Value.getLValueCallIndex()) >> - return false; >> - const Expr *E = Value.Base.dyn_cast<const Expr*>(); >> - return E && !isa<MaterializeTemporaryExpr>(E); >> -} >> - >> -static bool IsWeakLValue(const LValue &Value) { >> - const ValueDecl *Decl = GetLValueBaseDecl(Value); >> - return Decl && Decl->isWeak(); >> -} >> - >> -static bool isZeroSized(const LValue &Value) { >> - const ValueDecl *Decl = GetLValueBaseDecl(Value); >> - if (Decl && isa<VarDecl>(Decl)) { >> - QualType Ty = Decl->getType(); >> - if (Ty->isArrayType()) >> - return Ty->isIncompleteType() || >> - Decl->getASTContext().getTypeSize(Ty) == 0; >> - } >> - return false; >> -} >> - >> static bool EvalPointerValueAsBool(const APValue &Value, bool &Result) { >> // A null base expression indicates a null pointer. These are always >> // evaluatable, and they are false unless the offset is zero. >> @@ -6117,6 +6164,130 @@ bool PointerExprEvaluator::VisitBuiltinC >> return ZeroInitialization(E); >> } >> >> + case Builtin::BImemcpy: >> + case Builtin::BImemmove: >> + case Builtin::BIwmemcpy: >> + case Builtin::BIwmemmove: >> + if (Info.getLangOpts().CPlusPlus11) >> + Info.CCEDiag(E, diag::note_constexpr_invalid_function) >> + << /*isConstexpr*/0 << /*isConstructor*/0 >> + << (std::string("'") + Info.Ctx.BuiltinInfo.getName(BuiltinOp) + >> "'"); >> + else >> + Info.CCEDiag(E, diag::note_invalid_subexpr_in_const_expr); >> + LLVM_FALLTHROUGH; >> + case Builtin::BI__builtin_memcpy: >> + case Builtin::BI__builtin_memmove: >> + case Builtin::BI__builtin_wmemcpy: >> + case Builtin::BI__builtin_wmemmove: { >> + bool WChar = BuiltinOp == Builtin::BIwmemcpy || >> + BuiltinOp == Builtin::BIwmemmove || >> + BuiltinOp == Builtin::BI__builtin_wmemcpy || >> + BuiltinOp == Builtin::BI__builtin_wmemmove; >> + bool Move = BuiltinOp == Builtin::BImemmove || >> + BuiltinOp == Builtin::BIwmemmove || >> + BuiltinOp == Builtin::BI__builtin_memmove || >> + BuiltinOp == Builtin::BI__builtin_wmemmove; >> + >> + // The result of mem* is the first argument. >> + if (!Visit(E->getArg(0))) >> + return false; >> + LValue Dest = Result; >> + >> + LValue Src; >> + if (!EvaluatePointer(E->getArg(1), Src, Info)) >> + return false; >> + >> + APSInt N; >> + if (!EvaluateInteger(E->getArg(2), N, Info)) >> + return false; >> + assert(!N.isSigned() && "memcpy and friends take an unsigned size"); >> + >> + // If the size is zero, we treat this as always being a valid no-op. >> + // (Even if one of the src and dest pointers is null.) >> + if (!N) >> + return true; >> + >> + // We require that Src and Dest are both pointers to arrays of >> + // trivially-copyable type. (For the wide version, the designator >> will be >> + // invalid if the designated object is not a wchar_t.) >> + QualType T = Dest.Designator.getType(Info.Ctx); >> + QualType SrcT = Src.Designator.getType(Info.Ctx); >> + if (!Info.Ctx.hasSameUnqualifiedType(T, SrcT)) { >> + Info.FFDiag(E, diag::note_constexpr_memcpy_type_pun) << Move << >> SrcT << T; >> + return false; >> + } >> + if (!T.isTriviallyCopyableType(Info.Ctx)) { >> + Info.FFDiag(E, diag::note_constexpr_memcpy_nontrivial) << Move << >> T; >> + return false; >> + } >> + >> + // Figure out how many T's we're copying. >> + uint64_t TSize = Info.Ctx.getTypeSizeInChars(T).getQuantity(); >> + if (!WChar) { >> + uint64_t Remainder; >> + llvm::APInt OrigN = N; >> + llvm::APInt::udivrem(OrigN, TSize, N, Remainder); >> + if (Remainder) { >> + Info.FFDiag(E, diag::note_constexpr_memcpy_unsupported) >> + << Move << WChar << 0 << T << OrigN.toString(10, >> /*Signed*/false) >> + << (unsigned)TSize; >> + return false; >> + } >> + } >> + >> + // Check that the copying will remain within the arrays, just so that >> we >> + // can give a more meaningful diagnostic. This implicitly also checks >> that >> + // N fits into 64 bits. >> + uint64_t RemainingSrcSize = >> Src.Designator.validIndexAdjustments().second; >> + uint64_t RemainingDestSize = >> Dest.Designator.validIndexAdjustments().second; >> + if (N.ugt(RemainingSrcSize) || N.ugt(RemainingDestSize)) { >> + Info.FFDiag(E, diag::note_constexpr_memcpy_unsupported) >> + << Move << WChar << (N.ugt(RemainingSrcSize) ? 1 : 2) << T >> + << N.toString(10, /*Signed*/false); >> + return false; >> + } >> + uint64_t NElems = N.getZExtValue(); >> + uint64_t NBytes = NElems * TSize; >> + >> + // Check for overlap. >> + int Direction = 1; >> + if (HasSameBase(Src, Dest)) { >> + uint64_t SrcOffset = Src.getLValueOffset().getQuantity(); >> + uint64_t DestOffset = Dest.getLValueOffset().getQuantity(); >> + if (DestOffset >= SrcOffset && DestOffset - SrcOffset < NBytes) { >> + // Dest is inside the source region. >> + if (!Move) { >> + Info.FFDiag(E, diag::note_constexpr_memcpy_overlap) << WChar; >> + return false; >> + } >> + // For memmove and friends, copy backwards. >> + if (!HandleLValueArrayAdjustment(Info, E, Src, T, NElems - 1) || >> + !HandleLValueArrayAdjustment(Info, E, Dest, T, NElems - 1)) >> + return false; >> + Direction = -1; >> + } else if (!Move && SrcOffset >= DestOffset && >> + SrcOffset - DestOffset < NBytes) { >> + // Src is inside the destination region for memcpy: invalid. >> + Info.FFDiag(E, diag::note_constexpr_memcpy_overlap) << WChar; >> + return false; >> + } >> + } >> + >> + while (true) { >> + APValue Val; >> + if (!handleLValueToRValueConversion(Info, E, T, Src, Val) || >> + !handleAssignment(Info, E, Dest, T, Val)) >> + return false; >> + // Do not iterate past the last element; if we're copying >> backwards, that >> + // might take us off the start of the array. >> + if (--NElems == 0) >> + return true; >> + if (!HandleLValueArrayAdjustment(Info, E, Src, T, Direction) || >> + !HandleLValueArrayAdjustment(Info, E, Dest, T, Direction)) >> + return false; >> + } >> + } >> + >> default: >> return visitNonBuiltinCallExpr(E); >> } >> @@ -8357,27 +8528,6 @@ bool IntExprEvaluator::VisitBuiltinCallE >> } >> } >> >> -static bool HasSameBase(const LValue &A, const LValue &B) { >> - if (!A.getLValueBase()) >> - return !B.getLValueBase(); >> - if (!B.getLValueBase()) >> - return false; >> - >> - if (A.getLValueBase().getOpaqueValue() != >> - B.getLValueBase().getOpaqueValue()) { >> - const Decl *ADecl = GetLValueBaseDecl(A); >> - if (!ADecl) >> - return false; >> - const Decl *BDecl = GetLValueBaseDecl(B); >> - if (!BDecl || ADecl->getCanonicalDecl() != BDecl->getCanonicalDecl()) >> - return false; >> - } >> - >> - return IsGlobalLValue(A.getLValueBase()) || >> - (A.getLValueCallIndex() == B.getLValueCallIndex() && >> - A.getLValueVersion() == B.getLValueVersion()); >> -} >> - >> /// Determine whether this is a pointer past the end of the complete >> /// object referred to by the lvalue. >> static bool isOnePastTheEndOfCompleteObject(const ASTContext &Ctx, >> >> Modified: cfe/trunk/test/CodeGen/builtin-memfns.c >> URL: >> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/builtin-memfns.c?rev=338455&r1=338454&r2=338455&view=diff >> >> ============================================================================== >> --- cfe/trunk/test/CodeGen/builtin-memfns.c (original) >> +++ cfe/trunk/test/CodeGen/builtin-memfns.c Tue Jul 31 16:35:09 2018 >> @@ -1,5 +1,8 @@ >> // RUN: %clang_cc1 -triple i386-pc-linux-gnu -emit-llvm < %s| FileCheck >> %s >> >> +typedef __WCHAR_TYPE__ wchar_t; >> +typedef __SIZE_TYPE__ size_t; >> + >> // CHECK: @test1 >> // CHECK: call void @llvm.memset.p0i8.i32 >> // CHECK: call void @llvm.memset.p0i8.i32 >> @@ -83,3 +86,17 @@ void test9() { >> // CHECK: call void @llvm.memcpy{{.*}} align 16 {{.*}} align 16 {{.*}} >> 16, i1 false) >> __builtin_memcpy(x, y, sizeof(y)); >> } >> + >> +wchar_t dest; >> +wchar_t src; >> + >> +// CHECK-LABEL: @test10 >> +// FIXME: Consider lowering these to llvm.memcpy / llvm.memmove. >> +void test10() { >> + // CHECK: call i32* @wmemcpy(i32* @dest, i32* @src, i32 4) >> + __builtin_wmemcpy(&dest, &src, 4); >> + >> + // CHECK: call i32* @wmemmove(i32* @dest, i32* @src, i32 4) >> + __builtin_wmemmove(&dest, &src, 4); >> +} >> + >> >> Modified: cfe/trunk/test/SemaCXX/constexpr-string.cpp >> URL: >> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/constexpr-string.cpp?rev=338455&r1=338454&r2=338455&view=diff >> >> ============================================================================== >> --- cfe/trunk/test/SemaCXX/constexpr-string.cpp (original) >> +++ cfe/trunk/test/SemaCXX/constexpr-string.cpp Tue Jul 31 16:35:09 2018 >> @@ -1,6 +1,6 @@ >> -// RUN: %clang_cc1 %s -std=c++1z -fsyntax-only -verify -pedantic >> -// RUN: %clang_cc1 %s -std=c++1z -fsyntax-only -verify -pedantic >> -fno-signed-char >> -// RUN: %clang_cc1 %s -std=c++1z -fsyntax-only -verify -pedantic >> -fno-wchar -Dwchar_t=__WCHAR_TYPE__ >> +// RUN: %clang_cc1 %s -triple x86_64-linux-gnu -std=c++1z -fsyntax-only >> -verify -pedantic >> +// RUN: %clang_cc1 %s -triple x86_64-linux-gnu -std=c++1z -fsyntax-only >> -verify -pedantic -fno-signed-char >> +// RUN: %clang_cc1 %s -triple x86_64-linux-gnu -std=c++1z -fsyntax-only >> -verify -pedantic -fno-wchar -Dwchar_t=__WCHAR_TYPE__ >> >> # 6 "/usr/include/string.h" 1 3 4 >> extern "C" { >> @@ -14,10 +14,13 @@ extern "C" { >> >> extern char *strchr(const char *s, int c); >> extern void *memchr(const void *s, int c, size_t n); >> + >> + extern void *memcpy(void *d, const void *s, size_t n); >> + extern void *memmove(void *d, const void *s, size_t n); >> } >> -# 19 "SemaCXX/constexpr-string.cpp" 2 >> +# 22 "SemaCXX/constexpr-string.cpp" 2 >> >> -# 21 "/usr/include/wchar.h" 1 3 4 >> +# 24 "/usr/include/wchar.h" 1 3 4 >> extern "C" { >> extern size_t wcslen(const wchar_t *p); >> >> @@ -27,9 +30,12 @@ extern "C" { >> >> extern wchar_t *wcschr(const wchar_t *s, wchar_t c); >> extern wchar_t *wmemchr(const wchar_t *s, wchar_t c, size_t n); >> + >> + extern wchar_t *wmemcpy(wchar_t *d, const wchar_t *s, size_t n); >> + extern wchar_t *wmemmove(wchar_t *d, const wchar_t *s, size_t n); >> } >> >> -# 33 "SemaCXX/constexpr-string.cpp" 2 >> +# 39 "SemaCXX/constexpr-string.cpp" 2 >> namespace Strlen { >> constexpr int n = __builtin_strlen("hello"); // ok >> static_assert(n == 5); >> @@ -235,3 +241,133 @@ namespace WcschrEtc { >> constexpr bool a = !wcschr(L"hello", L'h'); // expected-error >> {{constant expression}} expected-note {{non-constexpr function 'wcschr' >> cannot be used in a constant expression}} >> constexpr bool b = !wmemchr(L"hello", L'h', 3); // expected-error >> {{constant expression}} expected-note {{non-constexpr function 'wmemchr' >> cannot be used in a constant expression}} >> } >> + >> +namespace MemcpyEtc { >> + template<typename T> >> + constexpr T result(T (&arr)[4]) { >> + return arr[0] * 1000 + arr[1] * 100 + arr[2] * 10 + arr[3]; >> + } >> + >> + constexpr int test_memcpy(int a, int b, int n) { >> + int arr[4] = {1, 2, 3, 4}; >> + __builtin_memcpy(arr + a, arr + b, n); >> + // expected-note@-1 2{{overlapping memory regions}} >> + // expected-note@-2 {{size to copy (1) is not a multiple of size of >> element type 'int'}} >> + // expected-note@-3 {{source is not a contiguous array of at least 2 >> elements of type 'int'}} >> + // expected-note@-4 {{destination is not a contiguous array of at >> least 3 elements of type 'int'}} >> + return result(arr); >> + } >> + constexpr int test_memmove(int a, int b, int n) { >> + int arr[4] = {1, 2, 3, 4}; >> + __builtin_memmove(arr + a, arr + b, n); >> + // expected-note@-1 {{size to copy (1) is not a multiple of size of >> element type 'int'}} >> + // expected-note@-2 {{source is not a contiguous array of at least 2 >> elements of type 'int'}} >> + // expected-note@-3 {{destination is not a contiguous array of at >> least 3 elements of type 'int'}} >> + return result(arr); >> + } >> + constexpr int test_wmemcpy(int a, int b, int n) { >> + wchar_t arr[4] = {1, 2, 3, 4}; >> + __builtin_wmemcpy(arr + a, arr + b, n); >> + // expected-note@-1 2{{overlapping memory regions}} >> + // expected-note-re@-2 {{source is not a contiguous array of at least >> 2 elements of type '{{wchar_t|int}}'}} >> + // expected-note-re@-3 {{destination is not a contiguous array of at >> least 3 elements of type '{{wchar_t|int}}'}} >> + return result(arr); >> + } >> + constexpr int test_wmemmove(int a, int b, int n) { >> + wchar_t arr[4] = {1, 2, 3, 4}; >> + __builtin_wmemmove(arr + a, arr + b, n); >> + // expected-note-re@-1 {{source is not a contiguous array of at least >> 2 elements of type '{{wchar_t|int}}'}} >> + // expected-note-re@-2 {{destination is not a contiguous array of at >> least 3 elements of type '{{wchar_t|int}}'}} >> + return result(arr); >> + } >> + >> + static_assert(test_memcpy(1, 2, 4) == 1334); >> + static_assert(test_memcpy(2, 1, 4) == 1224); >> + static_assert(test_memcpy(0, 1, 8) == 2334); // expected-error >> {{constant}} expected-note {{in call}} >> + static_assert(test_memcpy(1, 0, 8) == 1124); // expected-error >> {{constant}} expected-note {{in call}} >> + static_assert(test_memcpy(1, 2, 1) == 1334); // expected-error >> {{constant}} expected-note {{in call}} >> + static_assert(test_memcpy(0, 3, 4) == 4234); >> + static_assert(test_memcpy(0, 3, 8) == 4234); // expected-error >> {{constant}} expected-note {{in call}} >> + static_assert(test_memcpy(2, 0, 12) == 4234); // expected-error >> {{constant}} expected-note {{in call}} >> + >> + static_assert(test_memmove(1, 2, 4) == 1334); >> + static_assert(test_memmove(2, 1, 4) == 1224); >> + static_assert(test_memmove(0, 1, 8) == 2334); >> + static_assert(test_memmove(1, 0, 8) == 1124); >> + static_assert(test_memmove(1, 2, 1) == 1334); // expected-error >> {{constant}} expected-note {{in call}} >> + static_assert(test_memmove(0, 3, 4) == 4234); >> + static_assert(test_memmove(0, 3, 8) == 4234); // expected-error >> {{constant}} expected-note {{in call}} >> + static_assert(test_memmove(2, 0, 12) == 4234); // expected-error >> {{constant}} expected-note {{in call}} >> + >> + static_assert(test_wmemcpy(1, 2, 1) == 1334); >> + static_assert(test_wmemcpy(2, 1, 1) == 1224); >> + static_assert(test_wmemcpy(0, 1, 2) == 2334); // expected-error >> {{constant}} expected-note {{in call}} >> + static_assert(test_wmemcpy(1, 0, 2) == 1124); // expected-error >> {{constant}} expected-note {{in call}} >> + static_assert(test_wmemcpy(1, 2, 1) == 1334); >> + static_assert(test_wmemcpy(0, 3, 1) == 4234); >> + static_assert(test_wmemcpy(0, 3, 2) == 4234); // expected-error >> {{constant}} expected-note {{in call}} >> + static_assert(test_wmemcpy(2, 0, 3) == 4234); // expected-error >> {{constant}} expected-note {{in call}} >> + >> + static_assert(test_wmemmove(1, 2, 1) == 1334); >> + static_assert(test_wmemmove(2, 1, 1) == 1224); >> + static_assert(test_wmemmove(0, 1, 2) == 2334); >> + static_assert(test_wmemmove(1, 0, 2) == 1124); >> + static_assert(test_wmemmove(1, 2, 1) == 1334); >> + static_assert(test_wmemmove(0, 3, 1) == 4234); >> + static_assert(test_wmemmove(0, 3, 2) == 4234); // expected-error >> {{constant}} expected-note {{in call}} >> + static_assert(test_wmemmove(2, 0, 3) == 4234); // expected-error >> {{constant}} expected-note {{in call}} >> + >> + // Copying is permitted for any trivially-copyable type. >> + struct Trivial { char k; short s; constexpr bool ok() { return k == 3 >> && s == 4; } }; >> + constexpr bool test_trivial() { >> + Trivial arr[3] = {{1, 2}, {3, 4}, {5, 6}}; >> + __builtin_memcpy(arr, arr+1, sizeof(Trivial)); >> + __builtin_memmove(arr+1, arr, 2 * sizeof(Trivial)); >> + return arr[0].ok() && arr[1].ok() && arr[2].ok(); >> + } >> + static_assert(test_trivial()); >> + >> + // But not for a non-trivially-copyable type. >> + struct NonTrivial { >> + constexpr NonTrivial() : n(0) {} >> + constexpr NonTrivial(const NonTrivial &) : n(1) {} >> + int n; >> + }; >> + constexpr bool test_nontrivial_memcpy() { // expected-error {{never >> produces a constant}} >> + NonTrivial arr[3] = {}; >> + __builtin_memcpy(arr, arr + 1, sizeof(NonTrivial)); // expected-note >> 2{{non-trivially-copyable}} >> + return true; >> + } >> + static_assert(test_nontrivial_memcpy()); // expected-error {{constant}} >> expected-note {{in call}} >> + constexpr bool test_nontrivial_memmove() { // expected-error {{never >> produces a constant}} >> + NonTrivial arr[3] = {}; >> + __builtin_memcpy(arr, arr + 1, sizeof(NonTrivial)); // expected-note >> 2{{non-trivially-copyable}} >> + return true; >> + } >> + static_assert(test_nontrivial_memmove()); // expected-error >> {{constant}} expected-note {{in call}} >> + >> + // Type puns via constant evaluated memcpy are not supported yet. >> + constexpr float type_pun(const unsigned &n) { >> + float f = 0.0f; >> + __builtin_memcpy(&f, &n, 4); // expected-note {{cannot constant >> evaluate 'memcpy' from object of type 'const unsigned int' to object of type >> 'float'}} >> + return f; >> + } >> + static_assert(type_pun(0x3f800000) == 1.0f); // expected-error >> {{constant}} expected-note {{in call}} >> + >> + // Make sure we're not confused by derived-to-base conversions. >> + struct Base { int a; }; >> + struct Derived : Base { int b; }; >> + constexpr int test_derived_to_base(int n) { >> + Derived arr[2] = {1, 2, 3, 4}; >> + Base *p = &arr[0]; >> + Base *q = &arr[1]; >> + __builtin_memcpy(p, q, sizeof(Base) * n); // expected-note {{source >> is not a contiguous array of at least 2 elements of type 'MemcpyEtc::Base'}} >> + return arr[0].a * 1000 + arr[0].b * 100 + arr[1].a * 10 + arr[1].b; >> + } >> + static_assert(test_derived_to_base(0) == 1234); >> + static_assert(test_derived_to_base(1) == 3234); >> + // FIXME: We could consider making this work by stripping elements off >> both >> + // designators until we have a long enough matching size, if both >> designators >> + // point to the start of their respective final elements. >> + static_assert(test_derived_to_base(2) == 3434); // expected-error >> {{constant}} expected-note {{in call}} >> +} >> >> >> _______________________________________________ >> cfe-commits mailing list >> cfe-commits@lists.llvm.org >> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits > > > _______________________________________________ > cfe-commits mailing list > cfe-commits@lists.llvm.org > http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits > _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits