Index: clang/lib/AST/Interp/InterpBuiltin.cpp =================================================================== --- clang/lib/AST/Interp/InterpBuiltin.cpp +++ clang/lib/AST/Interp/InterpBuiltin.cpp @@ -80,21 +80,81 @@ llvm_unreachable("size_t isn't 64 or 32 bit?"); } +static bool usableInStrcmp(const Pointer &P) { + if (!P.getFieldDesc()->isPrimitiveArray()) + return false; + + const ArrayType *AT = P.getFieldDesc()->getType()->getAsArrayTypeUnsafe(); + if (!AT->getElementType()->isCharType() && + !AT->getElementType()->isChar8Type()) + return false; + + return true; +} + static bool interp__builtin_strcmp(InterpState &S, CodePtr OpPC, - const InterpFrame *Frame) { + const InterpFrame *Frame, + const Function *F) { const Pointer &A = getParam(Frame, 0); const Pointer &B = getParam(Frame, 1); + unsigned BuiltinOp = F->getBuiltinID(); + uint64_t MaxLength = uint64_t(-1); + if (BuiltinOp != Builtin::BIstrcmp && BuiltinOp != Builtin::BIwcscmp && + BuiltinOp != Builtin::BI__builtin_strcmp && + BuiltinOp != Builtin::BI__builtin_wcscmp) { + APSInt N = + peekToAPSInt(S.Stk, *S.getContext().classify(S.getCtx().getSizeType())); + MaxLength = N.getExtValue(); + } + + // Empty substrings compare equal by definition. + if (MaxLength == 0) { + pushInt(S, 0); + return true; + } + if (!CheckLive(S, OpPC, A, AK_Read) || !CheckLive(S, OpPC, B, AK_Read)) return false; + bool IsRawByte = BuiltinOp == Builtin::BImemcmp || + BuiltinOp == Builtin::BIbcmp || + BuiltinOp == Builtin::BI__builtin_memcmp || + BuiltinOp == Builtin::BI__builtin_bcmp; + // FIXME: We could support comparing arbitary pointers by first bitcasting to + // a buffer. + if (IsRawByte && (!usableInStrcmp(A) || !usableInStrcmp(B))) { + const Expr *E = Frame->Caller->getExpr(OpPC); + S.FFDiag(E, diag::note_constexpr_memcmp_unsupported) + << ("'" + S.getCtx().BuiltinInfo.getName(BuiltinOp) + "'").str() + << A.getType() << B.getType() << E->getSourceRange(); + return false; + } + + bool StopAtNull = BuiltinOp != Builtin::BImemcmp && + BuiltinOp != Builtin::BIbcmp && + BuiltinOp != Builtin::BIwmemcmp && + BuiltinOp != Builtin::BI__builtin_memcmp && + BuiltinOp != Builtin::BI__builtin_bcmp && + BuiltinOp != Builtin::BI__builtin_wmemcmp; + bool IsWide = BuiltinOp == Builtin::BIwcscmp || + BuiltinOp == Builtin::BIwcsncmp || + BuiltinOp == Builtin::BIwmemcmp || + BuiltinOp == Builtin::BI__builtin_wcscmp || + BuiltinOp == Builtin::BI__builtin_wcsncmp || + BuiltinOp == Builtin::BI__builtin_wmemcmp; + + // FIXME: Implement support for the wide versions. + if (IsWide) + return false; + assert(A.getFieldDesc()->isPrimitiveArray()); assert(B.getFieldDesc()->isPrimitiveArray()); - unsigned IndexA = A.getIndex(); - unsigned IndexB = B.getIndex(); + size_t IndexA = A.getIndex(); + size_t IndexB = B.getIndex(); int32_t Result = 0; - for (;; ++IndexA, ++IndexB) { + for (size_t I = 0; I < MaxLength; ++IndexA, ++IndexB, ++I) { const Pointer &PA = A.atIndex(IndexA); const Pointer &PB = B.atIndex(IndexB); if (!CheckRange(S, OpPC, PA, AK_Read) || @@ -111,7 +171,7 @@ Result = -1; break; } - if (CA == 0 || CB == 0) + if (StopAtNull && (CA == 0 || CB == 0)) break; } @@ -402,7 +462,13 @@ case Builtin::BI__builtin_assume: return RetVoid(S, OpPC, Dummy); case Builtin::BI__builtin_strcmp: - if (interp__builtin_strcmp(S, OpPC, Frame)) + case Builtin::BI__builtin_wcscmp: + case Builtin::BI__builtin_strncmp: + case Builtin::BI__builtin_wcsncmp: + case Builtin::BI__builtin_memcmp: + case Builtin::BI__builtin_bcmp: + case Builtin::BI__builtin_wmemcmp: + if (interp__builtin_strcmp(S, OpPC, Frame, F)) return retInt(S, OpPC, Dummy); break; case Builtin::BI__builtin_strlen: Index: clang/test/AST/Interp/builtin-functions.cpp =================================================================== --- clang/test/AST/Interp/builtin-functions.cpp +++ clang/test/AST/Interp/builtin-functions.cpp @@ -5,39 +5,228 @@ // RUN: %clang_cc1 -std=c++20 -Wno-string-plus-int -fexperimental-new-constant-interpreter -triple i686 %s -verify // RUN: %clang_cc1 -std=c++20 -Wno-string-plus-int -verify=ref %s -Wno-constant-evaluated - -namespace strcmp { +namespace StrcmpEtc { constexpr char kFoobar[6] = {'f','o','o','b','a','r'}; constexpr char kFoobazfoobar[12] = {'f','o','o','b','a','z','f','o','o','b','a','r'}; - static_assert(__builtin_strcmp("", "") == 0, ""); - static_assert(__builtin_strcmp("abab", "abab") == 0, ""); - static_assert(__builtin_strcmp("abab", "abba") == -1, ""); - static_assert(__builtin_strcmp("abab", "abaa") == 1, ""); - static_assert(__builtin_strcmp("ababa", "abab") == 1, ""); - static_assert(__builtin_strcmp("abab", "ababa") == -1, ""); - static_assert(__builtin_strcmp("a\203", "a") == 1, ""); - static_assert(__builtin_strcmp("a\203", "a\003") == 1, ""); - static_assert(__builtin_strcmp("abab\0banana", "abab") == 0, ""); - static_assert(__builtin_strcmp("abab", "abab\0banana") == 0, ""); - static_assert(__builtin_strcmp("abab\0banana", "abab\0canada") == 0, ""); - static_assert(__builtin_strcmp(0, "abab") == 0, ""); // expected-error {{not an integral constant}} \ - // expected-note {{dereferenced null}} \ - // expected-note {{in call to}} \ - // ref-error {{not an integral constant}} \ - // ref-note {{dereferenced null}} - static_assert(__builtin_strcmp("abab", 0) == 0, ""); // expected-error {{not an integral constant}} \ - // expected-note {{dereferenced null}} \ - // expected-note {{in call to}} \ - // ref-error {{not an integral constant}} \ - // ref-note {{dereferenced null}} - - static_assert(__builtin_strcmp(kFoobar, kFoobazfoobar) == -1, ""); - static_assert(__builtin_strcmp(kFoobar, kFoobazfoobar + 6) == 0, ""); // expected-error {{not an integral constant}} \ + static_assert(__builtin_strncmp("abaa", "abba", 5) == -1); + static_assert(__builtin_strncmp("abaa", "abba", 4) == -1); + static_assert(__builtin_strncmp("abaa", "abba", 3) == -1); + static_assert(__builtin_strncmp("abaa", "abba", 2) == 0); + static_assert(__builtin_strncmp("abaa", "abba", 1) == 0); + static_assert(__builtin_strncmp("abaa", "abba", 0) == 0); + static_assert(__builtin_strncmp(0, 0, 0) == 0); + static_assert(__builtin_strncmp("abab\0banana", "abab\0canada", 100) == 0); + + static_assert(__builtin_strncmp(kFoobar, kFoobazfoobar, 6) == -1); + static_assert(__builtin_strncmp(kFoobar, kFoobazfoobar, 7) == -1); + static_assert(__builtin_strncmp(kFoobar, kFoobazfoobar + 6, 6) == 0); + static_assert(__builtin_strncmp(kFoobar, kFoobazfoobar + 6, 7) == 0); // expected-error {{not an integral constant}} \ // expected-note {{dereferenced one-past-the-end}} \ // expected-note {{in call to}} \ - // ref-error {{not an integral constant}} \ + // ref-error {{not an integral constant expression}} \ // ref-note {{dereferenced one-past-the-end}} + static_assert(__builtin_memcmp("abaa", "abba", 3) == -1); + static_assert(__builtin_memcmp("abaa", "abba", 2) == 0); + static_assert(__builtin_memcmp("a\203", "a", 2) == 1); + static_assert(__builtin_memcmp("a\203", "a\003", 2) == 1); + static_assert(__builtin_memcmp(0, 0, 0) == 0); + static_assert(__builtin_memcmp("abab\0banana", "abab\0banana", 100) == 0); // expected-error {{not an integral constant}} \ + // expected-note {{dereferenced one-past-the-end}} \ + // expected-note {{in call to}} \ + // ref-error {{not an integral constant}} \ + // ref-note {{dereferenced one-past-the-end}} + static_assert(__builtin_memcmp("abab\0banana", "abab\0canada", 100) == -1); + static_assert(__builtin_memcmp("abab\0banana", "abab\0canada", 7) == -1); + static_assert(__builtin_memcmp("abab\0banana", "abab\0canada", 6) == -1); + static_assert(__builtin_memcmp("abab\0banana", "abab\0canada", 5) == 0); + + static_assert(__builtin_memcmp(u8"abaa", u8"abba", 3) == -1); + static_assert(__builtin_memcmp(u8"abaa", u8"abba", 2) == 0); + static_assert(__builtin_memcmp(u8"a\203", u8"a", 2) == 1); + static_assert(__builtin_memcmp(u8"a\203", u8"a\003", 2) == 1); + static_assert(__builtin_memcmp(0, 0, 0) == 0); + static_assert(__builtin_memcmp(u8"abab\0banana", u8"abab\0banana", 100) == 0); // expected-error {{not an integral constant}} \ + // expected-note {{dereferenced one-past-the-end}} \ + // expected-note {{in call to}} \ + // ref-error {{not an integral constant}} \ + // ref-note {{dereferenced one-past-the-end}} + static_assert(__builtin_memcmp(u8"abab\0banana", u8"abab\0canada", 100) == -1); + static_assert(__builtin_memcmp(u8"abab\0banana", u8"abab\0canada", 7) == -1); + static_assert(__builtin_memcmp(u8"abab\0banana", u8"abab\0canada", 6) == -1); + static_assert(__builtin_memcmp(u8"abab\0banana", u8"abab\0canada", 5) == 0); + + static_assert(__builtin_memcmp(u8"\u1234", "\xE1\x88\xB4", 4) == 0); + static_assert(__builtin_memcmp(u8"\u1234", "\xE1\x88\xB3", 4) == 1); + + static_assert(__builtin_bcmp("abaa", "abba", 3) != 0); + static_assert(__builtin_bcmp("abaa", "abba", 2) == 0); + static_assert(__builtin_bcmp("a\203", "a", 2) != 0); + static_assert(__builtin_bcmp("a\203", "a\003", 2) != 0); + static_assert(__builtin_bcmp(0, 0, 0) == 0); + static_assert(__builtin_bcmp("abab\0banana", "abab\0banana", 100) == 0); // expected-error {{not an integral constant}} \ + // expected-note {{dereferenced one-past-the-end}} \ + // expected-note {{in call to}} \ + // ref-error {{not an integral constant}} \ + // ref-note {{dereferenced one-past-the-end}} + static_assert(__builtin_bcmp("abab\0banana", "abab\0canada", 100) != 0); + static_assert(__builtin_bcmp("abab\0banana", "abab\0canada", 7) != 0); + static_assert(__builtin_bcmp("abab\0banana", "abab\0canada", 6) != 0); + static_assert(__builtin_bcmp("abab\0banana", "abab\0canada", 5) == 0); + + extern struct Incomplete incomplete; + /// FIXME: Referencing the incomplete struct above doesn't work yet. +#if 0 + static_assert(__builtin_memcmp(&incomplete, "", 0u) == 0); + static_assert(__builtin_memcmp("", &incomplete, 0u) == 0); + static_assert(__builtin_memcmp(&incomplete, "", 1u) == 42); + static_assert(__builtin_memcmp("", &incomplete, 1u) == 42); + + static_assert(__builtin_bcmp(&incomplete, "", 0u) == 0); + static_assert(__builtin_bcmp("", &incomplete, 0u) == 0); + static_assert(__builtin_bcmp(&incomplete, "", 1u) == 42); + static_assert(__builtin_bcmp("", &incomplete, 1u) == 42); +#endif + + constexpr unsigned char ku00fe00[] = {0x00, 0xfe, 0x00}; + constexpr unsigned char ku00feff[] = {0x00, 0xfe, 0xff}; + constexpr signed char ks00fe00[] = {0, -2, 0}; + constexpr signed char ks00feff[] = {0, -2, -1}; + static_assert(__builtin_memcmp(ku00feff, ks00fe00, 2) == 0); + static_assert(__builtin_memcmp(ku00feff, ks00fe00, 99) == 1); + static_assert(__builtin_memcmp(ku00fe00, ks00feff, 99) == -1); + static_assert(__builtin_memcmp(ks00feff, ku00fe00, 2) == 0); + static_assert(__builtin_memcmp(ks00feff, ku00fe00, 99) == 1); + static_assert(__builtin_memcmp(ks00fe00, ku00feff, 99) == -1); + static_assert(__builtin_memcmp(ks00fe00, ks00feff, 2) == 0); + static_assert(__builtin_memcmp(ks00feff, ks00fe00, 99) == 1); + static_assert(__builtin_memcmp(ks00fe00, ks00feff, 99) == -1); + + static_assert(__builtin_bcmp(ku00feff, ks00fe00, 2) == 0); + static_assert(__builtin_bcmp(ku00feff, ks00fe00, 99) != 0); + static_assert(__builtin_bcmp(ku00fe00, ks00feff, 99) != 0); + static_assert(__builtin_bcmp(ks00feff, ku00fe00, 2) == 0); + static_assert(__builtin_bcmp(ks00feff, ku00fe00, 99) != 0); + static_assert(__builtin_bcmp(ks00fe00, ku00feff, 99) != 0); + static_assert(__builtin_bcmp(ks00fe00, ks00feff, 2) == 0); + static_assert(__builtin_bcmp(ks00feff, ks00fe00, 99) != 0); + static_assert(__builtin_bcmp(ks00fe00, ks00feff, 99) != 0); + + struct Bool3Tuple { bool bb[3]; }; + constexpr Bool3Tuple kb000100 = {{false, true, false}}; + static_assert(sizeof(bool) != 1u || __builtin_memcmp(ks00fe00, kb000100.bb, 1) == 0); // ref-error {{constant}} \ + // ref-note {{not supported}} \ + // expected-error {{constant}} \ + // expected-note {{not supported}} \ + // expected-note {{in call to}} + static_assert(sizeof(bool) != 1u || __builtin_memcmp(ks00fe00, kb000100.bb, 2) == 1); // ref-error {{constant}} \ + // ref-note {{not supported}} \ + // expected-error {{constant}} \ + // expected-note {{not supported}} \ + // expected-note {{in call to}} + static_assert(sizeof(bool) != 1u || __builtin_bcmp(ks00fe00, kb000100.bb, 1) == 0); // ref-error {{constant}} \ + // ref-note {{not supported}} \ + // expected-error {{constant}} \ + // expected-note {{not supported}} \ + // expected-note {{in call to}} + + static_assert(sizeof(bool) != 1u || __builtin_bcmp(ks00fe00, kb000100.bb, 2) != 0); // ref-error {{constant}} \ + // ref-note {{not supported}} \ + // expected-error {{constant}} \ + // expected-note {{not supported}} \ + // expected-note {{in call to}} + + constexpr long ksl[] = {0, -1}; + constexpr unsigned int kui[] = {0, 0u - 1}; + constexpr unsigned long long kull[] = {0, 0ull - 1}; + constexpr const auto *kuSizeofLong(void) { + if constexpr(sizeof(long) == sizeof(int)) { + return kui; + } else if constexpr(sizeof(long) == sizeof(long long)) { + return kull; + } else { + return nullptr; + } + } + static_assert(__builtin_memcmp(ksl, kuSizeofLong(), sizeof(long) - 1) == 0); // ref-error {{constant}} \ + // ref-note {{not supported}} \ + // expected-error {{constant}} \ + // expected-note {{not supported}} \ + // expected-note {{in call to}} + static_assert(__builtin_memcmp(ksl, kuSizeofLong(), sizeof(long) + 0) == 0); // ref-error {{constant}} \ + // ref-note {{not supported}} \ + // expected-error {{constant}} \ + // expected-note {{not supported}} \ + // expected-note {{in call to}} + static_assert(__builtin_memcmp(ksl, kuSizeofLong(), sizeof(long) + 1) == 0); // ref-error {{constant}} ref-note {{not supported}} \ + // expected-error {{constant}} \ + // expected-note {{not supported}} \ + // expected-note {{in call to}} + static_assert(__builtin_memcmp(ksl, kuSizeofLong(), 2*sizeof(long) - 1) == 0); // ref-error {{constant}} ref-note {{not supported}} \ + // expected-error {{constant}} \ + // expected-note {{not supported}} \ + // expected-note {{in call to}} + static_assert(__builtin_memcmp(ksl, kuSizeofLong(), 2*sizeof(long) + 0) == 0); // ref-error {{constant}} ref-note {{not supported}} \ + // expected-error {{constant}} \ + // expected-note {{not supported}} \ + // expected-note {{in call to}} + static_assert(__builtin_memcmp(ksl, kuSizeofLong(), 2*sizeof(long) + 1) == 42); // ref-error {{constant}} ref-note {{not supported}} \ + // expected-error {{constant}} \ + // expected-note {{not supported}} \ + // expected-note {{in call to}} + static_assert(__builtin_memcmp(ksl + 1, kuSizeofLong() + 1, sizeof(long) - 1) == 0); // ref-error {{constant}} ref-note {{not supported}} \ + // expected-error {{constant}} \ + // expected-note {{not supported}} \ + // expected-note {{in call to}} + static_assert(__builtin_memcmp(ksl + 1, kuSizeofLong() + 1, sizeof(long) + 0) == 0); // ref-error {{constant}} ref-note {{not supported}} \ + // expected-error {{constant}} \ + // expected-note {{not supported}} \ + // expected-note {{in call to}} + static_assert(__builtin_memcmp(ksl + 1, kuSizeofLong() + 1, sizeof(long) + 1) == 42); // ref-error {{constant}} ref-note {{not supported}} \ + // expected-error {{constant}} \ + // expected-note {{not supported}} \ + // expected-note {{in call to}} + static_assert(__builtin_bcmp(ksl, kuSizeofLong(), sizeof(long) - 1) == 0); // ref-error {{constant}} ref-note {{not supported}} \ + // expected-error {{constant}} \ + // expected-note {{not supported}} \ + // expected-note {{in call to}} + static_assert(__builtin_bcmp(ksl, kuSizeofLong(), sizeof(long) + 0) == 0); // ref-error {{constant}} ref-note {{not supported}} \ + // expected-error {{constant}} \ + // expected-note {{not supported}} \ + // expected-note {{in call to}} + static_assert(__builtin_bcmp(ksl, kuSizeofLong(), sizeof(long) + 1) == 0); // ref-error {{constant}} ref-note {{not supported}} \ + // expected-error {{constant}} \ + // expected-note {{not supported}} \ + // expected-note {{in call to}} + static_assert(__builtin_bcmp(ksl, kuSizeofLong(), 2*sizeof(long) - 1) == 0); // ref-error {{constant}} ref-note {{not supported}} \ + // expected-error {{constant}} \ + // expected-note {{not supported}} \ + // expected-note {{in call to}} + static_assert(__builtin_bcmp(ksl, kuSizeofLong(), 2*sizeof(long) + 0) == 0); // ref-error {{constant}} ref-note {{not supported}} \ + // expected-error {{constant}} \ + // expected-note {{not supported}} \ + // expected-note {{in call to}} + static_assert(__builtin_bcmp(ksl, kuSizeofLong(), 2*sizeof(long) + 1) == 42); // ref-error {{constant}} ref-note {{not supported}} \ + // expected-error {{constant}} \ + // expected-note {{not supported}} \ + // expected-note {{in call to}} + static_assert(__builtin_bcmp(ksl + 1, kuSizeofLong() + 1, sizeof(long) - 1) == 0); // ref-error {{constant}} ref-note {{not supported}} \ + // expected-error {{constant}} \ + // expected-note {{not supported}} \ + // expected-note {{in call to}} + static_assert(__builtin_bcmp(ksl + 1, kuSizeofLong() + 1, sizeof(long) + 0) == 0); // ref-error {{constant}} ref-note {{not supported}} \ + // expected-error {{constant}} \ + // expected-note {{not supported}} \ + // expected-note {{in call to}} +static_assert(__builtin_bcmp(ksl + 1, kuSizeofLong() + 1, sizeof(long) + 1) == 42); // ref-error {{constant}} ref-note {{not supported}} \ + // expected-error {{constant}} \ + // expected-note {{not supported}} \ + // expected-note {{in call to}} +#if 0 +constexpr int a = strcmp("hello", "world"); // expected-error {{constant expression}} expected-note {{non-constexpr function 'strcmp' cannot be used in a constant expression}} + constexpr int b = strncmp("hello", "world", 3); // expected-error {{constant expression}} expected-note {{non-constexpr function 'strncmp' cannot be used in a constant expression}} + constexpr int c = memcmp("hello", "world", 3); // expected-error {{constant expression}} expected-note {{non-constexpr function 'memcmp' cannot be used in a constant expression}} +#endif } /// Copied from constant-expression-cxx11.cpp