Index: llvm/lib/Analysis/ValueTracking.cpp =================================================================== --- llvm/lib/Analysis/ValueTracking.cpp +++ llvm/lib/Analysis/ValueTracking.cpp @@ -4187,16 +4187,15 @@ // Look through bitcast instructions and geps. V = V->stripPointerCasts(); - // If the value is a GEP instruction or constant expression, treat it as an - // offset. if (const GEPOperator *GEP = dyn_cast(V)) { - // Fail if the first GEP operand is not a constant zero and we're - // not indexing into the initializer. - const ConstantInt *FirstIdx = dyn_cast(GEP->getOperand(1)); - if (!FirstIdx || !FirstIdx->isZero()) - return false; - + // Drill down into the first operand, ignoring any intervening casts, + // and determine the identity of the referenced object along with + // the byte offset into it. Value *Op0 = GEP->getOperand(0); + Op0 = Op0->stripPointerCasts(); + // TODO: Add a DataLayout argument to the function to avoid the cast + // below and to extend the algorithm to more than a single level of + // GEPs and casts. const GlobalVariable *GV = dyn_cast(Op0); if (!GV) return false; @@ -4287,6 +4286,10 @@ if (!getConstantDataArrayInfo(V, Slice, 8, Offset)) return false; + if (TrimAtNul && Slice.Length == 0) + // When the caller expects a string fail for empty/unterminated arrays. + return false; + if (Slice.Array == nullptr) { if (TrimAtNul) { Str = StringRef(); @@ -4367,7 +4370,12 @@ if (!getConstantDataArrayInfo(V, Slice, CharSize)) return 0; + if (Slice.Length == 0) + // Fail if the offset into the array is just past the end. + return 0; + if (Slice.Array == nullptr) + // Zeroinitializer. return 1; // Search for nul characters Index: llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp =================================================================== --- llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp +++ llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp @@ -953,6 +953,12 @@ return nullptr; } + if (Str.size() == 0) + // If the array is empty fold memrchr(A, C, N) to null for any value + // of C and nonconstant N on the basis that N must be zero (otherwise + // the call would be undefined). + return LenC ? nullptr : NullPtr; + if (ConstantInt *CharC = dyn_cast(CharVal)) { // Fold memrchr(S, C, N) for a constant C. size_t Pos = Str.rfind(CharC->getZExtValue(), EndOff); @@ -1046,6 +1052,12 @@ return B.CreateSelect(Cmp, NullPtr, SrcPlus); } + if (Str.size() == 0) + // If the array is empty fold memchr(A, C, N) to null for any value + // of C and nonconstant N on the basis that N must be zero (otherwise + // the call would be undefined). + return LenC ? nullptr : NullPtr; + if (LenC) Str = substr(Str, LenC->getZExtValue()); Index: llvm/test/Transforms/InstCombine/memchr-10.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/InstCombine/memchr-10.ll @@ -0,0 +1,86 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -passes=instcombine -S | FileCheck %s +; +; Verify that the result of memchr calls with past-the-end pointers used +; in equality expressions don't cause trouble and either are folded when +; they might be valid or not when they're provably undefined. + +declare i8* @memchr(i8*, i32, i64) + + +@a5 = constant [5 x i8] c"12345" + + +; TODO: Do not fold memchr(a5 + 5, c, 1) == a5 + 5. +; The call is transformed to a5[5] == c by the memchr simplifier, with +; a5[5] being indeterminate. The equality then is the folded with +; an undefined/arbitrary result. + +define i1 @call_memchr_ap5_c_1_eq_a(i32 %c, i64 %n) { +; CHECK-LABEL: @call_memchr_ap5_c_1_eq_a( +; CHECK-NEXT: ret i1 false +; + %pap5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 + %qap5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 1, i32 0 + %q = call i8* @memchr(i8* %pap5, i32 %c, i64 1) + %cmp = icmp eq i8* %q, %qap5 + ret i1 %cmp +} + + +; Do not fold memchr(a5 + 5, c, 5) == a5 + 5. + +define i1 @call_memchr_ap5_c_5_eq_a(i32 %c, i64 %n) { +; CHECK-LABEL: @call_memchr_ap5_c_5_eq_a( +; CHECK-NEXT: [[Q:%.*]] = call i8* @memchr(i8* noundef nonnull dereferenceable(1) getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 1, i64 0), i32 [[C:%.*]], i64 5) +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8* [[Q]], getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 1, i64 0) +; CHECK-NEXT: ret i1 [[CMP]] +; + %pap5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 + %qap5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 1, i32 0 + %q = call i8* @memchr(i8* %pap5, i32 %c, i64 5) + %cmp = icmp eq i8* %q, %qap5 + ret i1 %cmp +} + + +; Fold memchr(a5 + 5, c, n) == a5 to false. + +define i1 @fold_memchr_ap5_c_n_eq_a(i32 %c, i64 %n) { +; CHECK-LABEL: @fold_memchr_ap5_c_n_eq_a( +; CHECK-NEXT: ret i1 false +; + %pa = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 0 + %pap5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 + %q = call i8* @memchr(i8* %pap5, i32 %c, i64 %n) + %cmp = icmp eq i8* %q, %pa + ret i1 %cmp +} + + +; Fold memchr(a5 + 5, c, n) == null to true on the basis that n must +; be zero in order for the call to be valid. + +define i1 @fold_memchr_ap5_c_n_eqz(i32 %c, i64 %n) { +; CHECK-LABEL: @fold_memchr_ap5_c_n_eqz( +; CHECK-NEXT: ret i1 true +; + %p = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 + %q = call i8* @memchr(i8* %p, i32 %c, i64 %n) + %cmp = icmp eq i8* %q, null + ret i1 %cmp +} + + +; Fold memchr(a5 + 5, '\0', n) == null to true on the basis that n must +; be zero in order for the call to be valid. + +define i1 @fold_memchr_a_nul_n_eqz(i64 %n) { +; CHECK-LABEL: @fold_memchr_a_nul_n_eqz( +; CHECK-NEXT: ret i1 true +; + %p = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 + %q = call i8* @memchr(i8* %p, i32 0, i64 %n) + %cmp = icmp eq i8* %q, null + ret i1 %cmp +} Index: llvm/test/Transforms/InstCombine/memchr-9.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/InstCombine/memchr-9.ll @@ -0,0 +1,273 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; Verify that calls to memchr with arrays of elements larger than char +; are folded correctly. +; RUN: opt < %s -passes=instcombine -S -data-layout="E" | FileCheck %s +; RUN: opt < %s -passes=instcombine -S -data-layout="e" | FileCheck %s +; +; Exercise folding of memchr calls with addition expressions involving +; pointers into constant arrays of types larger than char and fractional +; offsets. + +declare i8* @memchr(i8*, i32, i64) + +%struct.A = type { [2 x i16], [2 x i16] } + +; Hex byte representation: 00 00 01 01 02 02 03 03 +@a = constant [1 x %struct.A] [%struct.A { [2 x i16] [i16 0, i16 257], [2 x i16] [i16 514, i16 771] }] + + +define void @fold_memchr_A_pIb_cst_cst(i8** %pchr) { +; CHECK-LABEL: @fold_memchr_A_pIb_cst_cst( +; CHECK-NEXT: store i8* bitcast ([1 x %struct.A]* @a to i8*), i8** [[PCHR:%.*]], align 8 +; CHECK-NEXT: [[PST_0_1_1:%.*]] = getelementptr i8*, i8** [[PCHR]], i64 1 +; CHECK-NEXT: store i8* null, i8** [[PST_0_1_1]], align 8 +; CHECK-NEXT: [[PST_0_4_4:%.*]] = getelementptr i8*, i8** [[PCHR]], i64 2 +; CHECK-NEXT: store i8* null, i8** [[PST_0_4_4]], align 8 +; CHECK-NEXT: [[PST_1_0_1:%.*]] = getelementptr i8*, i8** [[PCHR]], i64 3 +; CHECK-NEXT: store i8* getelementptr (i8, i8* bitcast ([1 x %struct.A]* @a to i8*), i64 1), i8** [[PST_1_0_1]], align 8 +; CHECK-NEXT: [[PST_1_0_3:%.*]] = getelementptr i8*, i8** [[PCHR]], i64 4 +; CHECK-NEXT: store i8* getelementptr (i8, i8* bitcast ([1 x %struct.A]* @a to i8*), i64 1), i8** [[PST_1_0_3]], align 8 +; CHECK-NEXT: [[PST_1_1_1:%.*]] = getelementptr i8*, i8** [[PCHR]], i64 5 +; CHECK-NEXT: store i8* null, i8** [[PST_1_1_1]], align 8 +; CHECK-NEXT: [[PST_1_1_2:%.*]] = getelementptr i8*, i8** [[PCHR]], i64 6 +; CHECK-NEXT: store i8* bitcast (i16* getelementptr inbounds ([1 x %struct.A], [1 x %struct.A]* @a, i64 0, i64 0, i32 0, i64 1) to i8*), i8** [[PST_1_1_2]], align 8 +; CHECK-NEXT: [[PST_1_3_3:%.*]] = getelementptr i8*, i8** [[PCHR]], i64 7 +; CHECK-NEXT: store i8* null, i8** [[PST_1_3_3]], align 8 +; CHECK-NEXT: [[PST_1_3_4:%.*]] = getelementptr i8*, i8** [[PCHR]], i64 8 +; CHECK-NEXT: store i8* null, i8** [[PST_1_3_4]], align 8 +; CHECK-NEXT: [[PST_1_3_6:%.*]] = getelementptr i8*, i8** [[PCHR]], i64 10 +; CHECK-NEXT: store i8* bitcast (i16* getelementptr inbounds ([1 x %struct.A], [1 x %struct.A]* @a, i64 0, i64 0, i32 1, i64 1) to i8*), i8** [[PST_1_3_6]], align 8 +; CHECK-NEXT: ret void +; + %pa = getelementptr [1 x %struct.A], [1 x %struct.A]* @a, i64 0, i64 0 + %pi8a = bitcast %struct.A* %pa to i8* + + %pi8ap0 = getelementptr i8, i8* %pi8a, i32 0 + + ; Fold memchr((char*)a + 0, '\0', 1) to a. + %pst_0_0_1 = getelementptr i8*, i8** %pchr, i32 0 + %chr_0_0_1 = call i8* @memchr(i8* %pi8ap0, i32 0, i64 1) + store i8* %chr_0_0_1, i8** %pst_0_0_1 + + ; Fold memchr((char*)a + 0, '\01', 1) to null. + %pst_0_1_1 = getelementptr i8*, i8** %pchr, i32 1 + %chr_0_1_1 = call i8* @memchr(i8* %pi8ap0, i32 1, i64 1) + store i8* %chr_0_1_1, i8** %pst_0_1_1 + + ; Fold memchr((char*)a + 0, '\04', 4) to null. + %pst_0_4_4 = getelementptr i8*, i8** %pchr, i32 2 + %chr_0_4_4 = call i8* @memchr(i8* %pi8ap0, i32 4, i64 4) + store i8* %chr_0_4_4, i8** %pst_0_4_4 + + + %pi8ap1 = getelementptr i8, i8* %pi8a, i32 1 + + ; Fold memchr((char*)a + 1, '\0', 1) to (char*)a + 1. + %pst_1_0_1 = getelementptr i8*, i8** %pchr, i32 3 + %chr_1_0_1 = call i8* @memchr(i8* %pi8ap1, i32 0, i64 1) + store i8* %chr_1_0_1, i8** %pst_1_0_1 + + ; Fold memchr((char*)a + 1, '\0', 3) to (char*)a + 1. + %pst_1_0_3 = getelementptr i8*, i8** %pchr, i32 4 + %chr_1_0_3 = call i8* @memchr(i8* %pi8ap1, i32 0, i64 3) + store i8* %chr_1_0_3, i8** %pst_1_0_3 + + ; Fold memchr((char*)a + 1, '\01', 1) to null. + %pst_1_1_1 = getelementptr i8*, i8** %pchr, i32 5 + %chr_1_1_1 = call i8* @memchr(i8* %pi8ap1, i32 1, i64 1) + store i8* %chr_1_1_1, i8** %pst_1_1_1 + + ; Fold memchr((char*)a + 1, '\01', 2) to (char*)a + 2. + %pst_1_1_2 = getelementptr i8*, i8** %pchr, i32 6 + %chr_1_1_2 = call i8* @memchr(i8* %pi8ap1, i32 1, i64 2) + store i8* %chr_1_1_2, i8** %pst_1_1_2 + + ; Fold memchr((char*)a + 1, '\03', 3) to null. + %pst_1_3_3 = getelementptr i8*, i8** %pchr, i32 7 + %chr_1_3_3 = call i8* @memchr(i8* %pi8ap1, i32 3, i64 3) + store i8* %chr_1_3_3, i8** %pst_1_3_3 + + ; Fold memchr((char*)a + 1, '\03', 4) to null. + %pst_1_3_4 = getelementptr i8*, i8** %pchr, i32 8 + %chr_1_3_4 = call i8* @memchr(i8* %pi8ap1, i32 3, i64 4) + store i8* %chr_1_3_4, i8** %pst_1_3_4 + + ; Fold memchr((char*)a + 1, '\03', 5) to null. + %pst_1_3_5 = getelementptr i8*, i8** %pchr, i32 9 + %chr_1_3_5 = call i8* @memchr(i8* %pi8ap1, i32 3, i64 5) + store i8* %chr_1_3_4, i8** %pst_1_3_4 + + ; Fold memchr((char*)a + 1, '\03', 6) to (char*)a + 5. + %pst_1_3_6 = getelementptr i8*, i8** %pchr, i32 10 + %chr_1_3_6 = call i8* @memchr(i8* %pi8ap1, i32 3, i64 6) + store i8* %chr_1_3_6, i8** %pst_1_3_6 + + + ret void +} + + +define void @fold_memchr_A_pIb_cst_N(i64 %N, i8** %pchr) { +; CHECK-LABEL: @fold_memchr_A_pIb_cst_N( +; CHECK-NEXT: [[MEMCHR_CMP:%.*]] = icmp eq i64 [[N:%.*]], 0 +; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[MEMCHR_CMP]], i8* null, i8* bitcast ([1 x %struct.A]* @a to i8*) +; CHECK-NEXT: store i8* [[TMP1]], i8** [[PCHR:%.*]], align 8 +; CHECK-NEXT: [[PST_0_1_N:%.*]] = getelementptr i8*, i8** [[PCHR]], i64 1 +; CHECK-NEXT: [[MEMCHR_CMP1:%.*]] = icmp ult i64 [[N]], 3 +; CHECK-NEXT: [[TMP2:%.*]] = select i1 [[MEMCHR_CMP1]], i8* null, i8* bitcast (i16* getelementptr inbounds ([1 x %struct.A], [1 x %struct.A]* @a, i64 0, i64 0, i32 0, i64 1) to i8*) +; CHECK-NEXT: store i8* [[TMP2]], i8** [[PST_0_1_N]], align 8 +; CHECK-NEXT: [[PST_0_4_N:%.*]] = getelementptr i8*, i8** [[PCHR]], i64 2 +; CHECK-NEXT: store i8* null, i8** [[PST_0_4_N]], align 8 +; CHECK-NEXT: [[PST_1_0_N:%.*]] = getelementptr i8*, i8** [[PCHR]], i64 3 +; CHECK-NEXT: [[MEMCHR_CMP2:%.*]] = icmp eq i64 [[N]], 0 +; CHECK-NEXT: [[TMP3:%.*]] = select i1 [[MEMCHR_CMP2]], i8* null, i8* getelementptr (i8, i8* bitcast ([1 x %struct.A]* @a to i8*), i64 1) +; CHECK-NEXT: store i8* [[TMP3]], i8** [[PST_1_0_N]], align 8 +; CHECK-NEXT: [[PST_1_1_N:%.*]] = getelementptr i8*, i8** [[PCHR]], i64 4 +; CHECK-NEXT: [[MEMCHR_CMP3:%.*]] = icmp ult i64 [[N]], 2 +; CHECK-NEXT: [[TMP4:%.*]] = select i1 [[MEMCHR_CMP3]], i8* null, i8* bitcast (i16* getelementptr inbounds ([1 x %struct.A], [1 x %struct.A]* @a, i64 0, i64 0, i32 0, i64 1) to i8*) +; CHECK-NEXT: store i8* [[TMP4]], i8** [[PST_1_1_N]], align 8 +; CHECK-NEXT: [[PST_1_2_N:%.*]] = getelementptr i8*, i8** [[PCHR]], i64 5 +; CHECK-NEXT: [[MEMCHR_CMP4:%.*]] = icmp ult i64 [[N]], 4 +; CHECK-NEXT: [[TMP5:%.*]] = select i1 [[MEMCHR_CMP4]], i8* null, i8* bitcast (i16* getelementptr inbounds ([1 x %struct.A], [1 x %struct.A]* @a, i64 0, i64 0, i32 1, i64 0) to i8*) +; CHECK-NEXT: store i8* [[TMP5]], i8** [[PST_1_2_N]], align 8 +; CHECK-NEXT: [[PST_1_3_N:%.*]] = getelementptr i8*, i8** [[PCHR]], i64 6 +; CHECK-NEXT: [[MEMCHR_CMP5:%.*]] = icmp ult i64 [[N]], 6 +; CHECK-NEXT: [[TMP6:%.*]] = select i1 [[MEMCHR_CMP5]], i8* null, i8* bitcast (i16* getelementptr inbounds ([1 x %struct.A], [1 x %struct.A]* @a, i64 0, i64 0, i32 1, i64 1) to i8*) +; CHECK-NEXT: store i8* [[TMP6]], i8** [[PST_1_3_N]], align 8 +; CHECK-NEXT: [[PST_1_4_N:%.*]] = getelementptr i8*, i8** [[PCHR]], i64 7 +; CHECK-NEXT: store i8* null, i8** [[PST_1_4_N]], align 8 +; CHECK-NEXT: [[PST_2_0_N:%.*]] = getelementptr i8*, i8** [[PCHR]], i64 8 +; CHECK-NEXT: store i8* null, i8** [[PST_2_0_N]], align 8 +; CHECK-NEXT: [[PST_2_1_N:%.*]] = getelementptr i8*, i8** [[PCHR]], i64 9 +; CHECK-NEXT: [[MEMCHR_CMP6:%.*]] = icmp eq i64 [[N]], 0 +; CHECK-NEXT: [[TMP7:%.*]] = select i1 [[MEMCHR_CMP6]], i8* null, i8* bitcast (i16* getelementptr inbounds ([1 x %struct.A], [1 x %struct.A]* @a, i64 0, i64 0, i32 0, i64 1) to i8*) +; CHECK-NEXT: store i8* [[TMP7]], i8** [[PST_2_1_N]], align 8 +; CHECK-NEXT: [[PST_2_2_N:%.*]] = getelementptr i8*, i8** [[PCHR]], i64 10 +; CHECK-NEXT: [[MEMCHR_CMP7:%.*]] = icmp ult i64 [[N]], 3 +; CHECK-NEXT: [[TMP8:%.*]] = select i1 [[MEMCHR_CMP7]], i8* null, i8* bitcast (i16* getelementptr inbounds ([1 x %struct.A], [1 x %struct.A]* @a, i64 0, i64 0, i32 1, i64 0) to i8*) +; CHECK-NEXT: store i8* [[TMP8]], i8** [[PST_2_2_N]], align 8 +; CHECK-NEXT: [[PST_2_3_N:%.*]] = getelementptr i8*, i8** [[PCHR]], i64 11 +; CHECK-NEXT: [[MEMCHR_CMP8:%.*]] = icmp ult i64 [[N]], 5 +; CHECK-NEXT: [[TMP9:%.*]] = select i1 [[MEMCHR_CMP8]], i8* null, i8* bitcast (i16* getelementptr inbounds ([1 x %struct.A], [1 x %struct.A]* @a, i64 0, i64 0, i32 1, i64 1) to i8*) +; CHECK-NEXT: store i8* [[TMP9]], i8** [[PST_2_3_N]], align 8 +; CHECK-NEXT: [[PST_2_4_N:%.*]] = getelementptr i8*, i8** [[PCHR]], i64 12 +; CHECK-NEXT: store i8* null, i8** [[PST_2_4_N]], align 8 +; CHECK-NEXT: ret void +; + %pa = getelementptr [1 x %struct.A], [1 x %struct.A]* @a, i64 0, i64 0 + %pi8a = bitcast %struct.A* %pa to i8* + + %pi8ap0 = getelementptr i8, i8* %pi8a, i32 0 + + ; Fold memchr((char*)a + 0, '\0', N) to N ? a : null. + %pst_0_0_n = getelementptr i8*, i8** %pchr, i32 0 + %chr_0_0_n = call i8* @memchr(i8* %pi8ap0, i32 0, i64 %N) + store i8* %chr_0_0_n, i8** %pst_0_0_n + + ; Fold memchr((char*)a + 0, '\01', N) to N < 2 ? null : a. + %pst_0_1_n = getelementptr i8*, i8** %pchr, i32 1 + %chr_0_1_n = call i8* @memchr(i8* %pi8ap0, i32 1, i64 %N) + store i8* %chr_0_1_n, i8** %pst_0_1_n + + ; Fold memchr((char*)a + 0, '\04', N) to null. + %pst_0_4_n = getelementptr i8*, i8** %pchr, i32 2 + %chr_0_4_n = call i8* @memchr(i8* %pi8ap0, i32 4, i64 %N) + store i8* %chr_0_4_n, i8** %pst_0_4_n + + + %pi8ap1 = getelementptr i8, i8* %pi8a, i32 1 + + ; Fold memchr((char*)a + 1, '\0', N) to null. + %pst_1_0_n = getelementptr i8*, i8** %pchr, i32 3 + %chr_1_0_n = call i8* @memchr(i8* %pi8ap1, i32 0, i64 %N) + store i8* %chr_1_0_n, i8** %pst_1_0_n + + ; Fold memchr((char*)a + 1, '\01', N) N ? (char*)a + 1 : null. + %pst_1_1_n = getelementptr i8*, i8** %pchr, i32 4 + %chr_1_1_n = call i8* @memchr(i8* %pi8ap1, i32 1, i64 %N) + store i8* %chr_1_1_n, i8** %pst_1_1_n + + ; Fold memchr((char*)a + 1, '\02', N) to N < 2 ? null : (char*)a + 4. + %pst_1_2_n = getelementptr i8*, i8** %pchr, i32 5 + %chr_1_2_n = call i8* @memchr(i8* %pi8ap1, i32 2, i64 %N) + store i8* %chr_1_2_n, i8** %pst_1_2_n + + ; Fold memchr((char*)a + 1, '\03', N) to N < 6 ? null : (char*)a + 6. + %pst_1_3_n = getelementptr i8*, i8** %pchr, i32 6 + %chr_1_3_n = call i8* @memchr(i8* %pi8ap1, i32 3, i64 %N) + store i8* %chr_1_3_n, i8** %pst_1_3_n + + ; Fold memchr((char*)a + 1, '\04', N) to null. + %pst_1_4_n = getelementptr i8*, i8** %pchr, i32 7 + %chr_1_4_n = call i8* @memchr(i8* %pi8ap1, i32 4, i64 %N) + store i8* %chr_1_4_n, i8** %pst_1_4_n + + + %pi8ap2 = getelementptr i8, i8* %pi8a, i32 2 + + ; Fold memchr((char*)a + 2, '\0', N) to null. + %pst_2_0_n = getelementptr i8*, i8** %pchr, i32 8 + %chr_2_0_n = call i8* @memchr(i8* %pi8ap2, i32 0, i64 %N) + store i8* %chr_2_0_n, i8** %pst_2_0_n + + ; Fold memchr((char*)a + 2, '\01', N) N ? (char*)a + 2 : null. + %pst_2_1_n = getelementptr i8*, i8** %pchr, i32 9 + %chr_2_1_n = call i8* @memchr(i8* %pi8ap2, i32 1, i64 %N) + store i8* %chr_2_1_n, i8** %pst_2_1_n + + ; Fold memchr((char*)a + 2, '\02', N) to N < 3 ? null : (char*)a + 2. + %pst_2_2_n = getelementptr i8*, i8** %pchr, i32 10 + %chr_2_2_n = call i8* @memchr(i8* %pi8ap2, i32 2, i64 %N) + store i8* %chr_2_2_n, i8** %pst_2_2_n + + ; Fold memchr((char*)a + 2, '\03', N) to N < 5 ? null : (char*)a + 4. + %pst_2_3_n = getelementptr i8*, i8** %pchr, i32 11 + %chr_2_3_n = call i8* @memchr(i8* %pi8ap2, i32 3, i64 %N) + store i8* %chr_2_3_n, i8** %pst_2_3_n + + ; Fold memchr((char*)a + 2, '\04', N) to null. + %pst_2_4_n = getelementptr i8*, i8** %pchr, i32 12 + %chr_2_4_n = call i8* @memchr(i8* %pi8ap2, i32 4, i64 %N) + store i8* %chr_2_4_n, i8** %pst_2_4_n + + ret void +} + + +; Verify that calls with out of bounds offsets are not folded. + +define void @call_memchr_A_pIb_xs_cst(i8** %pchr) { +; CHECK-LABEL: @call_memchr_A_pIb_xs_cst( +; +; Verify that the call isn't folded when the first GEP index is excessive. + %pa1 = getelementptr [1 x %struct.A], [1 x %struct.A]* @a, i64 1, i64 0 + %pi8a1 = bitcast %struct.A* %pa1 to i8* + + %pi8a1p0 = getelementptr i8, i8* %pi8a1, i32 0 + + ; Don't fold memchr((char*)&a[1] + 0, '\0', 2). + %pst_1_0_0_2 = getelementptr i8*, i8** %pchr, i32 0 + %chr_1_0_0_2 = call i8* @memchr(i8* %pi8a1p0, i32 0, i64 2) + store i8* %chr_1_0_0_2, i8** %pst_1_0_0_2 + + %pi8a1p1 = getelementptr i8, i8* %pi8a1, i32 1 + + ; Likewise, don't fold memchr((char*)&a[1] + 1, '\0', 2). + %pst_1_0_1_2 = getelementptr i8*, i8** %pchr, i32 1 + %chr_1_0_1_2 = call i8* @memchr(i8* %pi8a1p0, i32 0, i64 2) + store i8* %chr_1_0_1_2, i8** %pst_1_0_1_2 + +; Verify that the call isn't folded when the first GEP index is in bounds +; but the byte offset is excessive. + %pa0 = getelementptr [1 x %struct.A], [1 x %struct.A]* @a, i64 0, i64 0 + %pi8a0 = bitcast %struct.A* %pa0 to i8* + + %pi8a0p8 = getelementptr i8, i8* %pi8a0, i32 8 + + ; Don't fold memchr((char*)&a[0] + 8, '\0', 2). + %pst_0_0_8_2 = getelementptr i8*, i8** %pchr, i32 2 + %chr_0_0_8_2 = call i8* @memchr(i8* %pi8a0p8, i32 0, i64 2) + store i8* %chr_0_0_8_2, i8** %pst_0_0_8_2 + + ret void +} Index: llvm/test/Transforms/InstCombine/memcmp-7.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/InstCombine/memcmp-7.ll @@ -0,0 +1,144 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -passes=instcombine -S | FileCheck %s +; +; Exercise folding of memcmp calls with addition expressions involving +; pointers into constant arrays of types larger than char and fractional +; offsets. + +declare i32 @memcmp(i8*, i8*, i64) + +@i32a = constant [2 x i16] [i16 4386, i16 13124] +@i32b = constant [2 x i16] [i16 4386, i16 13124] + + +define void @fold_memcmp_i32a_i32b_pIb(i32 %I, i32* %pcmp) +; CHECK-LABEL: @fold_memcmp_i32a_i32b_pIb( +; CHECK-NEXT: store i32 0, i32* [[PCMP:%.*]], align 4 +; CHECK-NEXT: [[PST_1_1_2:%.*]] = getelementptr i32, i32* [[PCMP]], i64 1 +; CHECK-NEXT: store i32 0, i32* [[PST_1_1_2]], align 4 +; CHECK-NEXT: [[PST_1_1_3:%.*]] = getelementptr i32, i32* [[PCMP]], i64 2 +; CHECK-NEXT: store i32 0, i32* [[PST_1_1_3]], align 4 +; CHECK-NEXT: ret void +; +{ + %pi32a = getelementptr [2 x i16], [2 x i16]* @i32a, i32 0, i32 0 + %pi32b = getelementptr [2 x i16], [2 x i16]* @i32b, i32 0, i32 0 + + %pi8a = bitcast i16* %pi32a to i8* + %pi8b = bitcast i16* %pi32b to i8* + + %pi8ap1 = getelementptr i8, i8* %pi8a, i32 1 + %pi8bp1 = getelementptr i8, i8* %pi8b, i32 1 + + %pst_1_1_1 = getelementptr i32, i32* %pcmp, i32 0 + %cmp_1_1_1 = call i32 @memcmp(i8* %pi8ap1, i8* %pi8ap1, i64 1) + store i32 %cmp_1_1_1, i32* %pst_1_1_1 + + %pst_1_1_2 = getelementptr i32, i32* %pcmp, i32 1 + %cmp_1_1_2 = call i32 @memcmp(i8* %pi8ap1, i8* %pi8ap1, i64 2) + store i32 %cmp_1_1_2, i32* %pst_1_1_2 + + %pst_1_1_3 = getelementptr i32, i32* %pcmp, i32 2 + %cmp_1_1_3 = call i32 @memcmp(i8* %pi8ap1, i8* %pi8ap1, i64 3) + store i32 %cmp_1_1_3, i32* %pst_1_1_3 + + ret void +} + + +%struct.A = type { [4 x i8] } +%struct.B = type { [2 x i8], [2 x i8] } + +@a = constant [1 x %struct.A] [%struct.A { [4 x i8] [i8 1, i8 2, i8 3, i8 4] }] +@b = constant [1 x %struct.B] [%struct.B { [2 x i8] [i8 1, i8 2], [2 x i8] [i8 3, i8 4]}] + +define void @fold_memcmp_A_B_pIb(i32 %I, i32* %pcmp) { +; CHECK-LABEL: @fold_memcmp_A_B_pIb( +; CHECK-NEXT: store i32 0, i32* [[PCMP:%.*]], align 4 +; CHECK-NEXT: [[PST_0_0_2:%.*]] = getelementptr i32, i32* [[PCMP]], i64 1 +; CHECK-NEXT: store i32 0, i32* [[PST_0_0_2]], align 4 +; CHECK-NEXT: [[PST_0_0_3:%.*]] = getelementptr i32, i32* [[PCMP]], i64 2 +; CHECK-NEXT: store i32 0, i32* [[PST_0_0_3]], align 4 +; CHECK-NEXT: [[PST_0_0_4:%.*]] = getelementptr i32, i32* [[PCMP]], i64 3 +; CHECK-NEXT: store i32 0, i32* [[PST_0_0_4]], align 4 +; CHECK-NEXT: [[PST_0_1_1:%.*]] = getelementptr i32, i32* [[PCMP]], i64 4 +; CHECK-NEXT: store i32 -1, i32* [[PST_0_1_1]], align 4 +; CHECK-NEXT: [[PST_0_1_2:%.*]] = getelementptr i32, i32* [[PCMP]], i64 5 +; CHECK-NEXT: store i32 -1, i32* [[PST_0_1_2]], align 4 +; CHECK-NEXT: [[PST_0_1_3:%.*]] = getelementptr i32, i32* [[PCMP]], i64 6 +; CHECK-NEXT: store i32 -1, i32* [[PST_0_1_3]], align 4 +; CHECK-NEXT: [[PST_1_0_1:%.*]] = getelementptr i32, i32* [[PCMP]], i64 4 +; CHECK-NEXT: store i32 1, i32* [[PST_1_0_1]], align 4 +; CHECK-NEXT: [[PST_1_0_2:%.*]] = getelementptr i32, i32* [[PCMP]], i64 5 +; CHECK-NEXT: store i32 1, i32* [[PST_1_0_2]], align 4 +; CHECK-NEXT: [[PST_1_0_3:%.*]] = getelementptr i32, i32* [[PCMP]], i64 6 +; CHECK-NEXT: store i32 1, i32* [[PST_1_0_3]], align 4 +; CHECK-NEXT: ret void +; + %pa = getelementptr [1 x %struct.A], [1 x %struct.A]* @a, i64 0, i64 0 + %pb = getelementptr [1 x %struct.B], [1 x %struct.B]* @b, i64 0, i64 0 + + %pi8a = bitcast %struct.A* %pa to i8* + %pi8b = bitcast %struct.B* %pb to i8* + + %pi8ap0 = getelementptr i8, i8* %pi8a, i32 0 + %pi8bp0 = getelementptr i8, i8* %pi8b, i32 0 + + ; Fold memcmp(&a, &b, 1) to 0; + %pst_0_0_1 = getelementptr i32, i32* %pcmp, i32 0 + %cmp_0_0_1 = call i32 @memcmp(i8* %pi8ap0, i8* %pi8bp0, i64 1) + store i32 %cmp_0_0_1, i32* %pst_0_0_1 + + ; Fold memcmp(&a, &b, 2) to 0; + %pst_0_0_2 = getelementptr i32, i32* %pcmp, i32 1 + %cmp_0_0_2 = call i32 @memcmp(i8* %pi8ap0, i8* %pi8bp0, i64 2) + store i32 %cmp_0_0_2, i32* %pst_0_0_2 + + ; Fold memcmp(&a, &b, 3) to 0; + %pst_0_0_3 = getelementptr i32, i32* %pcmp, i32 2 + %cmp_0_0_3 = call i32 @memcmp(i8* %pi8ap0, i8* %pi8bp0, i64 3) + store i32 %cmp_0_0_3, i32* %pst_0_0_3 + + ; Fold memcmp(&a, &b, 4) to 0; + %pst_0_0_4 = getelementptr i32, i32* %pcmp, i32 3 + %cmp_0_0_4 = call i32 @memcmp(i8* %pi8ap0, i8* %pi8bp0, i64 4) + store i32 %cmp_0_0_4, i32* %pst_0_0_4 + + + %pi8bp1 = getelementptr i8, i8* %pi8b, i32 1 + + ; Fold memcmp(&a, (char*)&b + 1, 1) to -1; + %pst_0_1_1 = getelementptr i32, i32* %pcmp, i32 4 + %cmp_0_1_1 = call i32 @memcmp(i8* %pi8ap0, i8* %pi8bp1, i64 1) + store i32 %cmp_0_1_1, i32* %pst_0_1_1 + + ; Fold memcmp(&a, (char*)&b + 1, 2) to -1; + %pst_0_1_2 = getelementptr i32, i32* %pcmp, i32 5 + %cmp_0_1_2 = call i32 @memcmp(i8* %pi8ap0, i8* %pi8bp1, i64 2) + store i32 %cmp_0_1_2, i32* %pst_0_1_2 + + ; Fold memcmp(&a, (char*)&b + 1, 3) to -1; + %pst_0_1_3 = getelementptr i32, i32* %pcmp, i32 6 + %cmp_0_1_3 = call i32 @memcmp(i8* %pi8ap0, i8* %pi8bp1, i64 3) + store i32 %cmp_0_1_3, i32* %pst_0_1_3 + + + %pi8ap1 = getelementptr i8, i8* %pi8a, i32 1 + + ; Fold memcmp((char*)&a + 1, &b, 1) to +1; + %pst_1_0_1 = getelementptr i32, i32* %pcmp, i32 4 + %cmp_1_0_1 = call i32 @memcmp(i8* %pi8ap1, i8* %pi8bp0, i64 1) + store i32 %cmp_1_0_1, i32* %pst_1_0_1 + + ; Fold memcmp((char*)&a + 1, &b, 2) to +1; + %pst_1_0_2 = getelementptr i32, i32* %pcmp, i32 5 + %cmp_1_0_2 = call i32 @memcmp(i8* %pi8ap1, i8* %pi8bp0, i64 2) + store i32 %cmp_1_0_2, i32* %pst_1_0_2 + + ; Fold memcmp((char*)&a + 1, &b, 3) to +1; + %pst_1_0_3 = getelementptr i32, i32* %pcmp, i32 6 + %cmp_1_0_3 = call i32 @memcmp(i8* %pi8ap1, i8* %pi8bp0, i64 3) + store i32 %cmp_1_0_3, i32* %pst_1_0_3 + + ret void +} Index: llvm/test/Transforms/InstCombine/memcmp-8.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/InstCombine/memcmp-8.ll @@ -0,0 +1,53 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -passes=instcombine -S | FileCheck %s +; +; Verify that the result of memrchr calls with past-the-end pointers used +; don't cause trouble and are optimally folded. + +declare i32 @memcmp(i8*, i8*, i64) + + +@a5 = constant [5 x i8] c"12345"; + + +; Fold memcmp(a5, a5 + 5, n) to 0 on the assumption that n is 0 otherwise +; the call would be undefined. + +define i32 @fold_memcmp_a5_a5p5_n(i64 %n) { +; CHECK-LABEL: @fold_memcmp_a5_a5p5_n( +; CHECK-NEXT: ret i32 0 +; + %pa5_p0 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 0 + %pa5_p5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 + %cmp = call i32 @memcmp(i8* %pa5_p0, i8* %pa5_p5, i64 %n) + ret i32 %cmp +} + + +; Same as above but for memcmp(a5 + 5, a5 + 5, n). + +define i32 @fold_memcmp_a5p5_a5p5_n(i64 %n) { +; CHECK-LABEL: @fold_memcmp_a5p5_a5p5_n( +; CHECK-NEXT: ret i32 0 +; + %pa5_p5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 + %qa5_p5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 + %cmp = call i32 @memcmp(i8* %pa5_p5, i8* %qa5_p5, i64 %n) + ret i32 %cmp +} + + +; TODO: Likewise, fold memcmp(a5 + i, a5 + 5, n) to 0 on same basis. + +define i32 @fold_memcmp_a5pi_a5p5_n(i32 %i, i64 %n) { +; CHECK-LABEL: @fold_memcmp_a5pi_a5p5_n( +; CHECK-NEXT: [[TMP1:%.*]] = sext i32 [[I:%.*]] to i64 +; CHECK-NEXT: [[PA5_PI:%.*]] = getelementptr [5 x i8], [5 x i8]* @a5, i64 0, i64 [[TMP1]] +; CHECK-NEXT: [[CMP:%.*]] = call i32 @memcmp(i8* [[PA5_PI]], i8* getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 1, i64 0), i64 [[N:%.*]]) +; CHECK-NEXT: ret i32 [[CMP]] +; + %pa5_pi = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 %i + %pa5_p5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 + %cmp = call i32 @memcmp(i8* %pa5_pi, i8* %pa5_p5, i64 %n) + ret i32 %cmp +} Index: llvm/test/Transforms/InstCombine/memrchr-7.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/InstCombine/memrchr-7.ll @@ -0,0 +1,86 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -passes=instcombine -S | FileCheck %s +; +; Verify that the result of memrchr calls with past-the-end pointers used +; in equality expressions don't cause trouble and either are folded when +; they might be valid or not when they're provably undefined. + +declare i8* @memrchr(i8*, i32, i64) + + +@a5 = constant [5 x i8] c"12345" + + +; TODO: Do not fold memrchr(a5 + 5, c, 1) == a5 + 5. +; The call is transformed to a5[5] == c by the memrchr simplifier, with +; a5[5] being indeterminate. The equality then is the folded with +; an undefined/arbitrary result. + +define i1 @call_memrchr_ap5_c_1_eq_a(i32 %c, i64 %n) { +; CHECK-LABEL: @call_memrchr_ap5_c_1_eq_a( +; CHECK-NEXT: ret i1 false +; + %pap5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 + %qap5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 1, i32 0 + %q = call i8* @memrchr(i8* %pap5, i32 %c, i64 1) + %cmp = icmp eq i8* %q, %qap5 + ret i1 %cmp +} + + +; Do not fold memrchr(a5 + 5, c, 5) == a5 + 5. + +define i1 @call_memrchr_ap5_c_5_eq_a(i32 %c, i64 %n) { +; CHECK-LABEL: @call_memrchr_ap5_c_5_eq_a( +; CHECK-NEXT: [[Q:%.*]] = call i8* @memrchr(i8* noundef nonnull dereferenceable(5) getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 1, i64 0), i32 [[C:%.*]], i64 5) +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8* [[Q]], getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 1, i64 0) +; CHECK-NEXT: ret i1 [[CMP]] +; + %pap5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 + %qap5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 1, i32 0 + %q = call i8* @memrchr(i8* %pap5, i32 %c, i64 5) + %cmp = icmp eq i8* %q, %qap5 + ret i1 %cmp +} + + +; Fold memrchr(a5 + 5, c, n) == a5 to false. + +define i1 @fold_memrchr_ap5_c_n_eq_a(i32 %c, i64 %n) { +; CHECK-LABEL: @fold_memrchr_ap5_c_n_eq_a( +; CHECK-NEXT: ret i1 false +; + %pa = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 0 + %pap5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 + %q = call i8* @memrchr(i8* %pap5, i32 %c, i64 %n) + %cmp = icmp eq i8* %q, %pa + ret i1 %cmp +} + + +; Fold memrchr(a5 + 5, c, n) == null to true on the basis that n must +; be zero in order for the call to be valid. + +define i1 @fold_memrchr_ap5_c_n_eqz(i32 %c, i64 %n) { +; CHECK-LABEL: @fold_memrchr_ap5_c_n_eqz( +; CHECK-NEXT: ret i1 true +; + %p = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 + %q = call i8* @memrchr(i8* %p, i32 %c, i64 %n) + %cmp = icmp eq i8* %q, null + ret i1 %cmp +} + + +; Fold memrchr(a5 + 5, '\0', n) == null to true again on the basis that +; n must be zero in order for the call to be valid. + +define i1 @fold_memrchr_a_nul_n_eqz(i64 %n) { +; CHECK-LABEL: @fold_memrchr_a_nul_n_eqz( +; CHECK-NEXT: ret i1 true +; + %p = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 + %q = call i8* @memrchr(i8* %p, i32 0, i64 %n) + %cmp = icmp eq i8* %q, null + ret i1 %cmp +} Index: llvm/test/Transforms/InstCombine/strcall-no-nul.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/InstCombine/strcall-no-nul.ll @@ -0,0 +1,290 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -passes=instcombine -S | FileCheck %s +; +; Verify that calls with arguments with pointers just past the end of +; a string to [a subset of] library functions that expect nul-terminated +; strings as are accepted but not folded. The rationale is that since +; they are undefined folding them isn't important for efficiency and +; prevents sanitizers from detecting and reporting them. A counter- +; argument is that when sanitizers don't run, transforming such invalid +; calls to something valid could be safer than letting the program run +; off the rails. See the Safe Optimizations for Sanitizers RFC for more: +; https://discourse.llvm.org/t/rfc-safe-optimizations-for-sanitizers +; This test might need to be adjusted depending on the outcome of +; the effort. + +declare i8* @strchr(i8*, i32) +declare i8* @strrchr(i8*, i32) +declare i32 @strcmp(i8*, i8*) +declare i32 @strncmp(i8*, i8*, i64) +declare i8* @strstr(i8*, i8*) + +declare i8* @strcpy(i8*, i8*) +declare i8* @strncpy(i8*, i8*, i64) + +declare i64 @strlen(i8*) +declare i64 @strnlen(i8*, i64) + +declare i8* @strpbrk(i8*, i8*) + +declare i64 @strspn(i8*, i8*) +declare i64 @strcspn(i8*, i8*) + +declare i32 @sprintf(i8*, i8*, ...) +declare i32 @snprintf(i8*, i64, i8*, ...) + + +@a5 = constant [5 x i8] c"%s\0045"; + + +define i8* @call_strchr_past_end() { +; CHECK-LABEL: @call_strchr_past_end( +; CHECK-NEXT: [[STRLEN:%.*]] = call i64 @strlen(i8* noundef nonnull dereferenceable(1) getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 1, i64 0)) +; CHECK-NEXT: [[STRCHR:%.*]] = getelementptr [5 x i8], [5 x i8]* @a5, i64 1, i64 [[STRLEN]] +; CHECK-NEXT: ret i8* [[STRCHR]] +; + %p = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 + %q = call i8* @strchr(i8* %p, i32 0) + ret i8* %q +} + + +define void @call_strcmp_past_end(i32* %pcmp) { +; CHECK-LABEL: @call_strcmp_past_end( +; CHECK-NEXT: [[C05:%.*]] = call i32 @strcmp(i8* noundef nonnull dereferenceable(3) getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 0, i64 0), i8* noundef nonnull dereferenceable(1) getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 1, i64 0)) +; CHECK-NEXT: store i32 [[C05]], i32* [[PCMP:%.*]], align 4 +; CHECK-NEXT: [[C50:%.*]] = call i32 @strcmp(i8* noundef nonnull dereferenceable(1) getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 1, i64 0), i8* noundef nonnull dereferenceable(3) getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 0, i64 0)) +; CHECK-NEXT: [[PC50:%.*]] = getelementptr i32, i32* [[PCMP]], i64 1 +; CHECK-NEXT: store i32 [[C50]], i32* [[PC50]], align 4 +; CHECK-NEXT: ret void +; + %p0 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 0 + %p5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 + + %c05 = call i32 @strcmp(i8* %p0, i8* %p5) + %pc05 = getelementptr i32, i32* %pcmp, i32 0 + store i32 %c05, i32* %pc05 + + %c50 = call i32 @strcmp(i8* %p5, i8* %p0) + %pc50 = getelementptr i32, i32* %pcmp, i32 1 + store i32 %c50, i32* %pc50 + + ret void +} + + +define void @call_strncmp_past_end(i32* %pcmp) { +; CHECK-LABEL: @call_strncmp_past_end( +; CHECK-NEXT: [[C05:%.*]] = call i32 @strncmp(i8* noundef nonnull dereferenceable(3) getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 0, i64 0), i8* noundef nonnull dereferenceable(1) getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 1, i64 0), i64 5) +; CHECK-NEXT: store i32 [[C05]], i32* [[PCMP:%.*]], align 4 +; CHECK-NEXT: [[C50:%.*]] = call i32 @strncmp(i8* noundef nonnull dereferenceable(1) getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 1, i64 0), i8* noundef nonnull dereferenceable(3) getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 0, i64 0), i64 5) +; CHECK-NEXT: [[PC50:%.*]] = getelementptr i32, i32* [[PCMP]], i64 1 +; CHECK-NEXT: store i32 [[C50]], i32* [[PC50]], align 4 +; CHECK-NEXT: ret void +; + %p0 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 0 + %p5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 + + %c05 = call i32 @strncmp(i8* %p0, i8* %p5, i64 5) + %pc05 = getelementptr i32, i32* %pcmp, i32 0 + store i32 %c05, i32* %pc05 + + %c50 = call i32 @strncmp(i8* %p5, i8* %p0, i64 5) + %pc50 = getelementptr i32, i32* %pcmp, i32 1 + store i32 %c50, i32* %pc50 + + ret void +} + + +define i8* @call_strrchr_past_end(i32 %c) { +; CHECK-LABEL: @call_strrchr_past_end( +; CHECK-NEXT: [[STRLEN:%.*]] = call i64 @strlen(i8* noundef nonnull dereferenceable(1) getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 1, i64 0)) +; CHECK-NEXT: [[STRCHR1:%.*]] = getelementptr [5 x i8], [5 x i8]* @a5, i64 1, i64 [[STRLEN]] +; CHECK-NEXT: ret i8* [[STRCHR1]] +; + %p5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 + %r = call i8* @strrchr(i8* %p5, i32 0) + ret i8* %r +} + + +define void @call_strstr_past_end(i8** %psub) { +; CHECK-LABEL: @call_strstr_past_end( +; CHECK-NEXT: [[S05:%.*]] = call i8* @strstr(i8* noundef nonnull dereferenceable(1) getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 0, i64 0), i8* noundef nonnull dereferenceable(1) getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 1, i64 0)) +; CHECK-NEXT: store i8* [[S05]], i8** [[PSUB:%.*]], align 8 +; CHECK-NEXT: [[S50:%.*]] = call i8* @strstr(i8* noundef nonnull dereferenceable(1) getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 1, i64 0), i8* noundef nonnull dereferenceable(1) getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 0, i64 0)) +; CHECK-NEXT: [[PS50:%.*]] = getelementptr i8*, i8** [[PSUB]], i64 1 +; CHECK-NEXT: store i8* [[S50]], i8** [[PS50]], align 8 +; CHECK-NEXT: ret void +; + %p0 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 0 + %p5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 + + %s05 = call i8* @strstr(i8* %p0, i8* %p5) + %ps05 = getelementptr i8*, i8** %psub, i32 0 + store i8* %s05, i8** %ps05 + + %s50 = call i8* @strstr(i8* %p5, i8* %p0) + %ps50 = getelementptr i8*, i8** %psub, i32 1 + store i8* %s50, i8** %ps50 + + ret void +} + + +define i64 @call_strlen_past_end() { +; CHECK-LABEL: @call_strlen_past_end( +; CHECK-NEXT: [[R:%.*]] = call i64 @strlen(i8* noundef nonnull dereferenceable(1) getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 1, i64 0)) +; CHECK-NEXT: ret i64 [[R]] +; + %p5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 + %r = call i64 @strlen(i8* %p5) + ret i64 %r +} + + +define i8* @call_strcpy_past_end(i8* %dst) { +; CHECK-LABEL: @call_strcpy_past_end( +; CHECK-NEXT: [[R:%.*]] = call i8* @strcpy(i8* noundef nonnull dereferenceable(1) [[DST:%.*]], i8* noundef nonnull dereferenceable(1) getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 1, i64 0)) +; CHECK-NEXT: ret i8* [[R]] +; + %p5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 + %r = call i8* @strcpy(i8* %dst, i8* %p5) + ret i8* %r +} + + +define i8* @call_strncpy_past_end(i8* %dst) { +; CHECK-LABEL: @call_strncpy_past_end( +; CHECK-NEXT: [[R:%.*]] = call i8* @strncpy(i8* noundef nonnull dereferenceable(1) [[DST:%.*]], i8* noundef nonnull dereferenceable(1) getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 1, i64 0), i64 5) +; CHECK-NEXT: ret i8* [[R]] +; + %p5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 + %r = call i8* @strncpy(i8* %dst, i8* %p5, i64 5) + ret i8* %r +} + + +define void @call_strpbrk_past_end(i8** %psub) { +; CHECK-LABEL: @call_strpbrk_past_end( +; CHECK-NEXT: [[S05:%.*]] = call i8* @strpbrk(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 0, i64 0), i8* getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 1, i64 0)) +; CHECK-NEXT: store i8* [[S05]], i8** [[PSUB:%.*]], align 8 +; CHECK-NEXT: [[S50:%.*]] = call i8* @strpbrk(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 1, i64 0), i8* getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 0, i64 0)) +; CHECK-NEXT: [[PS50:%.*]] = getelementptr i8*, i8** [[PSUB]], i64 1 +; CHECK-NEXT: store i8* [[S50]], i8** [[PS50]], align 8 +; CHECK-NEXT: ret void +; + %p0 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 0 + %p5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 + + %s05 = call i8* @strpbrk(i8* %p0, i8* %p5) + %ps05 = getelementptr i8*, i8** %psub, i32 0 + store i8* %s05, i8** %ps05 + + %s50 = call i8* @strpbrk(i8* %p5, i8* %p0) + %ps50 = getelementptr i8*, i8** %psub, i32 1 + store i8* %s50, i8** %ps50 + + ret void +} + + +define void @call_strspn_past_end(i64* %poff) { +; CHECK-LABEL: @call_strspn_past_end( +; CHECK-NEXT: [[O05:%.*]] = call i64 @strspn(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 0, i64 0), i8* getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 1, i64 0)) +; CHECK-NEXT: store i64 [[O05]], i64* [[POFF:%.*]], align 4 +; CHECK-NEXT: [[O50:%.*]] = call i64 @strspn(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 1, i64 0), i8* getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 0, i64 0)) +; CHECK-NEXT: [[PO50:%.*]] = getelementptr i64, i64* [[POFF]], i64 1 +; CHECK-NEXT: store i64 [[O50]], i64* [[PO50]], align 4 +; CHECK-NEXT: ret void +; + %p0 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 0 + %p5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 + + %o05 = call i64 @strspn(i8* %p0, i8* %p5) + %po05 = getelementptr i64, i64* %poff, i32 0 + store i64 %o05, i64* %po05 + + %o50 = call i64 @strspn(i8* %p5, i8* %p0) + %po50 = getelementptr i64, i64* %poff, i32 1 + store i64 %o50, i64* %po50 + + ret void +} + + +define void @call_strcspn_past_end(i64* %poff) { +; CHECK-LABEL: @call_strcspn_past_end( +; CHECK-NEXT: [[O05:%.*]] = call i64 @strcspn(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 0, i64 0), i8* getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 1, i64 0)) +; CHECK-NEXT: store i64 [[O05]], i64* [[POFF:%.*]], align 4 +; CHECK-NEXT: [[O50:%.*]] = call i64 @strcspn(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 1, i64 0), i8* getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 0, i64 0)) +; CHECK-NEXT: [[PO50:%.*]] = getelementptr i64, i64* [[POFF]], i64 1 +; CHECK-NEXT: store i64 [[O50]], i64* [[PO50]], align 4 +; CHECK-NEXT: ret void +; + %p0 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 0 + %p5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 + + %o05 = call i64 @strcspn(i8* %p0, i8* %p5) + %po05 = getelementptr i64, i64* %poff, i32 0 + store i64 %o05, i64* %po05 + + %o50 = call i64 @strcspn(i8* %p5, i8* %p0) + %po50 = getelementptr i64, i64* %poff, i32 1 + store i64 %o50, i64* %po50 + + ret void +} + + +define void @call_sprintf_past_end(i32* %pcnt, i8* %dst) { +; CHECK-LABEL: @call_sprintf_past_end( +; CHECK-NEXT: [[N5_:%.*]] = call i32 (i8*, i8*, ...) @sprintf(i8* noundef nonnull dereferenceable(1) [[DST:%.*]], i8* noundef nonnull dereferenceable(1) getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 1, i64 0)) +; CHECK-NEXT: store i32 [[N5_]], i32* [[PCNT:%.*]], align 4 +; CHECK-NEXT: [[STPCPY:%.*]] = call i8* @stpcpy(i8* [[DST]], i8* getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 1, i64 0)) +; CHECK-NEXT: [[TMP1:%.*]] = ptrtoint i8* [[STPCPY]] to i64 +; CHECK-NEXT: [[TMP2:%.*]] = ptrtoint i8* [[DST]] to i64 +; CHECK-NEXT: [[TMP3:%.*]] = sub i64 [[TMP1]], [[TMP2]] +; CHECK-NEXT: [[TMP4:%.*]] = trunc i64 [[TMP3]] to i32 +; CHECK-NEXT: [[PN05:%.*]] = getelementptr i32, i32* [[PCNT]], i64 1 +; CHECK-NEXT: store i32 [[TMP4]], i32* [[PN05]], align 4 +; CHECK-NEXT: ret void +; + %p0 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 0 + %p5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 + + %n5_ = call i32 (i8*, i8*, ...) @sprintf(i8* %dst, i8* %p5) + %pn5_ = getelementptr i32, i32* %pcnt, i32 0 + store i32 %n5_, i32* %pn5_ + + %n05 = call i32 (i8*, i8*, ...) @sprintf(i8* %dst, i8* %p0, i8* %p5) + %pn05 = getelementptr i32, i32* %pcnt, i32 1 + store i32 %n05, i32* %pn05 + + ret void +} + + +define void @call_snprintf_past_end(i32* %pcnt, i8* %dst, i64 %n) { +; CHECK-LABEL: @call_snprintf_past_end( +; CHECK-NEXT: [[N5_:%.*]] = call i32 (i8*, i64, i8*, ...) @snprintf(i8* [[DST:%.*]], i64 [[N:%.*]], i8* getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 1, i64 0)) +; CHECK-NEXT: store i32 [[N5_]], i32* [[PCNT:%.*]], align 4 +; CHECK-NEXT: [[N05:%.*]] = call i32 (i8*, i64, i8*, ...) @snprintf(i8* [[DST]], i64 [[N]], i8* getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 0, i64 0), i8* getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 1, i64 0)) +; CHECK-NEXT: [[PN05:%.*]] = getelementptr i32, i32* [[PCNT]], i64 1 +; CHECK-NEXT: store i32 [[N05]], i32* [[PN05]], align 4 +; CHECK-NEXT: ret void +; + %p0 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 0 + %p5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 + + %n5_ = call i32 (i8*, i64, i8*, ...) @snprintf(i8* %dst, i64 %n, i8* %p5) + %pn5_ = getelementptr i32, i32* %pcnt, i32 0 + store i32 %n5_, i32* %pn5_ + + %n05 = call i32 (i8*, i64, i8*, ...) @snprintf(i8* %dst, i64 %n, i8* %p0, i8* %p5) + %pn05 = getelementptr i32, i32* %pcnt, i32 1 + store i32 %n05, i32* %pn05 + + ret void +} Index: llvm/test/Transforms/InstCombine/strnlen-1.ll =================================================================== --- llvm/test/Transforms/InstCombine/strnlen-1.ll +++ llvm/test/Transforms/InstCombine/strnlen-1.ll @@ -69,9 +69,9 @@ define i64 @fold_strnlen_ax_1() { ; CHECK-LABEL: @fold_strnlen_ax_1( ; CHECK-NEXT: [[STRNLEN_CHAR0:%.*]] = load i8, i8* getelementptr inbounds ([0 x i8], [0 x i8]* @ax, i64 0, i64 0), align 1 -; CHECK-NEXT: [[STRNLEN_CHAR0CMP_NOT:%.*]] = icmp ne i8 [[STRNLEN_CHAR0]], 0 -; CHECK-NEXT: [[STRNLEN_SEL:%.*]] = zext i1 [[STRNLEN_CHAR0CMP_NOT]] to i64 -; CHECK-NEXT: ret i64 [[STRNLEN_SEL]] +; CHECK-NEXT: [[STRNLEN_CHAR0CMP:%.*]] = icmp ne i8 [[STRNLEN_CHAR0]], 0 +; CHECK-NEXT: [[TMP1:%.*]] = zext i1 [[STRNLEN_CHAR0CMP]] to i64 +; CHECK-NEXT: ret i64 [[TMP1]] ; %ptr = getelementptr [0 x i8], [0 x i8]* @ax, i32 0, i32 0 %len = call i64 @strnlen(i8* %ptr, i64 1) @@ -151,13 +151,27 @@ } -; Fold strnlen(s5_3 + 6, 5) to 3. +; Fold strnlen(s5_3 + 6, 3) to 3. -define i64 @fold_strnlen_s5_3_p6_5() { -; CHECK-LABEL: @fold_strnlen_s5_3_p6_5( +define i64 @fold_strnlen_s5_3_p6_3() { +; CHECK-LABEL: @fold_strnlen_s5_3_p6_3( ; CHECK-NEXT: ret i64 3 ; %ptr = getelementptr [9 x i8], [9 x i8]* @s5_3, i32 0, i32 6 - %len = call i64 @strnlen(i8* %ptr, i64 5) + %len = call i64 @strnlen(i8* %ptr, i64 3) + ret i64 %len +} + + +; TODO: Do not fold the invalid strnlen(s5_3 + 6, 4) call. +; This folded because llvm::GetStringLength() doesn't differentiate +; between an array with no terminating nul and a string. + +define i64 @call_strnlen_s5_3_p6_4() { +; CHECK-LABEL: @call_strnlen_s5_3_p6_4( +; CHECK-NEXT: ret i64 3 +; + %ptr = getelementptr [9 x i8], [9 x i8]* @s5_3, i32 0, i32 6 + %len = call i64 @strnlen(i8* %ptr, i64 4) ret i64 %len }