Index: llvm/include/llvm/Analysis/ValueTracking.h =================================================================== --- llvm/include/llvm/Analysis/ValueTracking.h +++ llvm/include/llvm/Analysis/ValueTracking.h @@ -329,14 +329,14 @@ bool getConstantDataArrayInfo(const Value *V, ConstantDataArraySlice &Slice, unsigned ElementSize, uint64_t Offset = 0); - /// This function computes the length of a null-terminated C string pointed to - /// by V. If successful, it returns true and returns the string in Str. If - /// unsuccessful, it returns false. This does not include the trailing null - /// character by default. If TrimAtNul is set to false, then this returns any - /// trailing null characters as well as any other characters that come after - /// it. + /// Computes the length of a null-terminated C string pointed to by V at + /// Offset. On success returns true and sets Str to the string, otherwise + /// returns false. If ZeroInit is null, Str ends just before the first null + /// byte. Otherwise, Str includes any interior as well as trailing null + /// bytes; if the string corresponds to a zeroinitializer *ZeroInit is set + /// to true and Str is set to empty. bool getConstantStringInfo(const Value *V, StringRef &Str, - uint64_t Offset = 0, bool TrimAtNul = true); + uint64_t Offset = 0, bool *ZeroInit = nullptr); /// If we can compute the length of the string pointed to by the specified /// pointer, return 'len+1'. If we can't, return 0. Index: llvm/lib/Analysis/ValueTracking.cpp =================================================================== --- llvm/lib/Analysis/ValueTracking.cpp +++ llvm/lib/Analysis/ValueTracking.cpp @@ -4249,27 +4249,34 @@ return true; } -/// This function computes the length of a null-terminated C string pointed to -/// by V. If successful, it returns true and returns the string in Str. -/// If unsuccessful, it returns false. +/// Computes the length of a null-terminated C string pointed to by V at +/// Offset. On success returns true and sets Str to the string, otherwise +/// returns false. If ZeroInit is null, Str ends just before the first null +/// byte. Otherwise, Str includes any interior as well as trailing null +/// bytes; if the string corresponds to a zeroinitializer *ZeroInit is set +/// to true and Str is set to empty. bool llvm::getConstantStringInfo(const Value *V, StringRef &Str, - uint64_t Offset, bool TrimAtNul) { + uint64_t Offset, bool *ZeroInit) { ConstantDataArraySlice Slice; if (!getConstantDataArrayInfo(V, Slice, 8, Offset)) return false; if (Slice.Array == nullptr) { - if (TrimAtNul) { + if (ZeroInit) + *ZeroInit = true; + else { Str = StringRef(); return true; } if (Slice.Length == 1) { + // TODO: Increase this to some reasonably small number. Str = StringRef("", 1); return true; } - // We cannot instantiate a StringRef as we do not have an appropriate string - // of 0s at hand. - return false; + + // We cannot create an arbitrarily large StringRef as we do not have + // an appropriate string of 0s at hand. + return ZeroInit != nullptr; } // Start out with the entire array in the StringRef. @@ -4277,7 +4284,9 @@ // Skip over 'offset' bytes. Str = Str.substr(Slice.Offset); - if (TrimAtNul) { + if (ZeroInit) + *ZeroInit = false; + else { // Trim off the \0 and anything after it. If the array is not nul // terminated, we just return the whole end of string. The client may know // some other way that the string is length-bound. Index: llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp =================================================================== --- llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp +++ llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp @@ -206,6 +206,33 @@ return Len >= Str.size() ? Str : Str.substr(0, Len); } +// Helper used by optimizeMemchr and optimizeMemRChr. When Reverse is +// true return the IR corresponding to +// Size && (char)CharVal == '\0' ? Src + Size : Null +// otherwise +// Size && (char)CharVal == '\0' ? Src : Null + +static Value *createSelectFromZeroInit(CallInst *CI, Value *ZeroInit, + Value *CharVal, Value *Size, + bool Reverse, IRBuilderBase &B, + const char *Name) { + Type *Int8Ty = B.getInt8Ty(); + Type *SizeTy = Size->getType(); + CharVal = B.CreateTrunc(CharVal, B.getInt8Ty()); + Value *NullPtr = Constant::getNullValue(CI->getType()); + Value *CEqZ = B.CreateICmpEQ(CharVal, ConstantInt::get(Int8Ty, 0)); + Value *NNeZ = B.CreateICmpNE(Size, ConstantInt::get(SizeTy, 0)); + Value *And = B.CreateLogicalAnd(NNeZ, CEqZ); + + if (Reverse) { + Value *SizeM1 = B.CreateSub(Size, ConstantInt::get(SizeTy, 1)); + Value *InitLast = B.CreateGEP(B.getInt8Ty(), ZeroInit, SizeM1); + return B.CreateSelect(And, InitLast, NullPtr, Name); + } + + return B.CreateSelect(And, ZeroInit, NullPtr, Name); +} + //===----------------------------------------------------------------------===// // String and Memory Library Call Optimizations //===----------------------------------------------------------------------===// @@ -941,9 +968,14 @@ } StringRef Str; - if (!getConstantStringInfo(SrcStr, Str, 0, /*TrimAtNul=*/false)) + bool ZeroInit; + if (!getConstantStringInfo(SrcStr, Str, 0, &ZeroInit)) return nullptr; + if (ZeroInit) + return createSelectFromZeroInit(CI, SrcStr, CharVal, Size, true, B, + "memrchr.sel"); + uint64_t EndOff = UINT64_MAX; if (LenC) { EndOff = LenC->getZExtValue(); @@ -1025,9 +1057,14 @@ } StringRef Str; - if (!getConstantStringInfo(SrcStr, Str, 0, /*TrimAtNul=*/false)) + bool ZeroInit; + if (!getConstantStringInfo(SrcStr, Str, 0, &ZeroInit)) return nullptr; + if (ZeroInit) + return createSelectFromZeroInit(CI, SrcStr, CharVal, Size, false, B, + "memchr.sel"); + if (CharC) { size_t Pos = Str.find(CharC->getZExtValue()); if (Pos == StringRef::npos) @@ -1293,9 +1330,10 @@ if (N) { if (N->isNullValue()) return Constant::getNullValue(CI->getType()); - if (!getConstantStringInfo(Src, SrcStr, /*Offset=*/0, - /*TrimAtNul=*/false) || - !StopChar) + bool ZeroInit; + if (!getConstantStringInfo(Src, SrcStr, /*Offset=*/0, &ZeroInit) || + // TODO: Handle zeroinitializer. + ZeroInit || !StopChar) return nullptr; } else { return nullptr; Index: llvm/test/Transforms/InstCombine/memchr-6.ll =================================================================== --- llvm/test/Transforms/InstCombine/memchr-6.ll +++ llvm/test/Transforms/InstCombine/memchr-6.ll @@ -13,13 +13,13 @@ ; Fold memchr(a00000, C, 5) to *a00000 == C ? a00000 : null. -; TODO: This depends on getConstantStringInfo() being able to handle -; implicitly zeroed out constants. define i8* @fold_memchr_a00000_c_5(i32 %C) { ; CHECK-LABEL: @fold_memchr_a00000_c_5( -; CHECK-NEXT: [[RET:%.*]] = call i8* @memchr(i8* noundef nonnull dereferenceable(1) getelementptr inbounds ([5 x i8], [5 x i8]* @a00000, i64 0, i64 0), i32 [[C:%.*]], i64 5) -; CHECK-NEXT: ret i8* [[RET]] +; CHECK-NEXT: [[TMP1:%.*]] = trunc i32 [[C:%.*]] to i8 +; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i8 [[TMP1]], 0 +; CHECK-NEXT: [[MEMCHR_SEL:%.*]] = select i1 [[TMP2]], i8* getelementptr inbounds ([5 x i8], [5 x i8]* @a00000, i64 0, i64 0), i8* null +; CHECK-NEXT: ret i8* [[MEMCHR_SEL]] ; %ptr = getelementptr [5 x i8], [5 x i8]* @a00000, i64 0, i64 0 Index: llvm/test/Transforms/InstCombine/memchr-7.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/InstCombine/memchr-7.ll @@ -0,0 +1,211 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -passes=instcombine -S | FileCheck %s +; +; Verify that memchr calls with constant zeroinitializer arrays are +; folded correctly. + +declare i8* @memchr(i8*, i32, i64) + +@a8 = constant [8 x i8] zeroinitializer +@ai32max = constant [2147483647 x i8] zeroinitializer + +; Fold memchr(a8, 0, 1) to a8. + +define i8* @fold_memchr_a8_0_1() { +; CHECK-LABEL: @fold_memchr_a8_0_1( +; CHECK-NEXT: ret i8* getelementptr inbounds ([8 x i8], [8 x i8]* @a8, i64 0, i64 0) +; + %ptr = getelementptr [8 x i8], [8 x i8]* @a8, i32 0, i32 0 + %chr = tail call i8* @memchr(i8* %ptr, i32 0, i64 1) + ret i8* %chr +} + +; Fold memchr(a8, 0, 8) to a8. + +define i8* @fold_memchr_a8_0_8() { +; CHECK-LABEL: @fold_memchr_a8_0_8( +; CHECK-NEXT: ret i8* getelementptr inbounds ([8 x i8], [8 x i8]* @a8, i64 0, i64 0) +; + %ptr = getelementptr [8 x i8], [8 x i8]* @a8, i32 0, i32 0 + %chr = tail call i8* @memchr(i8* %ptr, i32 0, i64 8) + ret i8* %chr +} + +; Fold memchr(a8, 0, SIZE_MAX) to a8. + +define i8* @fold_memchr_a8_0_szmax() { +; CHECK-LABEL: @fold_memchr_a8_0_szmax( +; CHECK-NEXT: ret i8* getelementptr inbounds ([8 x i8], [8 x i8]* @a8, i64 0, i64 0) +; + %ptr = getelementptr [8 x i8], [8 x i8]* @a8, i32 0, i32 0 + %chr = tail call i8* @memchr(i8* %ptr, i32 0, i64 -1) + ret i8* %chr +} + +; Fold memchr(a8, 0, N) to N ? a8 : null. + +define i8* @fold_memchr_a8_0_N(i64 %N) { +; CHECK-LABEL: @fold_memchr_a8_0_N( +; CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq i64 [[N:%.*]], 0 +; CHECK-NEXT: [[MEMCHR_SEL:%.*]] = select i1 [[DOTNOT]], i8* null, i8* getelementptr inbounds ([8 x i8], [8 x i8]* @a8, i64 0, i64 0) +; CHECK-NEXT: ret i8* [[MEMCHR_SEL]] +; + %ptr = getelementptr [8 x i8], [8 x i8]* @a8, i32 0, i32 0 + %chr = tail call i8* @memchr(i8* %ptr, i32 0, i64 %N) + ret i8* %chr +} + + +; Fold memchr(a8, 1, 8) to null. + +define i8* @fold_memchr_a8_1_8() { +; CHECK-LABEL: @fold_memchr_a8_1_8( +; CHECK-NEXT: ret i8* null +; + %ptr = getelementptr [8 x i8], [8 x i8]* @a8, i32 0, i32 0 + %chr = tail call i8* @memchr(i8* %ptr, i32 1, i64 8) + ret i8* %chr +} + +; Fold memchr(a8, 1, SIZE_MAX) to null. + +define i8* @fold_memchr_a8_1_szmax() { +; CHECK-LABEL: @fold_memchr_a8_1_szmax( +; CHECK-NEXT: ret i8* null +; + %ptr = getelementptr [8 x i8], [8 x i8]* @a8, i32 0, i32 0 + %chr = tail call i8* @memchr(i8* %ptr, i32 1, i64 -1) + ret i8* %chr +} + +; Fold memchr(a8, 1, N) to null. + +define i8* @fold_memchr_a8_1_N(i64 %N) { +; CHECK-LABEL: @fold_memchr_a8_1_N( +; CHECK-NEXT: ret i8* null +; + %ptr = getelementptr [8 x i8], [8 x i8]* @a8, i32 0, i32 0 + %chr = tail call i8* @memchr(i8* %ptr, i32 1, i64 %N) + ret i8* %chr +} + + +; Fold memchr(a8, C, 8) to C == '\0' ? a8 : null. + +define i8* @fold_memchr_a8_C_8(i32 %C) { +; CHECK-LABEL: @fold_memchr_a8_C_8( +; CHECK-NEXT: [[TMP1:%.*]] = trunc i32 [[C:%.*]] to i8 +; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i8 [[TMP1]], 0 +; CHECK-NEXT: [[MEMCHR_SEL:%.*]] = select i1 [[TMP2]], i8* getelementptr inbounds ([8 x i8], [8 x i8]* @a8, i64 0, i64 0), i8* null +; CHECK-NEXT: ret i8* [[MEMCHR_SEL]] +; + %ptr = getelementptr [8 x i8], [8 x i8]* @a8, i32 0, i32 0 + %chr = tail call i8* @memchr(i8* %ptr, i32 %C, i64 8) + ret i8* %chr +} + +; Fold memchr(a8, C, SIZE_MAX) to C == '\0' ? a8 : null. + +define i8* @fold_memchr_a8_C_szmax(i32 %C) { +; CHECK-LABEL: @fold_memchr_a8_C_szmax( +; CHECK-NEXT: [[TMP1:%.*]] = trunc i32 [[C:%.*]] to i8 +; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i8 [[TMP1]], 0 +; CHECK-NEXT: [[MEMCHR_SEL:%.*]] = select i1 [[TMP2]], i8* getelementptr inbounds ([8 x i8], [8 x i8]* @a8, i64 0, i64 0), i8* null +; CHECK-NEXT: ret i8* [[MEMCHR_SEL]] +; + %ptr = getelementptr [8 x i8], [8 x i8]* @a8, i32 0, i32 0 + %chr = tail call i8* @memchr(i8* %ptr, i32 %C, i64 -1) + ret i8* %chr +} + + +; Fold memchr(a8, C, N) to C == '\0' && N ? a8 : null. + +define i8* @fold_memchr_a8_C_N(i32 %C, i64 %N) { +; CHECK-LABEL: @fold_memchr_a8_C_N( +; CHECK-NEXT: [[TMP1:%.*]] = trunc i32 [[C:%.*]] to i8 +; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i8 [[TMP1]], 0 +; CHECK-NEXT: [[TMP3:%.*]] = icmp ne i64 [[N:%.*]], 0 +; CHECK-NEXT: [[TMP4:%.*]] = select i1 [[TMP3]], i1 [[TMP2]], i1 false +; CHECK-NEXT: [[MEMCHR_SEL:%.*]] = select i1 [[TMP4]], i8* getelementptr inbounds ([8 x i8], [8 x i8]* @a8, i64 0, i64 0), i8* null +; CHECK-NEXT: ret i8* [[MEMCHR_SEL]] +; + %ptr = getelementptr [8 x i8], [8 x i8]* @a8, i32 0, i32 0 + %chr = tail call i8* @memchr(i8* %ptr, i32 %C, i64 %N) + ret i8* %chr +} + + +; Fold memchr(ai32max, 0, INT32_MAX) to a8. + +define i8* @fold_memchr_ai32max_0_i32max() { +; CHECK-LABEL: @fold_memchr_ai32max_0_i32max( +; CHECK-NEXT: ret i8* getelementptr inbounds ([2147483647 x i8], [2147483647 x i8]* @ai32max, i64 0, i64 0) +; + %ptr = getelementptr [2147483647 x i8], [2147483647 x i8]* @ai32max, i32 0, i32 0 + %chr = tail call i8* @memchr(i8* %ptr, i32 0, i64 2147483647) + ret i8* %chr +} + +; Fold memchr(ai32max, 1, INT32_MAX) to null. + +define i8* @fold_memchr_ai32max_1_i32max() { +; CHECK-LABEL: @fold_memchr_ai32max_1_i32max( +; CHECK-NEXT: ret i8* null +; + %ptr = getelementptr [2147483647 x i8], [2147483647 x i8]* @ai32max, i32 0, i32 0 + %chr = tail call i8* @memchr(i8* %ptr, i32 1, i64 2147483647) + ret i8* %chr +} + +; Fold memchr(ai32max, 256, INT32_MAX) to ai32max. + +define i8* @fold_memchr_ai32max_256_i32max() { +; CHECK-LABEL: @fold_memchr_ai32max_256_i32max( +; CHECK-NEXT: ret i8* getelementptr inbounds ([2147483647 x i8], [2147483647 x i8]* @ai32max, i64 0, i64 0) +; + %ptr = getelementptr [2147483647 x i8], [2147483647 x i8]* @ai32max, i32 0, i32 0 + %chr = tail call i8* @memchr(i8* %ptr, i32 256, i64 2147483647) + ret i8* %chr +} + +; Fold memchr(ai32max, 257, INT32_MAX) to null. + +define i8* @fold_memchr_ai32max_257_i32max() { +; CHECK-LABEL: @fold_memchr_ai32max_257_i32max( +; CHECK-NEXT: ret i8* null +; + %ptr = getelementptr [2147483647 x i8], [2147483647 x i8]* @ai32max, i32 0, i32 0 + %chr = tail call i8* @memchr(i8* %ptr, i32 257, i64 2147483647) + ret i8* %chr +} + +; Fold memchr(ai32max, C, INT32_MAX) to C == '\0' ? ai32max : null. + +define i8* @fold_memchr_ai32max_C_i32max(i32 %C) { +; CHECK-LABEL: @fold_memchr_ai32max_C_i32max( +; CHECK-NEXT: [[TMP1:%.*]] = trunc i32 [[C:%.*]] to i8 +; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i8 [[TMP1]], 0 +; CHECK-NEXT: [[MEMCHR_SEL:%.*]] = select i1 [[TMP2]], i8* getelementptr inbounds ([2147483647 x i8], [2147483647 x i8]* @ai32max, i64 0, i64 0), i8* null +; CHECK-NEXT: ret i8* [[MEMCHR_SEL]] +; + %ptr = getelementptr [2147483647 x i8], [2147483647 x i8]* @ai32max, i32 0, i32 0 + %chr = tail call i8* @memchr(i8* %ptr, i32 %C, i64 2147483647) + ret i8* %chr +} + +; Fold memchr(ai32max, C, N) to C == '\0' && N ? ai32max : null. + +define i8* @fold_memchr_ai32max_C_N(i32 %C, i64 %N) { +; CHECK-LABEL: @fold_memchr_ai32max_C_N( +; CHECK-NEXT: [[TMP1:%.*]] = trunc i32 [[C:%.*]] to i8 +; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i8 [[TMP1]], 0 +; CHECK-NEXT: [[TMP3:%.*]] = icmp ne i64 [[N:%.*]], 0 +; CHECK-NEXT: [[TMP4:%.*]] = select i1 [[TMP3]], i1 [[TMP2]], i1 false +; CHECK-NEXT: [[MEMCHR_SEL:%.*]] = select i1 [[TMP4]], i8* getelementptr inbounds ([2147483647 x i8], [2147483647 x i8]* @ai32max, i64 0, i64 0), i8* null +; CHECK-NEXT: ret i8* [[MEMCHR_SEL]] +; + %ptr = getelementptr [2147483647 x i8], [2147483647 x i8]* @ai32max, i32 0, i32 0 + %chr = tail call i8* @memchr(i8* %ptr, i32 %C, i64 %N) + ret i8* %chr +} Index: llvm/test/Transforms/InstCombine/memrchr-6.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/InstCombine/memrchr-6.ll @@ -0,0 +1,180 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -passes=instcombine -S | FileCheck %s +; +; Verify that memrchr calls with constant zeroinitializer arrays are +; folded correctly. + +declare i8* @memrchr(i8*, i32, i64) + +@a8 = constant [8 x i8] zeroinitializer +@ai32max = constant [2147483647 x i8] zeroinitializer + +; Fold memrchr(a8, 0, 1) to a8. + +define i8* @fold_memrchr_a8_0_1() { +; CHECK-LABEL: @fold_memrchr_a8_0_1( +; CHECK-NEXT: ret i8* getelementptr inbounds ([8 x i8], [8 x i8]* @a8, i64 0, i64 0) +; + %ptr = getelementptr [8 x i8], [8 x i8]* @a8, i32 0, i32 0 + %chr = tail call i8* @memrchr(i8* %ptr, i32 0, i64 1) + ret i8* %chr +} + +; Fold memrchr(a8, 0, 8) to a8 + 7. + +define i8* @fold_memrchr_a8_0_8() { +; CHECK-LABEL: @fold_memrchr_a8_0_8( +; CHECK-NEXT: ret i8* getelementptr inbounds ([8 x i8], [8 x i8]* @a8, i64 0, i64 7) +; + %ptr = getelementptr [8 x i8], [8 x i8]* @a8, i32 0, i32 0 + %chr = tail call i8* @memrchr(i8* %ptr, i32 0, i64 8) + ret i8* %chr +} + +; Fold memrchr(a8, 0, N) to N ? a8 + N - 1 : null. + +define i8* @fold_memrchr_a8_0_N(i64 %N) { +; CHECK-LABEL: @fold_memrchr_a8_0_N( +; CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq i64 [[N:%.*]], 0 +; CHECK-NEXT: [[TMP1:%.*]] = add i64 [[N]], -1 +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr [8 x i8], [8 x i8]* @a8, i64 0, i64 [[TMP1]] +; CHECK-NEXT: [[MEMRCHR_SEL:%.*]] = select i1 [[DOTNOT]], i8* null, i8* [[TMP2]] +; CHECK-NEXT: ret i8* [[MEMRCHR_SEL]] +; + %ptr = getelementptr [8 x i8], [8 x i8]* @a8, i32 0, i32 0 + %chr = tail call i8* @memrchr(i8* %ptr, i32 0, i64 %N) + ret i8* %chr +} + + +; Fold memrchr(a8, 1, 8) to null. + +define i8* @fold_memrchr_a8_1_8() { +; CHECK-LABEL: @fold_memrchr_a8_1_8( +; CHECK-NEXT: ret i8* null +; + %ptr = getelementptr [8 x i8], [8 x i8]* @a8, i32 0, i32 0 + %chr = tail call i8* @memrchr(i8* %ptr, i32 1, i64 8) + ret i8* %chr +} + +; Fold memrchr(a8, 1, N) to null. + +define i8* @fold_memrchr_a8_1_N(i64 %N) { +; CHECK-LABEL: @fold_memrchr_a8_1_N( +; CHECK-NEXT: ret i8* null +; + %ptr = getelementptr [8 x i8], [8 x i8]* @a8, i32 0, i32 0 + %chr = tail call i8* @memrchr(i8* %ptr, i32 1, i64 %N) + ret i8* %chr +} + + +; Fold memrchr(a8, C, 8) to C == '\0' ? a8 + 7 : null. + +define i8* @fold_memrchr_a8_C_8(i32 %C) { +; CHECK-LABEL: @fold_memrchr_a8_C_8( +; CHECK-NEXT: [[TMP1:%.*]] = trunc i32 [[C:%.*]] to i8 +; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i8 [[TMP1]], 0 +; CHECK-NEXT: [[MEMRCHR_SEL:%.*]] = select i1 [[TMP2]], i8* getelementptr inbounds ([8 x i8], [8 x i8]* @a8, i64 0, i64 7), i8* null +; CHECK-NEXT: ret i8* [[MEMRCHR_SEL]] +; + %ptr = getelementptr [8 x i8], [8 x i8]* @a8, i32 0, i32 0 + %chr = tail call i8* @memrchr(i8* %ptr, i32 %C, i64 8) + ret i8* %chr +} + +; Fold memrchr(a8, C, N) to C == '\0' && N ? a8 + N - 1 : null. + +define i8* @fold_memrchr_a8_C_N(i32 %C, i64 %N) { +; CHECK-LABEL: @fold_memrchr_a8_C_N( +; CHECK-NEXT: [[TMP1:%.*]] = trunc i32 [[C:%.*]] to i8 +; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i8 [[TMP1]], 0 +; CHECK-NEXT: [[TMP3:%.*]] = icmp ne i64 [[N:%.*]], 0 +; CHECK-NEXT: [[TMP4:%.*]] = select i1 [[TMP3]], i1 [[TMP2]], i1 false +; CHECK-NEXT: [[TMP5:%.*]] = add i64 [[N]], -1 +; CHECK-NEXT: [[TMP6:%.*]] = getelementptr [8 x i8], [8 x i8]* @a8, i64 0, i64 [[TMP5]] +; CHECK-NEXT: [[MEMRCHR_SEL:%.*]] = select i1 [[TMP4]], i8* [[TMP6]], i8* null +; CHECK-NEXT: ret i8* [[MEMRCHR_SEL]] +; + %ptr = getelementptr [8 x i8], [8 x i8]* @a8, i32 0, i32 0 + %chr = tail call i8* @memrchr(i8* %ptr, i32 %C, i64 %N) + ret i8* %chr +} + + +; Fold memrchr(ai32max, 0, INT32_MAX) to a8 + INT32_MAX - 1. + +define i8* @fold_memrchr_ai32max_0_i32max() { +; CHECK-LABEL: @fold_memrchr_ai32max_0_i32max( +; CHECK-NEXT: ret i8* getelementptr inbounds ([2147483647 x i8], [2147483647 x i8]* @ai32max, i64 0, i64 2147483646) +; + %ptr = getelementptr [2147483647 x i8], [2147483647 x i8]* @ai32max, i32 0, i32 0 + %chr = tail call i8* @memrchr(i8* %ptr, i32 0, i64 2147483647) + ret i8* %chr +} + +; Fold memrchr(ai32max, 1, INT32_MAX) to null. + +define i8* @fold_memrchr_ai32max_1_i32max() { +; CHECK-LABEL: @fold_memrchr_ai32max_1_i32max( +; CHECK-NEXT: ret i8* null +; + %ptr = getelementptr [2147483647 x i8], [2147483647 x i8]* @ai32max, i32 0, i32 0 + %chr = tail call i8* @memrchr(i8* %ptr, i32 1, i64 2147483647) + ret i8* %chr +} + +; Fold memrchr(ai32max, 256, INT32_MAX) to ai32max + INT32_MAX - 1. + +define i8* @fold_memrchr_ai32max_256_i32max() { +; CHECK-LABEL: @fold_memrchr_ai32max_256_i32max( +; CHECK-NEXT: ret i8* getelementptr inbounds ([2147483647 x i8], [2147483647 x i8]* @ai32max, i64 0, i64 2147483646) +; + %ptr = getelementptr [2147483647 x i8], [2147483647 x i8]* @ai32max, i32 0, i32 0 + %chr = tail call i8* @memrchr(i8* %ptr, i32 256, i64 2147483647) + ret i8* %chr +} + +; Fold memrchr(ai32max, 257, INT32_MAX) to null. + +define i8* @fold_memrchr_ai32max_257_i32max() { +; CHECK-LABEL: @fold_memrchr_ai32max_257_i32max( +; CHECK-NEXT: ret i8* null +; + %ptr = getelementptr [2147483647 x i8], [2147483647 x i8]* @ai32max, i32 0, i32 0 + %chr = tail call i8* @memrchr(i8* %ptr, i32 257, i64 2147483647) + ret i8* %chr +} + +; Fold memrchr(ai32max, C, INT32_MAX) to C == '\0' ? ai32max + INT32_MAX - 1 : null. + +define i8* @fold_memrchr_ai32max_C_i32max(i32 %C) { +; CHECK-LABEL: @fold_memrchr_ai32max_C_i32max( +; CHECK-NEXT: [[TMP1:%.*]] = trunc i32 [[C:%.*]] to i8 +; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i8 [[TMP1]], 0 +; CHECK-NEXT: [[MEMRCHR_SEL:%.*]] = select i1 [[TMP2]], i8* getelementptr inbounds ([2147483647 x i8], [2147483647 x i8]* @ai32max, i64 0, i64 2147483646), i8* null +; CHECK-NEXT: ret i8* [[MEMRCHR_SEL]] +; + %ptr = getelementptr [2147483647 x i8], [2147483647 x i8]* @ai32max, i32 0, i32 0 + %chr = tail call i8* @memrchr(i8* %ptr, i32 %C, i64 2147483647) + ret i8* %chr +} + +; Fold memrchr(ai32max, C, N) to C == '\0' && N ? ai32max + N - 1 : null. + +define i8* @fold_memrchr_ai32max_C_N(i32 %C, i64 %N) { +; CHECK-LABEL: @fold_memrchr_ai32max_C_N( +; CHECK-NEXT: [[TMP1:%.*]] = trunc i32 [[C:%.*]] to i8 +; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i8 [[TMP1]], 0 +; CHECK-NEXT: [[TMP3:%.*]] = icmp ne i64 [[N:%.*]], 0 +; CHECK-NEXT: [[TMP4:%.*]] = select i1 [[TMP3]], i1 [[TMP2]], i1 false +; CHECK-NEXT: [[TMP5:%.*]] = add i64 [[N]], -1 +; CHECK-NEXT: [[TMP6:%.*]] = getelementptr [2147483647 x i8], [2147483647 x i8]* @ai32max, i64 0, i64 [[TMP5]] +; CHECK-NEXT: [[MEMRCHR_SEL:%.*]] = select i1 [[TMP4]], i8* [[TMP6]], i8* null +; CHECK-NEXT: ret i8* [[MEMRCHR_SEL]] +; + %ptr = getelementptr [2147483647 x i8], [2147483647 x i8]* @ai32max, i32 0, i32 0 + %chr = tail call i8* @memrchr(i8* %ptr, i32 %C, i64 %N) + ret i8* %chr +}