r338941 - [constexpr] Support for constant evaluation of __builtin_memcpy and

Shoaib Meenai via cfe-commits cfe-commits at lists.llvm.org
Mon Nov 26 18:03:27 PST 2018


Uhh, we have our own build that uses the release branch, but somehow we didn’t have that commit in there. Not sure how that happened, but sorry for the noise.

From: cfe-commits <cfe-commits-bounces at lists.llvm.org> on behalf of Richard Smith via cfe-commits <cfe-commits at lists.llvm.org>
Reply-To: Richard Smith <richard at metafoo.co.uk>
Date: Monday, November 26, 2018 at 12:01 PM
To: Shoaib Meenai <shoaib.meenai at gmail.com>
Cc: cfe-commits <cfe-commits at lists.llvm.org>, Tom Stellard <tstellar at redhat.com>
Subject: Re: r338941 - [constexpr] Support for constant evaluation of __builtin_memcpy and

On Mon, 26 Nov 2018, 11:50 Richard Smith <richard at metafoo.co.uk<mailto:richard at metafoo.co.uk> wrote:
On Wed, 21 Nov 2018, 15:32 Shoaib Meenai via cfe-commits <cfe-commits at lists.llvm.org<mailto:cfe-commits at lists.llvm.org> wrote:
If it's not too late, could we have this as part of 7.0.1? (You'll also need to cherry-pick the initial reversion in r338602.)

The revert was cherrypicked onto the branch in r338674. Was that not in time for 7.0?

Looks like the revert was in fact the very first thing cherrypicked into clang's release_70 branch after the branch was cut, so it should certainly have been in time for 7.0. Are you sure your clang binary is really the 7.0 release?

I don't think we should take this as a new feature for 7.0.1.

7.0 hits assertion failures for pretty basic memcpy cases on windows-msvc targets, and this patch fixes that.

% cat /tmp/reduced.c
void *memcpy(void *, const void *, __SIZE_TYPE__);
void f(int i) {
  struct { int i } s;
  memcpy((char *)&s.i, &i, sizeof(i));
}

