From: Richard Smith Date: Sat, 12 Nov 2016 01:39:56 +0000 (+0000) Subject: [c++1z] Support constant folding for __builtin_strchr and __builtin_memchr. X-Git-Tag: llvmorg-4.0.0-rc1~4799 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=e950795a03bd0b1acd743221c8fde5a48d2d3b0e;p=platform%2Fupstream%2Fllvm.git [c++1z] Support constant folding for __builtin_strchr and __builtin_memchr. llvm-svn: 286699 --- diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 8096e0e..64e448f 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -5256,7 +5256,7 @@ bool PointerExprEvaluator::VisitCallExpr(const CallExpr *E) { if (IsStringLiteralCall(E)) return Success(E); - switch (E->getBuiltinCallee()) { + switch (unsigned BuiltinOp = E->getBuiltinCallee()) { case Builtin::BI__builtin_addressof: return EvaluateLValue(E->getArg(0), Result, Info); case Builtin::BI__builtin_assume_aligned: { @@ -5324,6 +5324,65 @@ bool PointerExprEvaluator::VisitCallExpr(const CallExpr *E) { return true; } + + case Builtin::BIstrchr: + case Builtin::BImemchr: + if (Info.getLangOpts().CPlusPlus11) + Info.CCEDiag(E, diag::note_constexpr_invalid_function) + << /*isConstexpr*/0 << /*isConstructor*/0 + << (BuiltinOp == Builtin::BIstrchr ? "'strchr'" : "'memchr'"); + else + Info.CCEDiag(E, diag::note_invalid_subexpr_in_const_expr); + // Fall through. + case Builtin::BI__builtin_strchr: + case Builtin::BI__builtin_memchr: { + if (!Visit(E->getArg(0))) + return false; + APSInt Desired; + if (!EvaluateInteger(E->getArg(1), Desired, Info)) + return false; + uint64_t MaxLength = uint64_t(-1); + if (BuiltinOp != Builtin::BIstrchr && + BuiltinOp != Builtin::BI__builtin_strchr) { + APSInt N; + if (!EvaluateInteger(E->getArg(2), N, Info)) + return false; + MaxLength = N.getExtValue(); + } + + QualType CharTy = Info.Ctx.CharTy; + bool IsStrchr = (BuiltinOp != Builtin::BImemchr && + BuiltinOp != Builtin::BI__builtin_memchr); + + // strchr compares directly to the passed integer, and therefore + // always fails if given an int that is not a char. + if (IsStrchr && + !APSInt::isSameValue(HandleIntToIntCast(Info, E, CharTy, + E->getArg(1)->getType(), + Desired), + Desired)) + return ZeroInitialization(E); + + // memchr compares by converting both sides to unsigned char. That's also + // correct for strchr if we get this far. + uint64_t DesiredVal = Desired.trunc(Info.Ctx.getCharWidth()).getZExtValue(); + + for (; MaxLength; --MaxLength) { + APValue Char; + if (!handleLValueToRValueConversion(Info, E, CharTy, Result, Char) || + !Char.isInt()) + return false; + if (Char.getInt().getZExtValue() == DesiredVal) + return true; + if (IsStrchr && !Char.getInt()) + break; + if (!HandleLValueArrayAdjustment(Info, E, Result, CharTy, 1)) + return false; + } + // Not found: return nullptr. + return ZeroInitialization(E); + } + default: return ExprEvaluatorBaseTy::VisitCallExpr(E); } @@ -7065,7 +7124,7 @@ bool IntExprEvaluator::VisitCallExpr(const CallExpr *E) { } // Slow path: scan the bytes of the string looking for the terminating 0. - QualType CharTy = E->getArg(0)->getType()->getPointeeType(); + QualType CharTy = Info.Ctx.CharTy; for (uint64_t Strlen = 0; /**/; ++Strlen) { APValue Char; if (!handleLValueToRValueConversion(Info, E, CharTy, String, Char) || @@ -7108,7 +7167,7 @@ bool IntExprEvaluator::VisitCallExpr(const CallExpr *E) { } bool StopAtNull = (BuiltinOp != Builtin::BImemcmp && BuiltinOp != Builtin::BI__builtin_memcmp); - QualType CharTy = E->getArg(0)->getType()->getPointeeType(); + QualType CharTy = Info.Ctx.CharTy; for (; MaxLength; --MaxLength) { APValue Char1, Char2; if (!handleLValueToRValueConversion(Info, E, CharTy, String1, Char1) || diff --git a/clang/test/SemaCXX/constexpr-string.cpp b/clang/test/SemaCXX/constexpr-string.cpp index e614d43..0ce3b05 100644 --- a/clang/test/SemaCXX/constexpr-string.cpp +++ b/clang/test/SemaCXX/constexpr-string.cpp @@ -1,4 +1,5 @@ // 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 # 4 "/usr/include/string.h" 1 3 4 extern "C" { @@ -9,9 +10,12 @@ extern "C" { extern int strcmp(const char *s1, const char *s2); extern int strncmp(const char *s1, const char *s2, size_t n); extern int memcmp(const char *s1, const char *s2, size_t n); // expected-note {{here}} + + extern char *strchr(const char *s, int c); + extern void *memchr(const void *s, int c, size_t n); } -# 15 "SemaCXX/constexpr-string.cpp" 2 +# 19 "SemaCXX/constexpr-string.cpp" 2 namespace Strlen { constexpr int n = __builtin_strlen("hello"); // ok constexpr int m = strlen("hello"); // expected-error {{constant expression}} expected-note {{non-constexpr function 'strlen' cannot be used in a constant expression}} @@ -66,3 +70,37 @@ namespace StrcmpEtc { 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}} } + +namespace StrchrEtc { + constexpr const char *kStr = "abca\xff\0d"; + constexpr char kFoo[] = {'f', 'o', 'o'}; + static_assert(__builtin_strchr(kStr, 'a') == kStr); + static_assert(__builtin_strchr(kStr, 'b') == kStr + 1); + static_assert(__builtin_strchr(kStr, 'c') == kStr + 2); + static_assert(__builtin_strchr(kStr, 'd') == nullptr); + static_assert(__builtin_strchr(kStr, 'e') == nullptr); + static_assert(__builtin_strchr(kStr, '\0') == kStr + 5); + static_assert(__builtin_strchr(kStr, 'a' + 256) == nullptr); + static_assert(__builtin_strchr(kStr, 'a' - 256) == nullptr); + static_assert(__builtin_strchr(kStr, '\xff') == kStr + 4); + static_assert(__builtin_strchr(kStr, '\xff' + 256) == nullptr); + static_assert(__builtin_strchr(kStr, '\xff' - 256) == nullptr); + static_assert(__builtin_strchr(kFoo, 'o') == kFoo + 1); + static_assert(__builtin_strchr(kFoo, 'x') == nullptr); // expected-error {{not an integral constant}} expected-note {{dereferenced one-past-the-end}} + static_assert(__builtin_strchr(nullptr, 'x') == nullptr); // expected-error {{not an integral constant}} expected-note {{dereferenced null}} + + static_assert(__builtin_memchr(kStr, 'a', 0) == nullptr); + static_assert(__builtin_memchr(kStr, 'a', 1) == kStr); + static_assert(__builtin_memchr(kStr, '\0', 5) == nullptr); + static_assert(__builtin_memchr(kStr, '\0', 6) == kStr + 5); + static_assert(__builtin_memchr(kStr, '\xff', 8) == kStr + 4); + static_assert(__builtin_memchr(kStr, '\xff' + 256, 8) == kStr + 4); + static_assert(__builtin_memchr(kStr, '\xff' - 256, 8) == kStr + 4); + static_assert(__builtin_memchr(kFoo, 'x', 3) == nullptr); + static_assert(__builtin_memchr(kFoo, 'x', 4) == nullptr); // expected-error {{not an integral constant}} expected-note {{dereferenced one-past-the-end}} + static_assert(__builtin_memchr(nullptr, 'x', 3) == nullptr); // expected-error {{not an integral constant}} expected-note {{dereferenced null}} + static_assert(__builtin_memchr(nullptr, 'x', 0) == nullptr); // FIXME: Should we reject this? + + constexpr bool a = !strchr("hello", 'h'); // expected-error {{constant expression}} expected-note {{non-constexpr function 'strchr' cannot be used in a constant expression}} + constexpr bool b = !memchr("hello", 'h', 3); // expected-error {{constant expression}} expected-note {{non-constexpr function 'memchr' cannot be used in a constant expression}} +}