diff --git a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp --- a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp @@ -884,26 +884,69 @@ Value *SrcStr = CI->getArgOperand(0); Value *Size = CI->getArgOperand(2); annotateNonNullAndDereferenceable(CI, 0, Size, DL); - ConstantInt *CharC = dyn_cast(CI->getArgOperand(1)); + Value *CharVal = CI->getArgOperand(1); + ConstantInt *CharC = dyn_cast(CharVal); ConstantInt *LenC = dyn_cast(Size); + // Set to the Size argument value if it's constant or left at maximum. + uint64_t MaxLen = UINT64_MAX; // memchr(x, y, 0) -> null if (LenC) { if (LenC->isZero()) return Constant::getNullValue(CI->getType()); - } else { - // From now on we need at least constant length and string. - return nullptr; + MaxLen = LenC->getZExtValue(); + } + + if (MaxLen == 1) { + // Fold memchr(x, y, 1) --> *x == y ? x : null for any x and y, + // constant or otherwise. + Value *Val = B.CreateLoad(B.getInt8Ty(), SrcStr, "memchr.char0"); + Val = B.CreateZExt(Val, CharVal->getType()); + // Slice off the character's high end bits. + CharVal = B.CreateTrunc(CharVal, B.getInt8Ty()); + CharVal = B.CreateZExt(CharVal, Val->getType()); + Value *Cmp = B.CreateICmpEQ(Val, CharVal, "memchr.char0cmp"); + Value *NullPtr = Constant::getNullValue(CI->getType()); + return B.CreateSelect(Cmp, SrcStr, NullPtr, "memchr.sel"); } StringRef Str; if (!getConstantStringInfo(SrcStr, Str, 0, /*TrimAtNul=*/false)) return nullptr; + if (CharC) { + size_t Pos = Str.find(CharC->getZExtValue()); + if (Pos == StringRef::npos) + // When the character is not in the source array fold the result + // to null regardless of Size. + return Constant::getNullValue(CI->getType()); + + if (MaxLen == UINT64_MAX) { + // Fold memchr(s, c, n) -> n < Pos ? null : s + Pos + Value *Cmp = B.CreateICmpULE(Size, ConstantInt::get(Size->getType(), Pos), + "memchr.cmp"); + Value *NullPtr = Constant::getNullValue(CI->getType()); + Value *SrcPlus = + B.CreateGEP(B.getInt8Ty(), SrcStr, B.getInt64(Pos), "memchr.ptr"); + return B.CreateSelect(Cmp, NullPtr, SrcPlus); + } + + if (MaxLen < Pos) + // When the constant Size is less than the character position + // fold the result to null. + return Constant::getNullValue(CI->getType()); + + // Set MaxLen so that the handling below folds the result either + // to a pointer to the array or to null. + MaxLen = Pos < MaxLen ? Pos + 1 : Pos; + } else if (MaxLen == UINT64_MAX) + // From now on we need at least constant length and constant array. + return nullptr; + // Truncate the string to LenC. If Str is smaller than LenC we will still only // scan the string, as reading past the end of it is undefined and we can just // return null if we don't find the char. - Str = Str.substr(0, LenC->getZExtValue()); + Str = Str.substr(0, MaxLen); // If the char is variable but the input str and length are not we can turn // this memchr call into a simple bit field test. Of course this only works @@ -939,7 +982,7 @@ Value *BitfieldC = B.getInt(Bitfield); // Adjust width of "C" to the bitfield width, then mask off the high bits. - Value *C = B.CreateZExtOrTrunc(CI->getArgOperand(1), BitfieldC->getType()); + Value *C = B.CreateZExtOrTrunc(CharVal, BitfieldC->getType()); C = B.CreateAnd(C, B.getIntN(Width, 0xFF)); // First check that the bit field access is within bounds. diff --git a/llvm/test/Transforms/InstCombine/memchr-2.ll b/llvm/test/Transforms/InstCombine/memchr-2.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/memchr-2.ll @@ -0,0 +1,109 @@ +; Verify that memchr calls with constant arrays, or constant characters, +; or constant bounds are folded (or not) as expected. +; +; RUN: opt < %s -passes=instcombine -S | FileCheck %s + +declare i8* @memchr(i8*, i32, i64) + +@ax = external global [0 x i8] +@a12345 = constant [5 x i8] c"\01\02\03\04\05" + + +; Fold memchr(a12345, '\06', n) to null. + +define i8* @fold_memchr_a12345_6_n(i64 %n) { +; CHECK-LABEL: @fold_memchr_a12345_6_n( +; CHECK-NEXT: ret i8* null + + %ptr = getelementptr [5 x i8], [5 x i8]* @a12345, i32 0, i32 0 + %res = call i8* @memchr(i8* %ptr, i32 6, i64 %n) + ret i8* %res +} + + +; Fold memchr(a12345, '\04', 2) to null. + +define i8* @fold_memchr_a12345_4_2() { +; CHECK-LABEL: @fold_memchr_a12345_4_2( +; CHECK-NEXT: ret i8* null + + %ptr = getelementptr [5 x i8], [5 x i8]* @a12345, i32 0, i32 0 + %res = call i8* @memchr(i8* %ptr, i32 4, i64 2) + ret i8* %res +} + + +; Fold memchr(a12345, '\04', 3) to null. + +define i8* @fold_memchr_a12345_4_3() { +; CHECK-LABEL: @fold_memchr_a12345_4_3( +; CHECK-NEXT: ret i8* null + + %ptr = getelementptr [5 x i8], [5 x i8]* @a12345, i32 0, i32 0 + %res = call i8* @memchr(i8* %ptr, i32 4, i64 3) + ret i8* %res +} + + +; Fold memchr(a12345, '\03', 3) to a12345 + 2. + +define i8* @fold_memchr_a12345_3_3() { +; CHECK-LABEL: @fold_memchr_a12345_3_3( +; CHECK-NEXT: ret i8* getelementptr inbounds ([5 x i8], [5 x i8]* @a12345, i64 0, i64 2) + + %ptr = getelementptr [5 x i8], [5 x i8]* @a12345, i32 0, i32 0 + %res = call i8* @memchr(i8* %ptr, i32 3, i64 3) + ret i8* %res +} + + +; Fold memchr(a12345, '\03', 9) to a12345 + 2. + +define i8* @fold_memchr_a12345_3_9() { +; CHECK-LABEL: @fold_memchr_a12345_3_9( +; CHECK-NEXT: ret i8* getelementptr inbounds ([5 x i8], [5 x i8]* @a12345, i64 0, i64 2) + + %ptr = getelementptr [5 x i8], [5 x i8]* @a12345, i32 0, i32 0 + %res = call i8* @memchr(i8* %ptr, i32 3, i64 9) + ret i8* %res +} + + +; Fold memchr(a12345, '\03', n) to n < 3 ? null : a12345 + 3. + +define i8* @call_a12345_3_n(i64 %n) { +; CHECK-LABEL: @call_a12345_3_n( +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i64 %n, 3 +; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP]], i8* null, i8* getelementptr inbounds ([5 x i8], [5 x i8]* @a12345, i64 0, i64 2) + + %ptr = getelementptr [5 x i8], [5 x i8]* @a12345, i32 0, i32 0 + %res = call i8* @memchr(i8* %ptr, i32 3, i64 %n) + ret i8* %res +} + + +; Fold memchr(a12345, 259, n) to n < 4 ? null : a12345 + 3 +; to verify the constant 259 is converted to unsigned char (yielding 3). + +define i8* @call_a12345_259_n(i64 %n) { +; CHECK-LABEL: @call_a12345_259_n( +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i64 %n, 3 +; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP]], i8* null, i8* getelementptr inbounds ([5 x i8], [5 x i8]* @a12345, i64 0, i64 2) + + %ptr = getelementptr [5 x i8], [5 x i8]* @a12345, i32 0, i32 0 + %res = call i8* @memchr(i8* %ptr, i32 259, i64 %n) + ret i8* %res +} + + +; Do no fold memchr(ax, 1, n). + +define i8* @call_ax_1_n(i64 %n) { +; CHECK-LABEL: @call_ax_1_n( +; CHECK-NEXT: [[RES:%.*]] = call i8* @memchr +; CHECK-NEXT: ret i8* [[RES]] + + %ptr = getelementptr [0 x i8], [0 x i8]* @ax, i32 0, i32 0 + %res = call i8* @memchr(i8* %ptr, i32 1, i64 %n) + ret i8* %res +} diff --git a/llvm/test/Transforms/InstCombine/memchr-3.ll b/llvm/test/Transforms/InstCombine/memchr-3.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/memchr-3.ll @@ -0,0 +1,65 @@ +; Verify that the special case of memchr calls with the size of 1 are +; folded as expected. +; +; RUN: opt < %s -passes=instcombine -S | FileCheck %s + +declare i8* @memchr(i8*, i32, i64) + +@ax = external global [0 x i8] +@a12345 = constant [5 x i8] c"\01\02\03\04\05" + + +; Fold memchr(a12345, 1, 1) to a12345. + +define i8* @fold_memchr_a12345_1_1() { +; CHECK-LABEL: @fold_memchr_a12345_1_1( +; CHECK-NEXT: ret i8* getelementptr inbounds ([5 x i8], [5 x i8]* @a12345, i64 0, i64 0) + + %ptr = getelementptr [5 x i8], [5 x i8]* @a12345, i32 0, i32 0 + %res = call i8* @memchr(i8* %ptr, i32 1, i64 1) + ret i8* %res +} + + +; Fold memchr(a12345, 2, 1) to null. + +define i8* @fold_memchr_a12345_2_1() { +; CHECK-LABEL: @fold_memchr_a12345_2_1( +; CHECK-NEXT: ret i8* null + + %ptr = getelementptr [5 x i8], [5 x i8]* @a12345, i32 0, i32 0 + %res = call i8* @memchr(i8* %ptr, i32 2, i64 1) + ret i8* %res +} + + +; Fold memchr(ax, 257, 1) to (unsigned char)*ax == 1 ? ax : null +; to verify the constant 257 is converted to unsigned char (yielding 1). + +define i8* @fold_memchr_ax_257_1(i32 %chr, i64 %n) { +; CHECK-LABEL: @fold_memchr_ax_257_1( +; CHECK-NEXT: [[AX_0:%.*]] = load i8, i8* getelementptr inbounds ([0 x i8], [0 x i8]* @ax, i64 0, i64 0) +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[AX_0]], 1 +; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP]], i8* getelementptr inbounds ([0 x i8], [0 x i8]* @ax, i64 0, i64 0), i8* null +; CHECK-NEXT: ret i8* [[SEL]] + + %ptr = getelementptr [0 x i8], [0 x i8]* @ax, i32 0, i32 0 + %res = call i8* @memchr(i8* %ptr, i32 257, i64 1) + ret i8* %res +} + + +; Fold memchr(ax, c, 1) to (unsigned char)*ax == (unsigned char)c ? ax : null. + +define i8* @fold_memchr_ax_c_1(i32 %chr, i64 %n) { +; CHECK-LABEL: @fold_memchr_ax_c_1( +; CHECK-NEXT: [[AX_0:%.*]] = load i8, i8* getelementptr inbounds ([0 x i8], [0 x i8]* @ax, i64 0, i64 0) +; CHECK-NEXT: [[CHR_TRUNC:%.*]] = trunc i32 %chr to i8 +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[AX_0]], [[CHR_TRUNC]] +; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP]], i8* getelementptr inbounds ([0 x i8], [0 x i8]* @ax, i64 0, i64 0), i8* null +; CHECK-NEXT: ret i8* [[SEL]] + + %ptr = getelementptr [0 x i8], [0 x i8]* @ax, i32 0, i32 0 + %res = call i8* @memchr(i8* %ptr, i32 %chr, i64 1) + ret i8* %res +}