% clang -cc1 -triple x86_64-windows-msvc -emit-llvm -fms-compatibility -o /dev/null /tmp/reduced.c
llvm::SmallVectorTemplateCommon::const_reference llvm::SmallVectorTemplateCommon<clang::APValue::LValuePathEntry, void>::back() const [T = clang::APValue::LValuePathEntry]: Assertion `!empty()' failed.

On Fri, Aug 3, 2018 at 5:57 PM Richard Smith via cfe-commits <cfe-commits at lists.llvm.org<mailto:cfe-commits at lists.llvm.org>> wrote:
Author: rsmith
Date: Fri Aug  3 17:57:17 2018
New Revision: 338941

URL: http://llvm.org/viewvc/llvm-project?rev=338941&view=rev<https://urldefense.proofpoint.com/v2/url?u=http-3A__llvm.org_viewvc_llvm-2Dproject-3Frev-3D338941-26view-3Drev&d=DwMFaQ&c=5VD0RTtNlTh3ycd41b3MUw&r=o3kDXzdBUE3ljQXKeTWOMw&m=Ru-mCU1Dn9QNNn6nn7wO6RrDpRjNecPIDS4JqFjlaDQ&s=kLZGidrWPNCR2HKca-VmTjfl22m1UcG_BNKpUqW8AbU&e=>
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.

This reinstates r338455, reverted in r338602, with a fix to avoid trying
to constant-evaluate a memcpy call if either pointer operand has an
invalid designator.

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=338941&r1=338940&r2=338941&view=diff<https://urldefense.proofpoint.com/v2/url?u=http-3A__llvm.org_viewvc_llvm-2Dproject_cfe_trunk_include_clang_Basic_Builtins.def-3Frev-3D338941-26r1-3D338940-26r2-3D338941-26view-3Ddiff&d=DwMFaQ&c=5VD0RTtNlTh3ycd41b3MUw&r=o3kDXzdBUE3ljQXKeTWOMw&m=Ru-mCU1Dn9QNNn6nn7wO6RrDpRjNecPIDS4JqFjlaDQ&s=agZQwByY7d4bRMtTI9wisVrDwIsrXQd4SFurViV23qU&e=>
==============================================================================
--- cfe/trunk/include/clang/Basic/Builtins.def (original)
+++ cfe/trunk/include/clang/Basic/Builtins.def Fri Aug  3 17:57:17 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=338941&r1=338940&r2=338941&view=diff<https://urldefense.proofpoint.com/v2/url?u=http-3A__llvm.org_viewvc_llvm-2Dproject_cfe_trunk_include_clang_Basic_DiagnosticASTKinds.td-3Frev-3D338941-26r1-3D338940-26r2-3D338941-26view-3Ddiff&d=DwMFaQ&c=5VD0RTtNlTh3ycd41b3MUw&r=o3kDXzdBUE3ljQXKeTWOMw&m=Ru-mCU1Dn9QNNn6nn7wO6RrDpRjNecPIDS4JqFjlaDQ&s=d-lncS2zdX44tHKq8u4wXeMsmXnWetztWj3QrH7MRAg&e=>
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td Fri Aug  3 17:57:17 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=338941&r1=338940&r2=338941&view=diff<https://urldefense.proofpoint.com/v2/url?u=http-3A__llvm.org_viewvc_llvm-2Dproject_cfe_trunk_lib_AST_ExprConstant.cpp-3Frev-3D338941-26r1-3D338940-26r2-3D338941-26view-3Ddiff&d=DwMFaQ&c=5VD0RTtNlTh3ycd41b3MUw&r=o3kDXzdBUE3ljQXKeTWOMw&m=Ru-mCU1Dn9QNNn6nn7wO6RrDpRjNecPIDS4JqFjlaDQ&s=VVRABWH1hyVLk5tZc1TeQ2MAPG3w4t861QiHwxZwi_g&e=>
==============================================================================
--- cfe/trunk/lib/AST/ExprConstant.cpp (original)
+++ cfe/trunk/lib/AST/ExprConstant.cpp Fri Aug  3 17:57:17 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,14 @@ 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 {
+      assert(!Invalid && "invalid designator has no subobject type");
+      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 +1733,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 +1992,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 +6165,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)) || Result.Designator.Invalid)
+      return false;
+    LValue Dest = Result;
+
+    LValue Src;
+    if (!EvaluatePointer(E->getArg(1), Src, Info) || Src.Designator.Invalid)
+      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 +8529,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=338941&r1=338940&r2=338941&view=diff<https://urldefense.proofpoint.com/v2/url?u=http-3A__llvm.org_viewvc_llvm-2Dproject_cfe_trunk_test_CodeGen_builtin-2Dmemfns.c-3Frev-3D338941-26r1-3D338940-26r2-3D338941-26view-3Ddiff&d=DwMFaQ&c=5VD0RTtNlTh3ycd41b3MUw&r=o3kDXzdBUE3ljQXKeTWOMw&m=Ru-mCU1Dn9QNNn6nn7wO6RrDpRjNecPIDS4JqFjlaDQ&s=LPPZygVNf8JIFrEYD5JEkJuiThAp6zynZ5cMd5XnoWs&e=>
==============================================================================
--- cfe/trunk/test/CodeGen/builtin-memfns.c (original)
+++ cfe/trunk/test/CodeGen/builtin-memfns.c Fri Aug  3 17:57:17 2018
@@ -1,5 +1,10 @@
 // RUN: %clang_cc1 -triple i386-pc-linux-gnu -emit-llvm < %s| FileCheck %s

+typedef __WCHAR_TYPE__ wchar_t;
+typedef __SIZE_TYPE__ size_t;
+
+void *memcpy(void *, void const *, size_t);
+
 // CHECK: @test1
 // CHECK: call void @llvm.memset.p0i8.i32
 // CHECK: call void @llvm.memset.p0i8.i32
@@ -83,3 +88,26 @@ 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);
+}
+
+// CHECK-LABEL: @test11
+void test11() {
+  typedef struct { int a; } b;
+  int d;
+  b e;
+  // CHECK: call void @llvm.memcpy{{.*}}(
+  memcpy(&d, (char *)&e.a, sizeof(e));
+}
+

Modified: cfe/trunk/test/SemaCXX/constexpr-string.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/constexpr-string.cpp?rev=338941&r1=338940&r2=338941&view=diff<https://urldefense.proofpoint.com/v2/url?u=http-3A__llvm.org_viewvc_llvm-2Dproject_cfe_trunk_test_SemaCXX_constexpr-2Dstring.cpp-3Frev-3D338941-26r1-3D338940-26r2-3D338941-26view-3Ddiff&d=DwMFaQ&c=5VD0RTtNlTh3ycd41b3MUw&r=o3kDXzdBUE3ljQXKeTWOMw&m=Ru-mCU1Dn9QNNn6nn7wO6RrDpRjNecPIDS4JqFjlaDQ&s=7q4587TzZEZWRUPTl4HsmVnOUI3aGmt1a3XDaiDyDWs&e=>
==============================================================================
--- cfe/trunk/test/SemaCXX/constexpr-string.cpp (original)
+++ cfe/trunk/test/SemaCXX/constexpr-string.cpp Fri Aug  3 17:57:17 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 at -1 2{{overlapping memory regions}}
+    // expected-note at -2 {{size to copy (1) is not a multiple of size of element type 'int'}}
+    // expected-note at -3 {{source is not a contiguous array of at least 2 elements of type 'int'}}
+    // expected-note at -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 at -1 {{size to copy (1) is not a multiple of size of element type 'int'}}
+    // expected-note at -2 {{source is not a contiguous array of at least 2 elements of type 'int'}}
+    // expected-note at -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 at -1 2{{overlapping memory regions}}
+    // expected-note-re at -2 {{source is not a contiguous array of at least 2 elements of type '{{wchar_t|int}}'}}
+    // expected-note-re at -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 at -1 {{source is not a contiguous array of at least 2 elements of type '{{wchar_t|int}}'}}
+    // expected-note-re at -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 at lists.llvm.org<mailto:cfe-commits at lists.llvm.org>
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits<https://urldefense.proofpoint.com/v2/url?u=http-3A__lists.llvm.org_cgi-2Dbin_mailman_listinfo_cfe-2Dcommits&d=DwMFaQ&c=5VD0RTtNlTh3ycd41b3MUw&r=o3kDXzdBUE3ljQXKeTWOMw&m=Ru-mCU1Dn9QNNn6nn7wO6RrDpRjNecPIDS4JqFjlaDQ&s=GOW-PBr4oTf0NNNsd0DJDqlu9QXg9Oifo4xXcnDITno&e=>
_______________________________________________
cfe-commits mailing list
cfe-commits at lists.llvm.org<mailto:cfe-commits at lists.llvm.org>
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits<https://urldefense.proofpoint.com/v2/url?u=http-3A__lists.llvm.org_cgi-2Dbin_mailman_listinfo_cfe-2Dcommits&d=DwMFaQ&c=5VD0RTtNlTh3ycd41b3MUw&r=o3kDXzdBUE3ljQXKeTWOMw&m=Ru-mCU1Dn9QNNn6nn7wO6RrDpRjNecPIDS4JqFjlaDQ&s=GOW-PBr4oTf0NNNsd0DJDqlu9QXg9Oifo4xXcnDITno&e=>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20181127/96ed0b8c/attachment-0001.html>


More information about the cfe-commits mailing list