petpav01 created this revision. petpav01 added a reviewer: rsmith. Herald added a reviewer: javed.absar. Herald added subscribers: cfe-commits, kristof.beyls.
Trying to compile the following example results in a clang crash: $ cat char.c void *memcpy(void *, const void *, unsigned int); extern char array2[]; extern char array[]; void test(void) { memcpy(&array, &array2, 9 * sizeof(char)); } $ ./llvm/build/bin/clang -target armv8a-none-eabi -c char.c clang-8: /work/llvm/lib/Support/APInt.cpp:1785: static void llvm::APInt::udivrem(const llvm::APInt&, uint64_t, llvm::APInt&, uint64_t&): Assertion `RHS != 0 && "Divide by zero?"' failed. [...] The problem occurs since rC338941 <https://reviews.llvm.org/rC338941>. The change added support for constant evaluation of `__builtin_memcpy/memmove()` but it does not always cope well with incomplete types. The AST for the `memcpy()` call looks as follows: CallExpr 0x7416d0 'void *' |-ImplicitCastExpr 0x7416b8 'void *(*)(void *, const void *, unsigned int)' <FunctionToPointerDecay> | `-DeclRefExpr 0x741518 'void *(void *, const void *, unsigned int)' Function 0x741198 'memcpy' 'void *(void *, const void *, unsigned int)' |-ImplicitCastExpr 0x741710 'void *' <BitCast> | `-UnaryOperator 0x741598 'char (*)[]' prefix '&' cannot overflow | `-DeclRefExpr 0x741540 'char []' lvalue Var 0x741358 'array' 'char []' |-ImplicitCastExpr 0x741728 'const void *' <BitCast> | `-UnaryOperator 0x7415e0 'char (*)[]' prefix '&' cannot overflow | `-DeclRefExpr 0x7415b8 'char []' lvalue Var 0x741298 'array2' 'char []' `-BinaryOperator 0x741668 'unsigned int' '*' |-ImplicitCastExpr 0x741650 'unsigned int' <IntegralCast> | `-IntegerLiteral 0x741600 'int' 9 `-UnaryExprOrTypeTraitExpr 0x741630 'unsigned int' sizeof 'char' The following happens in `PointerExprEvaluator::VisitBuiltinCallExpr()`, label `Builtin::BI__builtin_memcpy`: - Types `T` and `SrcT` are determined as: IncompleteArrayType 0x741250 'char []' `-BuiltinType 0x6ff430 'char' - Method `ASTContext::getTypeSizeInChars()` is called to obtain size of type `T`. It returns 0 because the type is incomplete. The result is stored in variable `TSize`. - Following call to `llvm::APInt::udivrem(OrigN, TSize, N, Remainder)` fails because it attempts a divide by zero. The proposed patch fixes the problem by adding a check that no incomplete type is getting copied prior to the call to `ASTContext::getTypeSizeInChars()`. Repository: rC Clang https://reviews.llvm.org/D51855 Files: include/clang/Basic/DiagnosticASTKinds.td lib/AST/ExprConstant.cpp test/CodeGen/builtin-memfns.c test/SemaCXX/constexpr-string.cpp Index: test/SemaCXX/constexpr-string.cpp =================================================================== --- test/SemaCXX/constexpr-string.cpp +++ test/SemaCXX/constexpr-string.cpp @@ -370,4 +370,31 @@ // 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}} + + // Check that when address-of an array is passed to a tested function the + // array can be fully copied. + constexpr int test_address_of_const_array_type() { + int arr[4] = {1, 2, 3, 4}; + __builtin_memmove(&arr, &arr, sizeof(arr)); + return arr[0] * 1000 + arr[1] * 100 + arr[2] * 10 + arr[3]; + } + static_assert(test_address_of_const_array_type() == 1234); + + // Check that an incomplete array is rejected. + constexpr int test_incomplete_array_type() { // expected-error {{never produces a constant}} + extern int arr[]; + __builtin_memmove(arr, arr, 4 * sizeof(arr[0])); + // expected-note@-1 2{{'memmove' not supported: source is not a contiguous array of at least 4 elements of type 'int'}} + return arr[0] * 1000 + arr[1] * 100 + arr[2] * 10 + arr[3]; + } + static_assert(test_incomplete_array_type() == 1234); // expected-error {{constant}} expected-note {{in call}} + + // Check that a pointer to an incomplete array is rejected. + constexpr int test_address_of_incomplete_array_type() { // expected-error {{never produces a constant}} + extern int arr[]; + __builtin_memmove(&arr, &arr, 4 * sizeof(arr[0])); + // expected-note@-1 2{{cannot constant evaluate 'memmove' between objects of incomplete type 'int []'}} + return arr[0] * 1000 + arr[1] * 100 + arr[2] * 10 + arr[3]; + } + static_assert(test_address_of_incomplete_array_type() == 1234); // expected-error {{constant}} expected-note {{in call}} } Index: test/CodeGen/builtin-memfns.c =================================================================== --- test/CodeGen/builtin-memfns.c +++ test/CodeGen/builtin-memfns.c @@ -111,3 +111,10 @@ memcpy(&d, (char *)&e.a, sizeof(e)); } +// CHECK-LABEL: @test12 +extern char dest_array[]; +extern char src_array[]; +void test12() { + // CHECK: call void @llvm.memcpy{{.*}}( + memcpy(&dest_array, &dest_array, 2); +} Index: lib/AST/ExprConstant.cpp =================================================================== --- lib/AST/ExprConstant.cpp +++ lib/AST/ExprConstant.cpp @@ -6222,6 +6222,10 @@ Info.FFDiag(E, diag::note_constexpr_memcpy_nontrivial) << Move << T; return false; } + if (T->isIncompleteType()) { + Info.FFDiag(E, diag::note_constexpr_memcpy_incompletetype) << Move << T; + return false; + } // Figure out how many T's we're copying. uint64_t TSize = Info.Ctx.getTypeSizeInChars(T).getQuantity(); Index: include/clang/Basic/DiagnosticASTKinds.td =================================================================== --- include/clang/Basic/DiagnosticASTKinds.td +++ include/clang/Basic/DiagnosticASTKinds.td @@ -169,6 +169,9 @@ 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_incompletetype : Note< + "cannot constant evaluate '%select{memcpy|memmove}0' between objects of " + "incomplete type %1">; def note_constexpr_memcpy_overlap : Note< "'%select{memcpy|wmemcpy}0' between overlapping memory regions">; def note_constexpr_memcpy_unsupported : Note<
Index: test/SemaCXX/constexpr-string.cpp =================================================================== --- test/SemaCXX/constexpr-string.cpp +++ test/SemaCXX/constexpr-string.cpp @@ -370,4 +370,31 @@ // 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}} + + // Check that when address-of an array is passed to a tested function the + // array can be fully copied. + constexpr int test_address_of_const_array_type() { + int arr[4] = {1, 2, 3, 4}; + __builtin_memmove(&arr, &arr, sizeof(arr)); + return arr[0] * 1000 + arr[1] * 100 + arr[2] * 10 + arr[3]; + } + static_assert(test_address_of_const_array_type() == 1234); + + // Check that an incomplete array is rejected. + constexpr int test_incomplete_array_type() { // expected-error {{never produces a constant}} + extern int arr[]; + __builtin_memmove(arr, arr, 4 * sizeof(arr[0])); + // expected-note@-1 2{{'memmove' not supported: source is not a contiguous array of at least 4 elements of type 'int'}} + return arr[0] * 1000 + arr[1] * 100 + arr[2] * 10 + arr[3]; + } + static_assert(test_incomplete_array_type() == 1234); // expected-error {{constant}} expected-note {{in call}} + + // Check that a pointer to an incomplete array is rejected. + constexpr int test_address_of_incomplete_array_type() { // expected-error {{never produces a constant}} + extern int arr[]; + __builtin_memmove(&arr, &arr, 4 * sizeof(arr[0])); + // expected-note@-1 2{{cannot constant evaluate 'memmove' between objects of incomplete type 'int []'}} + return arr[0] * 1000 + arr[1] * 100 + arr[2] * 10 + arr[3]; + } + static_assert(test_address_of_incomplete_array_type() == 1234); // expected-error {{constant}} expected-note {{in call}} } Index: test/CodeGen/builtin-memfns.c =================================================================== --- test/CodeGen/builtin-memfns.c +++ test/CodeGen/builtin-memfns.c @@ -111,3 +111,10 @@ memcpy(&d, (char *)&e.a, sizeof(e)); } +// CHECK-LABEL: @test12 +extern char dest_array[]; +extern char src_array[]; +void test12() { + // CHECK: call void @llvm.memcpy{{.*}}( + memcpy(&dest_array, &dest_array, 2); +} Index: lib/AST/ExprConstant.cpp =================================================================== --- lib/AST/ExprConstant.cpp +++ lib/AST/ExprConstant.cpp @@ -6222,6 +6222,10 @@ Info.FFDiag(E, diag::note_constexpr_memcpy_nontrivial) << Move << T; return false; } + if (T->isIncompleteType()) { + Info.FFDiag(E, diag::note_constexpr_memcpy_incompletetype) << Move << T; + return false; + } // Figure out how many T's we're copying. uint64_t TSize = Info.Ctx.getTypeSizeInChars(T).getQuantity(); Index: include/clang/Basic/DiagnosticASTKinds.td =================================================================== --- include/clang/Basic/DiagnosticASTKinds.td +++ include/clang/Basic/DiagnosticASTKinds.td @@ -169,6 +169,9 @@ 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_incompletetype : Note< + "cannot constant evaluate '%select{memcpy|memmove}0' between objects of " + "incomplete type %1">; def note_constexpr_memcpy_overlap : Note< "'%select{memcpy|wmemcpy}0' between overlapping memory regions">; def note_constexpr_memcpy_unsupported : Note<
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits