324 changes: 324 additions & 0 deletions llvm/test/Transforms/InstCombine/memchr-9.ll

Large diffs are not rendered by default.

28 changes: 13 additions & 15 deletions llvm/test/Transforms/InstCombine/memcmp-4.ll
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ declare i32 @memcmp(i8*, i8*, i64)
; value (analogous to strncmp) is safer than letting a SIMD library
; implementation return a bogus value.

define void @fold_memcmp_too_big(i32* %pcmp) {
; BE-LABEL: @fold_memcmp_too_big(
define void @fold_memcmp_mismatch_too_big(i32* %pcmp) {
; BE-LABEL: @fold_memcmp_mismatch_too_big(
; BE-NEXT: store i32 -1, i32* [[PCMP:%.*]], align 4
; BE-NEXT: [[PSTOR_CB:%.*]] = getelementptr i32, i32* [[PCMP]], i64 1
; BE-NEXT: store i32 1, i32* [[PSTOR_CB]], align 4
; BE-NEXT: ret void
;
; LE-LABEL: @fold_memcmp_too_big(
; LE-LABEL: @fold_memcmp_mismatch_too_big(
; LE-NEXT: store i32 -1, i32* [[PCMP:%.*]], align 4
; LE-NEXT: [[PSTOR_CB:%.*]] = getelementptr i32, i32* [[PCMP]], i64 1
; LE-NEXT: store i32 1, i32* [[PSTOR_CB]], align 4
Expand All @@ -47,23 +47,21 @@ define void @fold_memcmp_too_big(i32* %pcmp) {
}


; Don't fold calls with excessive byte counts of arrays with the same bytes.
; Fold even calls with excessive byte counts of arrays with matching bytes.
; Like in the instances above, this is preferable to letting the undefined
; calls take place, although it does prevent sanitizers from detecting them.

define void @call_memcmp_too_big(i32* %pcmp) {
; BE-LABEL: @call_memcmp_too_big(
; BE-NEXT: [[CMP_AB_9:%.*]] = call i32 @memcmp(i8* noundef nonnull dereferenceable(9) bitcast ([4 x i16]* @ia16a to i8*), i8* noundef nonnull dereferenceable(9) bitcast ([5 x i16]* @ia16b to i8*), i64 9)
; BE-NEXT: store i32 [[CMP_AB_9]], i32* [[PCMP:%.*]], align 4
; BE-NEXT: [[CMP_AB_M1:%.*]] = call i32 @memcmp(i8* noundef nonnull dereferenceable(18446744073709551615) bitcast ([4 x i16]* @ia16a to i8*), i8* noundef nonnull dereferenceable(18446744073709551615) bitcast ([5 x i16]* @ia16b to i8*), i64 -1)
define void @fold_memcmp_match_too_big(i32* %pcmp) {
; BE-LABEL: @fold_memcmp_match_too_big(
; BE-NEXT: store i32 0, i32* [[PCMP:%.*]], align 4
; BE-NEXT: [[PSTOR_AB_M1:%.*]] = getelementptr i32, i32* [[PCMP]], i64 1
; BE-NEXT: store i32 [[CMP_AB_M1]], i32* [[PSTOR_AB_M1]], align 4
; BE-NEXT: store i32 0, i32* [[PSTOR_AB_M1]], align 4
; BE-NEXT: ret void
;
; LE-LABEL: @call_memcmp_too_big(
; LE-NEXT: [[CMP_AB_9:%.*]] = call i32 @memcmp(i8* noundef nonnull dereferenceable(9) bitcast ([4 x i16]* @ia16a to i8*), i8* noundef nonnull dereferenceable(9) bitcast ([5 x i16]* @ia16b to i8*), i64 9)
; LE-NEXT: store i32 [[CMP_AB_9]], i32* [[PCMP:%.*]], align 4
; LE-NEXT: [[CMP_AB_M1:%.*]] = call i32 @memcmp(i8* noundef nonnull dereferenceable(18446744073709551615) bitcast ([4 x i16]* @ia16a to i8*), i8* noundef nonnull dereferenceable(18446744073709551615) bitcast ([5 x i16]* @ia16b to i8*), i64 -1)
; LE-LABEL: @fold_memcmp_match_too_big(
; LE-NEXT: store i32 0, i32* [[PCMP:%.*]], align 4
; LE-NEXT: [[PSTOR_AB_M1:%.*]] = getelementptr i32, i32* [[PCMP]], i64 1
; LE-NEXT: store i32 [[CMP_AB_M1]], i32* [[PSTOR_AB_M1]], align 4
; LE-NEXT: store i32 0, i32* [[PSTOR_AB_M1]], align 4
; LE-NEXT: ret void
;
%p0 = getelementptr [4 x i16], [4 x i16]* @ia16a, i64 0, i64 0
Expand Down
144 changes: 144 additions & 0 deletions llvm/test/Transforms/InstCombine/memcmp-7.ll
Original file line number Diff line number Diff line change
@@ -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
}
53 changes: 53 additions & 0 deletions llvm/test/Transforms/InstCombine/memcmp-8.ll
Original file line number Diff line number Diff line change
@@ -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
}
84 changes: 84 additions & 0 deletions llvm/test/Transforms/InstCombine/memrchr-7.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
; 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"


; Fold memrchr(a5 + 5, c, 1) == a5 + 5 to an arbitrary constant.
; 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
;
%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
}


; Fold memrchr(a5 + 5, c, 5) == a5 + 5 to an arbitrary constant.

define i1 @call_memrchr_ap5_c_5_eq_a(i32 %c, i64 %n) {
; CHECK-LABEL: @call_memrchr_ap5_c_5_eq_a(
; CHECK-NEXT: ret i1
;
%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
}
13 changes: 6 additions & 7 deletions llvm/test/Transforms/InstCombine/str-int-3.ll
Original file line number Diff line number Diff line change
Expand Up @@ -57,25 +57,24 @@ define void @fold_atoi_member(i32* %pi) {
}


; Do not fold atoi with an excessive offset. It's undefined so folding
; it (e.g., to zero) would be valid and might prevent crashes or returning
; a bogus value but could also prevent detecting the bug by sanitizers.
; Fold atoi with an excessive offset. It's undefined so folding it to zero
; is valid and might prevent crashes or returning a bogus value, even though
; it prevents detecting the bug by sanitizers.

define void @call_atoi_offset_out_of_bounds(i32* %pi) {
; CHECK-LABEL: @call_atoi_offset_out_of_bounds(
; CHECK-NEXT: [[IA_0_0_32:%.*]] = call i32 @atoi(i8* getelementptr inbounds ([2 x %struct.A], [2 x %struct.A]* @a, i64 1, i64 0, i32 0, i64 0))
; CHECK-NEXT: store i32 [[IA_0_0_32]], i32* [[PI:%.*]], align 4
; CHECK-NEXT: store i32 0, i32* [[PI:%.*]], align 4
; CHECK-NEXT: [[IA_0_0_33:%.*]] = call i32 @atoi(i8* getelementptr ([2 x %struct.A], [2 x %struct.A]* @a, i64 1, i64 0, i32 0, i64 1))
; CHECK-NEXT: store i32 [[IA_0_0_33]], i32* [[PI]], align 4
; CHECK-NEXT: ret void
;
; Do not fold atoi((const char*)a + sizeof a).
; Fold atoi((const char*)a + sizeof a) to zero.
%pa_0_0_32 = getelementptr [2 x %struct.A], [2 x %struct.A]* @a, i64 0, i64 0, i32 0, i64 32
%ia_0_0_32 = call i32 @atoi(i8* %pa_0_0_32)
%pia_0_0_32 = getelementptr i32, i32* %pi, i32 0
store i32 %ia_0_0_32, i32* %pia_0_0_32

; Likewise, do not fold atoi((const char*)a + sizeof a + 1).
; Likewise, fold atoi((const char*)a + sizeof a + 1) to zero.
%pa_0_0_33 = getelementptr [2 x %struct.A], [2 x %struct.A]* @a, i64 0, i64 0, i32 0, i64 33
%ia_0_0_33 = call i32 @atoi(i8* %pa_0_0_33)
%pia_0_0_33 = getelementptr i32, i32* %pi, i32 0
Expand Down
319 changes: 319 additions & 0 deletions llvm/test/Transforms/InstCombine/strcall-no-nul.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,319 @@
; 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 arguments are folded to safe values. The rationale is that
; since they are undefined and even though folding them isn't important
; for efficiency and prevents sanitizers from detecting and reporting
; them, sanitizers usually don't run, and transforming such invalid
; calls to something valid is safer than letting the program run off
; the rails. See the Safe Optimizations for Sanitizers RFC for
; an in-depth discussion of the trade-offs:
; https://discourse.llvm.org/t/rfc-safe-optimizations-for-sanitizers

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* @stpcpy(i8*, i8*)
declare i8* @strcpy(i8*, i8*)
declare i8* @stpncpy(i8*, i8*, i64)
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";


; Fold strchr(a5 + 5, '\0') to null.

define i8* @fold_strchr_past_end() {
; CHECK-LABEL: @fold_strchr_past_end(
; CHECK-NEXT: ret i8* getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 1, i64 0)
;
%p = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5
%q = call i8* @strchr(i8* %p, i32 0)
ret i8* %q
}

; Fold strcmp(a5, a5 + 5) (and vice versa) to null.

define void @fold_strcmp_past_end(i32* %pcmp) {
; CHECK-LABEL: @fold_strcmp_past_end(
; CHECK-NEXT: store i32 1, i32* [[PCMP:%.*]], align 4
; CHECK-NEXT: [[PC50:%.*]] = getelementptr i32, i32* [[PCMP]], i64 1
; CHECK-NEXT: store i32 -1, 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
}


; Likewise, fold strncmp(a5, a5 + 5, 5) (and vice versa) to null.

define void @fold_strncmp_past_end(i32* %pcmp) {
; CHECK-LABEL: @fold_strncmp_past_end(
; CHECK-NEXT: store i32 1, i32* [[PCMP:%.*]], align 4
; CHECK-NEXT: [[PC50:%.*]] = getelementptr i32, i32* [[PCMP]], i64 1
; CHECK-NEXT: store i32 -1, 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
}


; Fold strrchr(a5 + 5, '\0') to null.

define i8* @fold_strrchr_past_end(i32 %c) {
; CHECK-LABEL: @fold_strrchr_past_end(
; CHECK-NEXT: ret i8* getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 1, i64 0)
;
%p5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5
%r = call i8* @strrchr(i8* %p5, i32 0)
ret i8* %r
}


; Fold strstr(a5 + 5, a5) (and vice versa) to null.

define void @fold_strstr_past_end(i8** %psub) {
; CHECK-LABEL: @fold_strstr_past_end(
; CHECK-NEXT: store i8* getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 0, i64 0), i8** [[PSUB:%.*]], align 8
; CHECK-NEXT: [[PS50:%.*]] = getelementptr i8*, i8** [[PSUB]], i64 1
; CHECK-NEXT: store i8* null, 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
}


; Fold strlen(a5 + 5) to 0.

define i64 @fold_strlen_past_end() {
; CHECK-LABEL: @fold_strlen_past_end(
; CHECK-NEXT: ret i64 0
;
%p5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5
%r = call i64 @strlen(i8* %p5)
ret i64 %r
}


; TODO: Fold stpcpy(dst, a5 + 5) to (*dst = '\0', dst).

define i8* @fold_stpcpy_past_end(i8* %dst) {
; CHECK-LABEL: @fold_stpcpy_past_end(
; CHECK-NEXT: ret i8* [[DST:%.*]]
;
%p5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5
%r = call i8* @strcpy(i8* %dst, i8* %p5)
ret i8* %r
}


; TODO: Fold strcpy(dst, a5 + 5) to (*dst = '\0', dst).

define i8* @fold_strcpy_past_end(i8* %dst) {
; CHECK-LABEL: @fold_strcpy_past_end(
; CHECK-NEXT: ret i8* [[DST:%.*]]
;
%p5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5
%r = call i8* @strcpy(i8* %dst, i8* %p5)
ret i8* %r
}


; TODO: Fold stpncpy(dst, a5 + 5, 5) to (memset(dst, 0, 5), dst + 5).

define i8* @fold_stpncpy_past_end(i8* %dst) {
; CHECK-LABEL: @fold_stpncpy_past_end(
; CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* noundef nonnull align 1 dereferenceable(5) [[DST:%.*]], i8 0, i64 5, i1 false)
; CHECK-NEXT: ret i8* [[DST]]
;
%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
}


; TODO: Fold strncpy(dst, a5 + 5, 5) to memset(dst, 0, 5).

define i8* @fold_strncpy_past_end(i8* %dst) {
; CHECK-LABEL: @fold_strncpy_past_end(
; CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* noundef nonnull align 1 dereferenceable(5) [[DST:%.*]], i8 0, i64 5, i1 false)
; CHECK-NEXT: ret i8* [[DST]]
;
%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
}


; Fold strpbrk(a5, a5 + 5) (and vice versa) to null.

define void @fold_strpbrk_past_end(i8** %psub) {
; CHECK-LABEL: @fold_strpbrk_past_end(
; CHECK-NEXT: store i8* null, i8** [[PSUB:%.*]], align 8
; CHECK-NEXT: [[PS50:%.*]] = getelementptr i8*, i8** [[PSUB]], i64 1
; CHECK-NEXT: store i8* null, 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
}


; Fold strspn(a5, a5 + 5) (and vice versa) to null.

define void @fold_strspn_past_end(i64* %poff) {
; CHECK-LABEL: @fold_strspn_past_end(
; CHECK-NEXT: store i64 0, i64* [[POFF:%.*]], align 4
; CHECK-NEXT: [[PO50:%.*]] = getelementptr i64, i64* [[POFF]], i64 1
; CHECK-NEXT: store i64 0, 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
}


; Fold strcspn(a5, a5 + 5) (and vice versa) to null.

define void @fold_strcspn_past_end(i64* %poff) {
; CHECK-LABEL: @fold_strcspn_past_end(
; CHECK-NEXT: store i64 2, i64* [[POFF:%.*]], align 4
; CHECK-NEXT: [[PO50:%.*]] = getelementptr i64, i64* [[POFF]], i64 1
; CHECK-NEXT: store i64 0, 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
}


; Fold sprintf(dst, a5 + 5) to zero, and also
; TODO: fold sprintf(dst, "%s", a5 + 5) to zero.

define void @fold_sprintf_past_end(i32* %pcnt, i8* %dst) {
; CHECK-LABEL: @fold_sprintf_past_end(
; CHECK-NEXT: store i32 0, i32* [[PCNT:%.*]], align 4
; CHECK-NEXT: [[PN05:%.*]] = getelementptr i32, i32* [[PCNT]], i64 1
; CHECK-NEXT: store i32 0, 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
}


; Fold snprintf(dst, n, a5 + 5) to zero, and also
; TODO: fold snprintf(dst, n, "%s", a5 + 5) to zero.

define void @fold_snprintf_past_end(i32* %pcnt, i8* %dst, i64 %n) {
; CHECK-LABEL: @fold_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
}
91 changes: 91 additions & 0 deletions llvm/test/Transforms/InstCombine/strlen-9.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
;
; Verify that strlen calls with unterminated constant arrays or with
; just past-the-end pointers to strings are not folded.
;
; RUN: opt < %s -passes=instcombine -S | FileCheck %s

declare i64 @strlen(i8*)

@a5 = constant [5 x i8] c"12345"
@s5 = constant [6 x i8] c"12345\00"
@z0 = constant [0 x i8] zeroinitializer
@z5 = constant [5 x i8] zeroinitializer


; Verify that all the invalid calls below are folded. This is safer than
; making the library calls even though it prevents sanitizers from reporting
; the bugs.

define void @fold_strlen_no_nul(i64* %plen, i32 %i) {
; CHECK-LABEL: @fold_strlen_no_nul(
; CHECK-NEXT: store i64 5, i64* [[PLEN:%.*]], align 4
; CHECK-NEXT: [[PNA5_P5:%.*]] = getelementptr i64, i64* [[PLEN]], i64 1
; CHECK-NEXT: store i64 0, i64* [[PNA5_P5]], align 4
; CHECK-NEXT: [[PNS5_P6:%.*]] = getelementptr i64, i64* [[PLEN]], i64 2
; CHECK-NEXT: store i64 0, i64* [[PNS5_P6]], align 4
; 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: [[NA5_PI:%.*]] = call i64 @strlen(i8* noundef nonnull dereferenceable(1) [[PA5_PI]])
; CHECK-NEXT: [[PNA5_PI:%.*]] = getelementptr i64, i64* [[PLEN]], i64 3
; CHECK-NEXT: store i64 [[NA5_PI]], i64* [[PNA5_PI]], align 4
; CHECK-NEXT: [[PNZ0_P0:%.*]] = getelementptr i64, i64* [[PLEN]], i64 4
; CHECK-NEXT: store i64 0, i64* [[PNZ0_P0]], align 4
; CHECK-NEXT: [[TMP2:%.*]] = sext i32 [[I]] to i64
; CHECK-NEXT: [[PZ0_PI:%.*]] = getelementptr [0 x i8], [0 x i8]* @z0, i64 0, i64 [[TMP2]]
; CHECK-NEXT: [[NZ0_PI:%.*]] = call i64 @strlen(i8* noundef nonnull dereferenceable(1) [[PZ0_PI]])
; CHECK-NEXT: [[PNZ0_PI:%.*]] = getelementptr i64, i64* [[PLEN]], i64 5
; CHECK-NEXT: store i64 [[NZ0_PI]], i64* [[PNZ0_PI]], align 4
; CHECK-NEXT: [[PNZ5_P5:%.*]] = getelementptr i64, i64* [[PLEN]], i64 6
; CHECK-NEXT: store i64 0, i64* [[PNZ5_P5]], align 4
; CHECK-NEXT: ret void
;
; Verify that strlen(a5) is folded to 5.
%pa0_p0 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 0
%na5_p0 = call i64 @strlen(i8* %pa0_p0)
%pna5_p0 = getelementptr i64, i64* %plen, i64 0
store i64 %na5_p0, i64* %pna5_p0

; Verify that strlen(a5 + 5) is folded to 0.
%pa5_p5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5
%na5_p5 = call i64 @strlen(i8* %pa5_p5)
%pna5_p5 = getelementptr i64, i64* %plen, i64 1
store i64 %na5_p5, i64* %pna5_p5

; Verify that strlen(s5 + 6) is folded to 0.
%ps5_p6 = getelementptr [6 x i8], [6 x i8]* @s5, i32 0, i32 6
%ns5_p6 = call i64 @strlen(i8* %ps5_p6)
%pns5_p6 = getelementptr i64, i64* %plen, i64 2
store i64 %ns5_p6, i64* %pns5_p6

; TODO: Verify that strlen(a5 + i) is folded to 5 - i? It's currently
; not folded because the variable offset makes getConstantDataArrayInfo
; fail.
%pa5_pi = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 %i
%na5_pi = call i64 @strlen(i8* %pa5_pi)
%pna5_pi = getelementptr i64, i64* %plen, i64 3
store i64 %na5_pi, i64* %pna5_pi

; Verify that strlen(z0) is folded to 0.
%pz0_p0 = getelementptr [0 x i8], [0 x i8]* @z0, i32 0, i32 0
%nz0_p0 = call i64 @strlen(i8* %pz0_p0)
%pnz0_p0 = getelementptr i64, i64* %plen, i64 4
store i64 %nz0_p0, i64* %pnz0_p0

; TODO: Verify that strlen(z0 + i) is folded to 0. As the case above,
; this one is not folded either because the variable offset makes
; getConstantDataArrayInfo fail.

%pz0_pi = getelementptr [0 x i8], [0 x i8]* @z0, i32 0, i32 %i
%nz0_pi = call i64 @strlen(i8* %pz0_pi)
%pnz0_pi = getelementptr i64, i64* %plen, i64 5
store i64 %nz0_pi, i64* %pnz0_pi

; Verify that strlen(z5 + 5) is folded to 0.
%pz5_p5 = getelementptr [5 x i8], [5 x i8]* @z5, i32 0, i32 5
%nz5_p5 = call i64 @strlen(i8* %pz5_p5)
%pnz5_p5 = getelementptr i64, i64* %plen, i64 6
store i64 %nz5_p5, i64* %pnz5_p5

ret void
}
357 changes: 357 additions & 0 deletions llvm/test/Transforms/InstCombine/strncmp-5.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,357 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -passes=instcombine -S | FileCheck %s
;
; Exercise folding of strncmp calls with constant arrays and nonconstant
; sizes.

declare i32 @strncmp(i8*, i8*, i64)

@ax = external constant [8 x i8]
@a01230123 = constant [8 x i8] c"01230123"
@b01230123 = constant [8 x i8] c"01230123"
@c01230129 = constant [8 x i8] c"01230129"
@d9123_12 = constant [7 x i8] c"9123\0012"
@e9123_34 = constant [7 x i8] c"9123\0034"


; Exercise strncmp(A, B, N) folding of arrays with the same bytes.

define void @fold_strncmp_a_b_n(i32* %pcmp, i64 %n) {
; CHECK-LABEL: @fold_strncmp_a_b_n(
; CHECK-NEXT: store i32 0, i32* [[PCMP:%.*]], align 4
; CHECK-NEXT: [[TMP1:%.*]] = icmp ne i64 [[N:%.*]], 0
; CHECK-NEXT: [[TMP2:%.*]] = sext i1 [[TMP1]] to i32
; CHECK-NEXT: [[S0_1:%.*]] = getelementptr i32, i32* [[PCMP]], i64 1
; CHECK-NEXT: store i32 [[TMP2]], i32* [[S0_1]], align 4
; CHECK-NEXT: [[TMP3:%.*]] = icmp ne i64 [[N]], 0
; CHECK-NEXT: [[TMP4:%.*]] = sext i1 [[TMP3]] to i32
; CHECK-NEXT: [[S0_2:%.*]] = getelementptr i32, i32* [[PCMP]], i64 2
; CHECK-NEXT: store i32 [[TMP4]], i32* [[S0_2]], align 4
; CHECK-NEXT: [[TMP5:%.*]] = icmp ne i64 [[N]], 0
; CHECK-NEXT: [[TMP6:%.*]] = sext i1 [[TMP5]] to i32
; CHECK-NEXT: [[S0_3:%.*]] = getelementptr i32, i32* [[PCMP]], i64 3
; CHECK-NEXT: store i32 [[TMP6]], i32* [[S0_3]], align 4
; CHECK-NEXT: [[S0_4:%.*]] = getelementptr i32, i32* [[PCMP]], i64 4
; CHECK-NEXT: store i32 0, i32* [[S0_4]], align 4
; CHECK-NEXT: [[TMP7:%.*]] = icmp ne i64 [[N]], 0
; CHECK-NEXT: [[TMP8:%.*]] = sext i1 [[TMP7]] to i32
; CHECK-NEXT: [[S0_5:%.*]] = getelementptr i32, i32* [[PCMP]], i64 5
; CHECK-NEXT: store i32 [[TMP8]], i32* [[S0_5]], align 4
; CHECK-NEXT: [[TMP9:%.*]] = icmp ne i64 [[N]], 0
; CHECK-NEXT: [[TMP10:%.*]] = zext i1 [[TMP9]] to i32
; CHECK-NEXT: [[S5_0:%.*]] = getelementptr i32, i32* [[PCMP]], i64 6
; CHECK-NEXT: store i32 [[TMP10]], i32* [[S5_0]], align 4
; CHECK-NEXT: ret void
;

%p0 = getelementptr [8 x i8], [8 x i8]* @a01230123, i64 0, i64 0

%q0 = getelementptr [8 x i8], [8 x i8]* @b01230123, i64 0, i64 0
%q1 = getelementptr [8 x i8], [8 x i8]* @b01230123, i64 0, i64 1
%q2 = getelementptr [8 x i8], [8 x i8]* @b01230123, i64 0, i64 2
%q3 = getelementptr [8 x i8], [8 x i8]* @b01230123, i64 0, i64 3
%q4 = getelementptr [8 x i8], [8 x i8]* @b01230123, i64 0, i64 4
%q5 = getelementptr [8 x i8], [8 x i8]* @b01230123, i64 0, i64 5

; Fold strncmp(a, b, n) to 0.
%c0_0 = call i32 @strncmp(i8* %p0, i8* %q0, i64 %n)
%s0_0 = getelementptr i32, i32* %pcmp, i64 0
store i32 %c0_0, i32* %s0_0

; Fold strncmp(a, b + 1, n) to N != 0 ? -1 : 0.
%c0_1 = call i32 @strncmp(i8* %p0, i8* %q1, i64 %n)
%s0_1 = getelementptr i32, i32* %pcmp, i64 1
store i32 %c0_1, i32* %s0_1

; Fold strncmp(a, b + 2, n) to N != 0 ? -1 : 0.
%c0_2 = call i32 @strncmp(i8* %p0, i8* %q2, i64 %n)
%s0_2 = getelementptr i32, i32* %pcmp, i64 2
store i32 %c0_2, i32* %s0_2

; Fold strncmp(a, b + 3, n) to N != 0 ? -1 : 0.
%c0_3 = call i32 @strncmp(i8* %p0, i8* %q3, i64 %n)
%s0_3 = getelementptr i32, i32* %pcmp, i64 3
store i32 %c0_3, i32* %s0_3

; Fold strncmp(a, b + 4, n) to 0.
%c0_4 = call i32 @strncmp(i8* %p0, i8* %q4, i64 %n)
%s0_4 = getelementptr i32, i32* %pcmp, i64 4
store i32 %c0_4, i32* %s0_4

; Fold strncmp(a, b + 5, n) to N != 0 ? -1 : 0.
%c0_5 = call i32 @strncmp(i8* %p0, i8* %q5, i64 %n)
%s0_5 = getelementptr i32, i32* %pcmp, i64 5
store i32 %c0_5, i32* %s0_5

; Fold strncmp(b + 5, a, n) to N != 0 ? +1 : 0.
%c5_0 = call i32 @strncmp(i8* %q5, i8* %p0, i64 %n)
%s5_0 = getelementptr i32, i32* %pcmp, i64 6
store i32 %c5_0, i32* %s5_0

ret void
}

; Vefify that a strncmp() call involving a constant array with unknown
; contents is not folded.

define void @call_strncmp_a_ax_n(i32* %pcmp, i64 %n) {
; CHECK-LABEL: @call_strncmp_a_ax_n(
; CHECK-NEXT: [[C0_0:%.*]] = call i32 @strncmp(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @a01230123, i64 0, i64 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* @ax, i64 0, i64 0), i64 [[N:%.*]])
; CHECK-NEXT: store i32 [[C0_0]], i32* [[PCMP:%.*]], align 4
; CHECK-NEXT: ret void
;

%p0 = getelementptr [8 x i8], [8 x i8]* @a01230123, i64 0, i64 0
%q0 = getelementptr [8 x i8], [8 x i8]* @ax, i64 0, i64 0

; Do not fold strncmp(a, ax, n).
%c0_0 = call i32 @strncmp(i8* %p0, i8* %q0, i64 %n)
%s0_0 = getelementptr i32, i32* %pcmp, i64 0
store i32 %c0_0, i32* %s0_0

ret void
}


; Exercise strncmp(A, C, N) folding of arrays with the same leading bytes
; but a difference in the trailing byte.

define void @fold_strncmp_a_c_n(i32* %pcmp, i64 %n) {
; CHECK-LABEL: @fold_strncmp_a_c_n(
; CHECK-NEXT: [[TMP1:%.*]] = icmp ugt i64 [[N:%.*]], 7
; CHECK-NEXT: [[TMP2:%.*]] = sext i1 [[TMP1]] to i32
; CHECK-NEXT: store i32 [[TMP2]], i32* [[PCMP:%.*]], align 4
; CHECK-NEXT: [[TMP3:%.*]] = icmp ne i64 [[N]], 0
; CHECK-NEXT: [[TMP4:%.*]] = sext i1 [[TMP3]] to i32
; CHECK-NEXT: [[S0_1:%.*]] = getelementptr i32, i32* [[PCMP]], i64 1
; CHECK-NEXT: store i32 [[TMP4]], i32* [[S0_1]], align 4
; CHECK-NEXT: [[TMP5:%.*]] = icmp ne i64 [[N]], 0
; CHECK-NEXT: [[TMP6:%.*]] = sext i1 [[TMP5]] to i32
; CHECK-NEXT: [[S0_2:%.*]] = getelementptr i32, i32* [[PCMP]], i64 2
; CHECK-NEXT: store i32 [[TMP6]], i32* [[S0_2]], align 4
; CHECK-NEXT: [[TMP7:%.*]] = icmp ne i64 [[N]], 0
; CHECK-NEXT: [[TMP8:%.*]] = sext i1 [[TMP7]] to i32
; CHECK-NEXT: [[S0_3:%.*]] = getelementptr i32, i32* [[PCMP]], i64 3
; CHECK-NEXT: store i32 [[TMP8]], i32* [[S0_3]], align 4
; CHECK-NEXT: [[TMP9:%.*]] = icmp ugt i64 [[N]], 3
; CHECK-NEXT: [[TMP10:%.*]] = sext i1 [[TMP9]] to i32
; CHECK-NEXT: [[S0_4:%.*]] = getelementptr i32, i32* [[PCMP]], i64 4
; CHECK-NEXT: store i32 [[TMP10]], i32* [[S0_4]], align 4
; CHECK-NEXT: [[TMP11:%.*]] = icmp ugt i64 [[N]], 3
; CHECK-NEXT: [[TMP12:%.*]] = sext i1 [[TMP11]] to i32
; CHECK-NEXT: [[S0_5:%.*]] = getelementptr i32, i32* [[PCMP]], i64 5
; CHECK-NEXT: store i32 [[TMP12]], i32* [[S0_5]], align 4
; CHECK-NEXT: ret void
;

%p0 = getelementptr [8 x i8], [8 x i8]* @a01230123, i64 0, i64 0

%q0 = getelementptr [8 x i8], [8 x i8]* @c01230129, i64 0, i64 0
%q1 = getelementptr [8 x i8], [8 x i8]* @c01230129, i64 0, i64 1
%q2 = getelementptr [8 x i8], [8 x i8]* @c01230129, i64 0, i64 2
%q3 = getelementptr [8 x i8], [8 x i8]* @c01230129, i64 0, i64 3
%q4 = getelementptr [8 x i8], [8 x i8]* @c01230129, i64 0, i64 4
%q5 = getelementptr [8 x i8], [8 x i8]* @c01230129, i64 0, i64 5

; Fold strncmp(a, c, n) to N > 7 ? -1 : 0.
%c0_0 = call i32 @strncmp(i8* %p0, i8* %q0, i64 %n)
%s0_0 = getelementptr i32, i32* %pcmp, i64 0
store i32 %c0_0, i32* %s0_0

; Fold strncmp(a, c + 1, n) to N != 0 ? -1 : 0.
%c0_1 = call i32 @strncmp(i8* %p0, i8* %q1, i64 %n)
%s0_1 = getelementptr i32, i32* %pcmp, i64 1
store i32 %c0_1, i32* %s0_1

; Fold strncmp(a, c + 2, n) to N != 0 ? -1 : 0.
%c0_2 = call i32 @strncmp(i8* %p0, i8* %q2, i64 %n)
%s0_2 = getelementptr i32, i32* %pcmp, i64 2
store i32 %c0_2, i32* %s0_2

; Fold strncmp(a, c + 3, n) to N != 0 ? -1 : 0.
%c0_3 = call i32 @strncmp(i8* %p0, i8* %q3, i64 %n)
%s0_3 = getelementptr i32, i32* %pcmp, i64 3
store i32 %c0_3, i32* %s0_3

; Fold strncmp(a, c + 4, n) to N > 3 ? -1 : 0.
%c0_4 = call i32 @strncmp(i8* %p0, i8* %q4, i64 %n)
%s0_4 = getelementptr i32, i32* %pcmp, i64 4
store i32 %c0_4, i32* %s0_4

; Fold strncmp(a, c + 5, n) to N != 0 ? -1 : 0.
%c0_5 = call i32 @strncmp(i8* %p0, i8* %q4, i64 %n)
%s0_5 = getelementptr i32, i32* %pcmp, i64 5
store i32 %c0_5, i32* %s0_5

ret void
}


; Exercise strncmp(A, D, N) folding of arrays of different sizes and
; a difference in the leading byte.

define void @fold_strncmp_a_d_n(i32* %pcmp, i64 %n) {
; CHECK-LABEL: @fold_strncmp_a_d_n(
; CHECK-NEXT: [[TMP1:%.*]] = icmp ne i64 [[N:%.*]], 0
; CHECK-NEXT: [[TMP2:%.*]] = sext i1 [[TMP1]] to i32
; CHECK-NEXT: store i32 [[TMP2]], i32* [[PCMP:%.*]], align 4
; CHECK-NEXT: [[TMP3:%.*]] = icmp ne i64 [[N]], 0
; CHECK-NEXT: [[TMP4:%.*]] = sext i1 [[TMP3]] to i32
; CHECK-NEXT: [[S0_1:%.*]] = getelementptr i32, i32* [[PCMP]], i64 1
; CHECK-NEXT: store i32 [[TMP4]], i32* [[S0_1]], align 4
; CHECK-NEXT: [[TMP5:%.*]] = icmp ugt i64 [[N]], 3
; CHECK-NEXT: [[TMP6:%.*]] = zext i1 [[TMP5]] to i32
; CHECK-NEXT: [[S1_1:%.*]] = getelementptr i32, i32* [[PCMP]], i64 2
; CHECK-NEXT: store i32 [[TMP6]], i32* [[S1_1]], align 4
; CHECK-NEXT: [[TMP7:%.*]] = icmp ugt i64 [[N]], 2
; CHECK-NEXT: [[TMP8:%.*]] = zext i1 [[TMP7]] to i32
; CHECK-NEXT: [[S2_2:%.*]] = getelementptr i32, i32* [[PCMP]], i64 3
; CHECK-NEXT: store i32 [[TMP8]], i32* [[S2_2]], align 4
; CHECK-NEXT: [[TMP9:%.*]] = icmp ne i64 [[N]], 0
; CHECK-NEXT: [[TMP10:%.*]] = zext i1 [[TMP9]] to i32
; CHECK-NEXT: [[S4_4:%.*]] = getelementptr i32, i32* [[PCMP]], i64 4
; CHECK-NEXT: store i32 [[TMP10]], i32* [[S4_4]], align 4
; CHECK-NEXT: [[TMP11:%.*]] = icmp ne i64 [[N]], 0
; CHECK-NEXT: [[TMP12:%.*]] = sext i1 [[TMP11]] to i32
; CHECK-NEXT: [[S4_4_2:%.*]] = getelementptr i32, i32* [[PCMP]], i64 5
; CHECK-NEXT: store i32 [[TMP12]], i32* [[S4_4_2]], align 4
; CHECK-NEXT: [[S5_5:%.*]] = getelementptr i32, i32* [[PCMP]], i64 6
; CHECK-NEXT: store i32 0, i32* [[S5_5]], align 4
; CHECK-NEXT: [[S6_6:%.*]] = getelementptr i32, i32* [[PCMP]], i64 7
; CHECK-NEXT: store i32 0, i32* [[S6_6]], align 4
; CHECK-NEXT: ret void
;

%p0 = getelementptr [8 x i8], [8 x i8]* @a01230123, i64 0, i64 0
%p1 = getelementptr [8 x i8], [8 x i8]* @a01230123, i64 0, i64 1
%p2 = getelementptr [8 x i8], [8 x i8]* @a01230123, i64 0, i64 2
%p3 = getelementptr [8 x i8], [8 x i8]* @a01230123, i64 0, i64 3
%p4 = getelementptr [8 x i8], [8 x i8]* @a01230123, i64 0, i64 4
%p5 = getelementptr [8 x i8], [8 x i8]* @a01230123, i64 0, i64 5
%p6 = getelementptr [8 x i8], [8 x i8]* @a01230123, i64 0, i64 6

%q0 = getelementptr [7 x i8], [7 x i8]* @d9123_12, i64 0, i64 0
%q1 = getelementptr [7 x i8], [7 x i8]* @d9123_12, i64 0, i64 1
%q2 = getelementptr [7 x i8], [7 x i8]* @d9123_12, i64 0, i64 2
%q3 = getelementptr [7 x i8], [7 x i8]* @d9123_12, i64 0, i64 3
%q4 = getelementptr [7 x i8], [7 x i8]* @d9123_12, i64 0, i64 4
%q5 = getelementptr [7 x i8], [7 x i8]* @d9123_12, i64 0, i64 5
%q6 = getelementptr [7 x i8], [7 x i8]* @d9123_12, i64 0, i64 6

; Fold strncmp(a, d, n) to N != 0 ? -1 : 0.
%c0_0 = call i32 @strncmp(i8* %p0, i8* %q0, i64 %n)
%s0_0 = getelementptr i32, i32* %pcmp, i64 0
store i32 %c0_0, i32* %s0_0

; Fold strncmp(a, d + 1, n) to N != 0 ? -1 : 0.
%c0_1 = call i32 @strncmp(i8* %p0, i8* %q1, i64 %n)
%s0_1 = getelementptr i32, i32* %pcmp, i64 1
store i32 %c0_1, i32* %s0_1

; Fold strncmp(a + 1, d + 1, n) N > 3 ? +1 : 0.
%c1_1 = call i32 @strncmp(i8* %p1, i8* %q1, i64 %n)
%s1_1 = getelementptr i32, i32* %pcmp, i64 2
store i32 %c1_1, i32* %s1_1

; Fold strncmp(a + 2, d + 2, n) N > 2 ? +1 : 0.
%c2_2 = call i32 @strncmp(i8* %p2, i8* %q2, i64 %n)
%s2_2 = getelementptr i32, i32* %pcmp, i64 3
store i32 %c2_2, i32* %s2_2

; Fold strncmp(a + 3, d + 3, n) N > 1 ? +1 : 0.
%c3_3 = call i32 @strncmp(i8* %p3, i8* %q3, i64 %n)
%s3_3 = getelementptr i32, i32* %pcmp, i64 4
store i32 %c3_3, i32* %s3_3

; Fold strncmp(a + 4, d + 4, n) N != 0 ? +1 : 0.
%c4_4 = call i32 @strncmp(i8* %p4, i8* %q4, i64 %n)
%s4_4 = getelementptr i32, i32* %pcmp, i64 4
store i32 %c4_4, i32* %s4_4

; Fold strncmp(d + 4, a + 4, n) N != 0 ? -1 : 0 (same as above but
; with the array arguments reversed).
%c4_4_2 = call i32 @strncmp(i8* %q4, i8* %p4, i64 %n)
%s4_4_2 = getelementptr i32, i32* %pcmp, i64 5
store i32 %c4_4_2, i32* %s4_4_2

; Fold strncmp(a + 5, d + 5, n) to 0.
%c5_5 = call i32 @strncmp(i8* %p5, i8* %q5, i64 %n)
%s5_5 = getelementptr i32, i32* %pcmp, i64 6
store i32 %c5_5, i32* %s5_5

; Fold strncmp(a + 6, d + 6, n) to 0.
%c6_6 = call i32 @strncmp(i8* %p6, i8* %q6, i64 %n)
%s6_6 = getelementptr i32, i32* %pcmp, i64 7
store i32 %c6_6, i32* %s6_6

ret void
}


; Exercise strncmp(A, D, N) folding of arrays with the same bytes and
; a nonzero size.

define void @fold_strncmp_a_d_nz(i32* %pcmp, i64 %n) {
; CHECK-LABEL: @fold_strncmp_a_d_nz(
; CHECK-NEXT: store i32 -1, i32* [[PCMP:%.*]], align 4
; CHECK-NEXT: ret void
;

%p0 = getelementptr [8 x i8], [8 x i8]* @a01230123, i64 0, i64 0
%q0 = getelementptr [7 x i8], [7 x i8]* @d9123_12, i64 0, i64 0
%nz = or i64 %n, 1

%c0_0 = call i32 @strncmp(i8* %p0, i8* %q0, i64 %nz)
%s0_0 = getelementptr i32, i32* %pcmp, i64 0
store i32 %c0_0, i32* %s0_0

ret void
}


; Exercise strncmp(D, E, N) folding of equal strings but unequal arrays.

define void @fold_strncmp_d_e_n(i32* %pcmp, i64 %n) {
; CHECK-LABEL: @fold_strncmp_d_e_n(
; CHECK-NEXT: store i32 0, i32* [[PCMP:%.*]], align 4
; CHECK-NEXT: [[TMP1:%.*]] = icmp ne i64 [[N:%.*]], 0
; CHECK-NEXT: [[TMP2:%.*]] = zext i1 [[TMP1]] to i32
; CHECK-NEXT: [[S0_1:%.*]] = getelementptr i32, i32* [[PCMP]], i64 1
; CHECK-NEXT: store i32 [[TMP2]], i32* [[S0_1]], align 4
; CHECK-NEXT: [[TMP3:%.*]] = icmp ne i64 [[N]], 0
; CHECK-NEXT: [[TMP4:%.*]] = sext i1 [[TMP3]] to i32
; CHECK-NEXT: [[S1_0:%.*]] = getelementptr i32, i32* [[PCMP]], i64 2
; CHECK-NEXT: store i32 [[TMP4]], i32* [[S1_0]], align 4
; CHECK-NEXT: [[S1_1:%.*]] = getelementptr i32, i32* [[PCMP]], i64 3
; CHECK-NEXT: store i32 0, i32* [[S1_1]], align 4
; CHECK-NEXT: ret void
;

%p0 = getelementptr [7 x i8], [7 x i8]* @d9123_12, i64 0, i64 0
%p1 = getelementptr [7 x i8], [7 x i8]* @d9123_12, i64 0, i64 1

%q0 = getelementptr [7 x i8], [7 x i8]* @e9123_34, i64 0, i64 0
%q1 = getelementptr [7 x i8], [7 x i8]* @e9123_34, i64 0, i64 1

; Fold to 0.
%c0_0 = call i32 @strncmp(i8* %p0, i8* %q0, i64 %n)
%s0_0 = getelementptr i32, i32* %pcmp, i64 0
store i32 %c0_0, i32* %s0_0

; Fold to N ? +1 : 0.
%c0_1 = call i32 @strncmp(i8* %p0, i8* %q1, i64 %n)
%s0_1 = getelementptr i32, i32* %pcmp, i64 1
store i32 %c0_1, i32* %s0_1

; Fold to N ? -1 : 0.
%c1_0 = call i32 @strncmp(i8* %p1, i8* %q0, i64 %n)
%s1_0 = getelementptr i32, i32* %pcmp, i64 2
store i32 %c1_0, i32* %s1_0

; Fold to 0.
%c1_1 = call i32 @strncmp(i8* %p1, i8* %q1, i64 %n)
%s1_1 = getelementptr i32, i32* %pcmp, i64 3
store i32 %c1_1, i32* %s1_1

ret void
}
106 changes: 106 additions & 0 deletions llvm/test/Transforms/InstCombine/strncmp-6.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -passes=instcombine -S | FileCheck %s
;
; Exercise folding of strncmp calls with constant arrays including both
; negative and positive characters and both constant and nonconstant sizes.

declare i32 @strncmp(i8*, i8*, i64)

@a = constant [7 x i8] c"abcdef\7f"
@b = constant [7 x i8] c"abcdef\80"


; Exercise strncmp(A + C, B + C, 2) folding of small arrays that differ in
; a character with the opposite sign and a constant size.

define void @fold_strncmp_cst_cst(i32* %pcmp) {
; CHECK-LABEL: @fold_strncmp_cst_cst(
; CHECK-NEXT: store i32 -1, i32* [[PCMP:%.*]], align 4
; CHECK-NEXT: [[SB5_A5:%.*]] = getelementptr i32, i32* [[PCMP]], i64 1
; CHECK-NEXT: store i32 1, i32* [[SB5_A5]], align 4
; CHECK-NEXT: [[SA6_B6:%.*]] = getelementptr i32, i32* [[PCMP]], i64 2
; CHECK-NEXT: store i32 -1, i32* [[SA6_B6]], align 4
; CHECK-NEXT: [[SB6_A6:%.*]] = getelementptr i32, i32* [[PCMP]], i64 3
; CHECK-NEXT: store i32 1, i32* [[SB6_A6]], align 4
; CHECK-NEXT: ret void
;
%p5 = getelementptr [7 x i8], [7 x i8]* @a, i64 0, i64 5
%p6 = getelementptr [7 x i8], [7 x i8]* @a, i64 0, i64 6

%q5 = getelementptr [7 x i8], [7 x i8]* @b, i64 0, i64 5
%q6 = getelementptr [7 x i8], [7 x i8]* @b, i64 0, i64 6

; Fold strncmp(a + 5, b + 5, 2) to -1.
%ca5_b5 = call i32 @strncmp(i8* %p5, i8* %q5, i64 2)
%sa5_b5 = getelementptr i32, i32* %pcmp, i64 0
store i32 %ca5_b5, i32* %sa5_b5

; Fold strncmp(b + 5, a + 5, 2) to +1.
%cb5_a5 = call i32 @strncmp(i8* %q5, i8* %p5, i64 2)
%sb5_a5 = getelementptr i32, i32* %pcmp, i64 1
store i32 %cb5_a5, i32* %sb5_a5

; Fold strncmp(a + 6, b + 6, 1) to -1.
%ca6_b6 = call i32 @strncmp(i8* %p6, i8* %q6, i64 1)
%sa6_b6 = getelementptr i32, i32* %pcmp, i64 2
store i32 %ca6_b6, i32* %sa6_b6

; Fold strncmp(b + 6, a + 6, 1) to +1.
%cb6_a6 = call i32 @strncmp(i8* %q6, i8* %p6, i64 1)
%sb6_a6 = getelementptr i32, i32* %pcmp, i64 3
store i32 %cb6_a6, i32* %sb6_a6

ret void
}


; Exercise strncmp(A, B, N) folding of arrays that differ in a character
; with the opposite sign and a variable size

define void @fold_strncmp_cst_var(i32* %pcmp, i64 %n) {
; CHECK-LABEL: @fold_strncmp_cst_var(
; CHECK-NEXT: [[TMP1:%.*]] = icmp ugt i64 [[N:%.*]], 6
; CHECK-NEXT: [[TMP2:%.*]] = sext i1 [[TMP1]] to i32
; CHECK-NEXT: store i32 [[TMP2]], i32* [[PCMP:%.*]], align 4
; CHECK-NEXT: [[TMP3:%.*]] = icmp ugt i64 [[N]], 6
; CHECK-NEXT: [[TMP4:%.*]] = zext i1 [[TMP3]] to i32
; CHECK-NEXT: [[SB0_A0:%.*]] = getelementptr i32, i32* [[PCMP]], i64 1
; CHECK-NEXT: store i32 [[TMP4]], i32* [[SB0_A0]], align 4
; CHECK-NEXT: [[TMP5:%.*]] = icmp ne i64 [[N]], 0
; CHECK-NEXT: [[TMP6:%.*]] = sext i1 [[TMP5]] to i32
; CHECK-NEXT: [[SA6_B6:%.*]] = getelementptr i32, i32* [[PCMP]], i64 2
; CHECK-NEXT: store i32 [[TMP6]], i32* [[SA6_B6]], align 4
; CHECK-NEXT: [[TMP7:%.*]] = icmp ne i64 [[N]], 0
; CHECK-NEXT: [[TMP8:%.*]] = zext i1 [[TMP7]] to i32
; CHECK-NEXT: [[SB6_A6:%.*]] = getelementptr i32, i32* [[PCMP]], i64 3
; CHECK-NEXT: store i32 [[TMP8]], i32* [[SB6_A6]], align 4
; CHECK-NEXT: ret void
;
%p0 = getelementptr [7 x i8], [7 x i8]* @a, i64 0, i64 0
%p6 = getelementptr [7 x i8], [7 x i8]* @a, i64 0, i64 6

%q0 = getelementptr [7 x i8], [7 x i8]* @b, i64 0, i64 0
%q6 = getelementptr [7 x i8], [7 x i8]* @b, i64 0, i64 6

; Fold strncmp(a, b, n) to -1.
%ca0_b0 = call i32 @strncmp(i8* %p0, i8* %q0, i64 %n)
%sa0_b0 = getelementptr i32, i32* %pcmp, i64 0
store i32 %ca0_b0, i32* %sa0_b0

; Fold strncmp(b, a, n) to +1.
%cb0_a0 = call i32 @strncmp(i8* %q0, i8* %p0, i64 %n)
%sb0_a0 = getelementptr i32, i32* %pcmp, i64 1
store i32 %cb0_a0, i32* %sb0_a0

; Fold strncmp(a + 6, b + 6, n) to -1.
%ca6_b6 = call i32 @strncmp(i8* %p6, i8* %q6, i64 %n)
%sa6_b6 = getelementptr i32, i32* %pcmp, i64 2
store i32 %ca6_b6, i32* %sa6_b6

; Fold strncmp(b + 6, a + 6, n) to +1.
%cb6_a6 = call i32 @strncmp(i8* %q6, i8* %p6, i64 %n)
%sb6_a6 = getelementptr i32, i32* %pcmp, i64 3
store i32 %cb6_a6, i32* %sb6_a6

ret void
}
29 changes: 22 additions & 7 deletions llvm/test/Transforms/InstCombine/strnlen-1.ll
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,9 @@ define i64 @fold_strnlen_ax_0() {
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)
Expand Down Expand Up @@ -151,13 +151,28 @@ define i64 @fold_strnlen_s5_3_p5_5() {
}


; 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
}


; Fold even the invalid strnlen(s5_3 + 6, 4) call where the bound exceeds
; the number of characters in the array. This is arguably safer than
; making the library call (although the low bound makes it unlikely that
; the call would misbehave).

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
}
19 changes: 11 additions & 8 deletions llvm/test/Transforms/InstCombine/wcslen-1.ll
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ define i1 @test_simplify5() {

define i1 @test_simplify6(i32* %str_p) {
; CHECK-LABEL: @test_simplify6(
; CHECK-NEXT: [[STRLENFIRST:%.*]] = load i32, i32* [[STR_P:%.*]], align 4
; CHECK-NEXT: [[EQ_NULL:%.*]] = icmp eq i32 [[STRLENFIRST]], 0
; CHECK-NEXT: [[CHAR0:%.*]] = load i32, i32* [[STR_P:%.*]], align 4
; CHECK-NEXT: [[EQ_NULL:%.*]] = icmp eq i32 [[CHAR0]], 0
; CHECK-NEXT: ret i1 [[EQ_NULL]]
;
%str_l = call i64 @wcslen(i32* %str_p)
Expand All @@ -90,8 +90,8 @@ define i1 @test_simplify7() {

define i1 @test_simplify8(i32* %str_p) {
; CHECK-LABEL: @test_simplify8(
; CHECK-NEXT: [[STRLENFIRST:%.*]] = load i32, i32* [[STR_P:%.*]], align 4
; CHECK-NEXT: [[NE_NULL:%.*]] = icmp ne i32 [[STRLENFIRST]], 0
; CHECK-NEXT: [[CHAR0:%.*]] = load i32, i32* [[STR_P:%.*]], align 4
; CHECK-NEXT: [[NE_NULL:%.*]] = icmp ne i32 [[CHAR0]], 0
; CHECK-NEXT: ret i1 [[NE_NULL]]
;
%str_l = call i64 @wcslen(i32* %str_p)
Expand Down Expand Up @@ -210,10 +210,13 @@ define i64 @test_no_simplify3_no_null_opt(i32 %x) #0 {

@str16 = constant [1 x i16] [i16 0]

define i64 @test_no_simplify4() {
; CHECK-LABEL: @test_no_simplify4(
; CHECK-NEXT: [[L:%.*]] = call i64 @wcslen(i32* bitcast ([1 x i16]* @str16 to i32*))
; CHECK-NEXT: ret i64 [[L]]
; Fold the invalid call to zero. This is safer than letting the undefined
; library call take place even though it prevents sanitizers from detecting
; it.

define i64 @test_simplify12() {
; CHECK-LABEL: @test_simplify12(
; CHECK-NEXT: ret i64 0
;
%l = call i64 @wcslen(i32* bitcast ([1 x i16]* @str16 to i32*))
ret i64 %l
Expand Down