[InstCombine] Add additional tests for strlen/strnlen (NFC)
authorMartin Sebor <msebor@redhat.com>
Fri, 1 Apr 2022 14:58:38 +0000 (16:58 +0200)
committerNikita Popov <npopov@redhat.com>
Fri, 1 Apr 2022 14:58:38 +0000 (16:58 +0200)
Taken from D122686.

llvm/test/Transforms/InstCombine/strlen-4.ll [new file with mode: 0644]
llvm/test/Transforms/InstCombine/strnlen-1.ll [new file with mode: 0644]
llvm/test/Transforms/InstCombine/strnlen-2.ll [new file with mode: 0644]
llvm/test/Transforms/InstCombine/strnlen-3.ll [new file with mode: 0644]
llvm/test/Transforms/InstCombine/strnlen-4.ll [new file with mode: 0644]
llvm/test/Transforms/InstCombine/strnlen-5.ll [new file with mode: 0644]
llvm/test/Transforms/InstCombine/strnlen-6.ll [new file with mode: 0644]
llvm/test/Transforms/InstCombine/wcslen-5.ll [new file with mode: 0644]

diff --git a/llvm/test/Transforms/InstCombine/strlen-4.ll b/llvm/test/Transforms/InstCombine/strlen-4.ll
new file mode 100644 (file)
index 0000000..7b8ab18
--- /dev/null
@@ -0,0 +1,195 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; Verify that strlen calls with conditional expressions involving constant
+; string arguments with nonconstant offsets are folded as expected.
+;
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+declare i64 @strlen(i8* nocapture)
+
+@sx = external global [0 x i8]
+@s3 = constant [4 x i8] c"123\00", align 1
+@s5 = constant [6 x i8] c"12345\00", align 1
+@s7 = constant [8 x i8] c"1234567\00", align 1
+
+@s5_3 = constant [10 x i8] c"12345\00123\00", align 1
+
+
+; Fold strlen (x ? s3 + i: s5) to x ? 3 - i : 5.
+
+define i64 @fold_strlen_s3_pi_s5(i1 %0, i64 %1) {
+; CHECK-LABEL: @fold_strlen_s3_pi_s5(
+; CHECK-NEXT:    [[PS3_PI:%.*]] = getelementptr inbounds [4 x i8], [4 x i8]* @s3, i64 0, i64 [[TMP1:%.*]]
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[TMP0:%.*]], i8* [[PS3_PI]], i8* getelementptr inbounds ([6 x i8], [6 x i8]* @s5, i64 0, i64 0)
+; CHECK-NEXT:    [[LEN:%.*]] = tail call i64 @strlen(i8* noundef nonnull dereferenceable(1) [[SEL]])
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+
+  %ps3_pi = getelementptr inbounds [4 x i8], [4 x i8]* @s3, i64 0, i64 %1
+  %ps5 = getelementptr [6 x i8], [6 x i8]* @s5, i64 0, i64 0
+  %sel = select i1 %0, i8* %ps3_pi, i8* %ps5
+  %len = tail call i64 @strlen(i8* %sel)
+  ret i64 %len
+}
+
+
+; More complex expressions like the one below are not handled yet.
+; Fold: strlen (x ? s3 + i + 1 : s5 + j + 2) to x ? 2 - i : 3 - j.
+
+define i64 @fold_strlen_s3_pi_p1_s5(i1 %0, i64 %1) {
+; XFAIL-CHECK-LABEL: @fold_strlen_s3_pi_p1_s5(
+; XFAIL-CHECK-NEXT:    [[DIF_I:%.*]] = sub i64 2, %1
+; XFAIL-CHECK-NEXT:    [[SEL:%.*]] = select i1 %0, i64 [[DIF_I]], i64 5
+; XFAIL-CHECK-NEXT:    ret i64 [[SEL]]
+; CHECK-LABEL: @fold_strlen_s3_pi_p1_s5(
+; CHECK-NEXT:    [[PS3_PI:%.*]] = getelementptr inbounds [4 x i8], [4 x i8]* @s3, i64 0, i64 [[TMP1:%.*]]
+; CHECK-NEXT:    [[PS3_PI_P1:%.*]] = getelementptr i8, i8* [[PS3_PI]], i64 1
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[TMP0:%.*]], i8* [[PS3_PI_P1]], i8* getelementptr inbounds ([6 x i8], [6 x i8]* @s5, i64 0, i64 0)
+; CHECK-NEXT:    [[LEN:%.*]] = tail call i64 @strlen(i8* noundef nonnull dereferenceable(1) [[SEL]])
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+
+  %ps3_pi = getelementptr inbounds [4 x i8], [4 x i8]* @s3, i64 0, i64 %1
+  %ps3_pi_p1 = getelementptr i8, i8* %ps3_pi, i64 1
+  %ps5 = getelementptr [6 x i8], [6 x i8]* @s5, i64 0, i64 0
+  %sel = select i1 %0, i8* %ps3_pi_p1, i8* %ps5
+  %len = tail call i64 @strlen(i8* %sel)
+  ret i64 %len
+}
+
+
+; Avoid folding calls with conditional expressions involving constant
+; string arguments with embedded nuls such as:
+;   strlen (x ? s5_3 + i : s5).
+
+define i64 @call_strlen_s5_3_pi_s5(i1 %0, i64 %1) {
+; CHECK-LABEL: @call_strlen_s5_3_pi_s5(
+; CHECK-NEXT:    [[PS5_3_PI:%.*]] = getelementptr inbounds [10 x i8], [10 x i8]* @s5_3, i64 0, i64 [[TMP1:%.*]]
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[TMP0:%.*]], i8* [[PS5_3_PI]], i8* getelementptr inbounds ([6 x i8], [6 x i8]* @s5, i64 0, i64 0)
+; CHECK-NEXT:    [[LEN:%.*]] = tail call i64 @strlen(i8* noundef nonnull dereferenceable(1) [[SEL]])
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+
+  %ps5_3_pi = getelementptr inbounds [10 x i8], [10 x i8]* @s5_3, i64 0, i64 %1
+  %ps5 = getelementptr [6 x i8], [6 x i8]* @s5, i64 0, i64 0
+  %sel = select i1 %0, i8* %ps5_3_pi, i8* %ps5
+  %len = tail call i64 @strlen(i8* %sel)
+  ret i64 %len
+}
+
+
+; But do fold strlen (x ? s5_3 : s5 + j) to x ? 5 : 5 - j.
+
+define i64 @call_strlen_s5_3_s5_pj(i1 %0, i64 %1) {
+; CHECK-LABEL: @call_strlen_s5_3_s5_pj(
+; CHECK-NEXT:    [[PS5:%.*]] = getelementptr inbounds [6 x i8], [6 x i8]* @s5, i64 0, i64 [[TMP1:%.*]]
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[TMP0:%.*]], i8* getelementptr inbounds ([10 x i8], [10 x i8]* @s5_3, i64 0, i64 0), i8* [[PS5]]
+; CHECK-NEXT:    [[LEN:%.*]] = tail call i64 @strlen(i8* noundef nonnull dereferenceable(1) [[SEL]])
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+
+  %ps5_3_pi = getelementptr [10 x i8], [10 x i8]* @s5_3, i64 0, i64 0
+  %ps5 = getelementptr inbounds [6 x i8], [6 x i8]* @s5, i64 0, i64 %1
+  %sel = select i1 %0, i8* %ps5_3_pi, i8* %ps5
+  %len = tail call i64 @strlen(i8* %sel)
+  ret i64 %len
+}
+
+
+; Fold strlen (x ? s3: s5 + j) to x ? 3 : 5 - j.
+
+define i64 @fold_strlen_s3_s5_pj(i1 %0, i64 %1) {
+; CHECK-LABEL: @fold_strlen_s3_s5_pj(
+; CHECK-NEXT:    [[PS5_PJ:%.*]] = getelementptr inbounds [6 x i8], [6 x i8]* @s5, i64 0, i64 [[TMP1:%.*]]
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[TMP0:%.*]], i8* getelementptr inbounds ([4 x i8], [4 x i8]* @s3, i64 0, i64 0), i8* [[PS5_PJ]]
+; CHECK-NEXT:    [[LEN:%.*]] = tail call i64 @strlen(i8* noundef nonnull dereferenceable(1) [[SEL]])
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+
+  %ps3 = getelementptr [4 x i8], [4 x i8]* @s3, i64 0, i64 0
+  %ps5_pj = getelementptr inbounds [6 x i8], [6 x i8]* @s5, i64 0, i64 %1
+  %sel = select i1 %0, i8* %ps3, i8* %ps5_pj
+  %len = tail call i64 @strlen(i8* %sel)
+  ret i64 %len
+}
+
+
+; Same as above, avoid folding calls with conditional expressions involving
+; constant string arguments with embedded nuls such as:
+;   strlen (x ? s3 : s5_3 + j).
+
+define i64 @call_strlen_s3_s5_3_pj(i1 %0, i64 %1) {
+; CHECK-LABEL: @call_strlen_s3_s5_3_pj(
+; CHECK-NEXT:    [[PS5_3_PJ:%.*]] = getelementptr inbounds [10 x i8], [10 x i8]* @s5_3, i64 0, i64 [[TMP1:%.*]]
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[TMP0:%.*]], i8* getelementptr inbounds ([4 x i8], [4 x i8]* @s3, i64 0, i64 0), i8* [[PS5_3_PJ]]
+; CHECK-NEXT:    [[LEN:%.*]] = tail call i64 @strlen(i8* noundef nonnull dereferenceable(1) [[SEL]])
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+
+  %ps3 = getelementptr [4 x i8], [4 x i8]* @s3, i64 0, i64 0
+  %ps5_3_pj = getelementptr inbounds [10 x i8], [10 x i8]* @s5_3, i64 0, i64 %1
+  %sel = select i1 %0, i8* %ps3, i8* %ps5_3_pj
+  %len = tail call i64 @strlen(i8* %sel)
+  ret i64 %len
+}
+
+
+; Fold strlen (x ? s3 + i: s5 + j) to x ? 3 - i : 5 - j.
+
+define i64 @fold_strlen_s3_pi_s5_pj(i1 %0, i64 %1, i64 %2) {
+; CHECK-LABEL: @fold_strlen_s3_pi_s5_pj(
+; CHECK-NEXT:    [[PS3_PI:%.*]] = getelementptr inbounds [4 x i8], [4 x i8]* @s3, i64 0, i64 [[TMP1:%.*]]
+; CHECK-NEXT:    [[PS5_PJ:%.*]] = getelementptr inbounds [6 x i8], [6 x i8]* @s5, i64 0, i64 [[TMP2:%.*]]
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[TMP0:%.*]], i8* [[PS3_PI]], i8* [[PS5_PJ]]
+; CHECK-NEXT:    [[LEN:%.*]] = tail call i64 @strlen(i8* noundef nonnull [[SEL]])
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+
+  %ps3_pi = getelementptr inbounds [4 x i8], [4 x i8]* @s3, i64 0, i64 %1
+  %ps5_pj = getelementptr inbounds [6 x i8], [6 x i8]* @s5, i64 0, i64 %2
+  %sel = select i1 %0, i8* %ps3_pi, i8* %ps5_pj
+  %len = tail call i64 @strlen(i8* %sel)
+  ret i64 %len
+}
+
+
+; Fold strlen(E) with E being two conditional expressions:
+;   strlen (x == 3 ? s3 : x == 5 ? s5 : s7) to x == 3 ? 3 : x == 5 ? 5 : 7.
+
+define i64 @fold_strlen_s3_s5_s7(i32 %0) {
+; CHECK-LABEL: @fold_strlen_s3_s5_s7(
+; CHECK-NEXT:    [[TMP2:%.*]] = icmp eq i32 [[TMP0:%.*]], 3
+; CHECK-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[TMP0]], 5
+; CHECK-NEXT:    [[TMP4:%.*]] = select i1 [[TMP3]], i8* getelementptr inbounds ([6 x i8], [6 x i8]* @s5, i64 0, i64 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* @s7, i64 0, i64 0)
+; CHECK-NEXT:    [[TMP5:%.*]] = select i1 [[TMP2]], i8* getelementptr inbounds ([4 x i8], [4 x i8]* @s3, i64 0, i64 0), i8* [[TMP4]]
+; CHECK-NEXT:    [[TMP6:%.*]] = tail call i64 @strlen(i8* noundef nonnull dereferenceable(1) [[TMP5]])
+; CHECK-NEXT:    ret i64 [[TMP6]]
+;
+
+  %2 = icmp eq i32 %0, 3
+  %3 = icmp eq i32 %0, 5
+  %4 = select i1 %3, i8* getelementptr ([6 x i8], [6 x i8]* @s5, i64 0, i64 0), i8* getelementptr ([8 x i8], [8 x i8]* @s7, i64 0, i64 0)
+  %5 = select i1 %2, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @s3, i64 0, i64 0), i8* %4
+  %6 = tail call i64 @strlen(i8* %5)
+  ret i64 %6
+}
+
+
+; Do not fold strlen (x == 3 ? sx : x == 5 ? s5 : s7).
+
+define i64 @call_strlen_sx_s5_s7(i32 %0) {
+; CHECK-LABEL: @call_strlen_sx_s5_s7(
+; CHECK-NEXT:    [[TMP2:%.*]] = icmp eq i32 [[TMP0:%.*]], 3
+; CHECK-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[TMP0]], 5
+; CHECK-NEXT:    [[TMP4:%.*]] = select i1 [[TMP3]], i8* getelementptr inbounds ([6 x i8], [6 x i8]* @s5, i64 0, i64 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* @s7, i64 0, i64 0)
+; CHECK-NEXT:    [[TMP5:%.*]] = select i1 [[TMP2]], i8* getelementptr inbounds ([0 x i8], [0 x i8]* @sx, i64 0, i64 0), i8* [[TMP4]]
+; CHECK-NEXT:    [[TMP6:%.*]] = tail call i64 @strlen(i8* noundef nonnull dereferenceable(1) [[TMP5]])
+; CHECK-NEXT:    ret i64 [[TMP6]]
+;
+
+  %2 = icmp eq i32 %0, 3
+  %3 = icmp eq i32 %0, 5
+  %4 = select i1 %3, i8* getelementptr ([6 x i8], [6 x i8]* @s5, i64 0, i64 0), i8* getelementptr ([8 x i8], [8 x i8]* @s7, i64 0, i64 0)
+  %5 = select i1 %2, i8* getelementptr inbounds ([0 x i8], [0 x i8]* @sx, i64 0, i64 0), i8* %4
+  %6 = tail call i64 @strlen(i8* %5)
+  ret i64 %6
+}
diff --git a/llvm/test/Transforms/InstCombine/strnlen-1.ll b/llvm/test/Transforms/InstCombine/strnlen-1.ll
new file mode 100644 (file)
index 0000000..0fb94db
--- /dev/null
@@ -0,0 +1,101 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; Verify that strnlen calls with constant string arguments and offsets
+; and constant bounds are folded correctly.
+;
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+declare i64 @strnlen(i8*, i64)
+
+@s5 = constant [6 x i8] c"12345\00"
+@s5_3 = constant [9 x i8] c"12345\00xyz"
+
+
+; Fold strnlen(s5, 0) to 0.
+
+define i64 @fold_strnlen_s5_0() {
+; CHECK-LABEL: @fold_strnlen_s5_0(
+; CHECK-NEXT:    [[LEN:%.*]] = call i64 @strnlen(i8* getelementptr inbounds ([6 x i8], [6 x i8]* @s5, i64 0, i64 0), i64 0)
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+  %ptr = getelementptr [6 x i8], [6 x i8]* @s5, i32 0, i32 0
+  %len = call i64 @strnlen(i8* %ptr, i64 0)
+  ret i64 %len
+}
+
+
+; Fold strnlen(s5, 4) to 4.
+
+define i64 @fold_strnlen_s5_4() {
+; CHECK-LABEL: @fold_strnlen_s5_4(
+; CHECK-NEXT:    [[LEN:%.*]] = call i64 @strnlen(i8* getelementptr inbounds ([6 x i8], [6 x i8]* @s5, i64 0, i64 0), i64 4)
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+  %ptr = getelementptr [6 x i8], [6 x i8]* @s5, i32 0, i32 0
+  %len = call i64 @strnlen(i8* %ptr, i64 4)
+  ret i64 %len
+}
+
+
+; Fold strnlen(s5, 5) to 5.
+
+define i64 @fold_strnlen_s5_5() {
+; CHECK-LABEL: @fold_strnlen_s5_5(
+; CHECK-NEXT:    [[LEN:%.*]] = call i64 @strnlen(i8* getelementptr inbounds ([6 x i8], [6 x i8]* @s5, i64 0, i64 0), i64 5)
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+  %ptr = getelementptr [6 x i8], [6 x i8]* @s5, i32 0, i32 0
+  %len = call i64 @strnlen(i8* %ptr, i64 5)
+  ret i64 %len
+}
+
+
+; Fold strnlen(s5, (size_t)-1) to 5.
+
+define i64 @fold_strnlen_s5_m1() {
+; CHECK-LABEL: @fold_strnlen_s5_m1(
+; CHECK-NEXT:    [[LEN:%.*]] = call i64 @strnlen(i8* getelementptr inbounds ([6 x i8], [6 x i8]* @s5, i64 0, i64 0), i64 -1)
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+  %ptr = getelementptr [6 x i8], [6 x i8]* @s5, i32 0, i32 0
+  %len = call i64 @strnlen(i8* %ptr, i64 -1)
+  ret i64 %len
+}
+
+
+; Fold strnlen(s5_3 + 4, 5) to 1.
+
+define i64 @fold_strnlen_s5_3_p4_5() {
+; CHECK-LABEL: @fold_strnlen_s5_3_p4_5(
+; CHECK-NEXT:    [[LEN:%.*]] = call i64 @strnlen(i8* getelementptr inbounds ([9 x i8], [9 x i8]* @s5_3, i64 0, i64 4), i64 5)
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+  %ptr = getelementptr [9 x i8], [9 x i8]* @s5_3, i32 0, i32 4
+  %len = call i64 @strnlen(i8* %ptr, i64 5)
+  ret i64 %len
+}
+
+
+; Fold strnlen(s5_3 + 5, 5) to 1.
+
+define i64 @fold_strnlen_s5_3_p5_5() {
+; CHECK-LABEL: @fold_strnlen_s5_3_p5_5(
+; CHECK-NEXT:    [[LEN:%.*]] = call i64 @strnlen(i8* getelementptr inbounds ([9 x i8], [9 x i8]* @s5_3, i64 0, i64 5), i64 5)
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+  %ptr = getelementptr [9 x i8], [9 x i8]* @s5_3, i32 0, i32 5
+  %len = call i64 @strnlen(i8* %ptr, i64 5)
+  ret i64 %len
+}
+
+
+; Fold strnlen(s5_3 + 6, 5) to 3.
+
+define i64 @fold_strnlen_s5_3_p6_5() {
+; CHECK-LABEL: @fold_strnlen_s5_3_p6_5(
+; CHECK-NEXT:    [[LEN:%.*]] = call i64 @strnlen(i8* getelementptr inbounds ([9 x i8], [9 x i8]* @s5_3, i64 0, i64 6), i64 5)
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+  %ptr = getelementptr [9 x i8], [9 x i8]* @s5_3, i32 0, i32 6
+  %len = call i64 @strnlen(i8* %ptr, i64 5)
+  ret i64 %len
+}
diff --git a/llvm/test/Transforms/InstCombine/strnlen-2.ll b/llvm/test/Transforms/InstCombine/strnlen-2.ll
new file mode 100644 (file)
index 0000000..4af460b
--- /dev/null
@@ -0,0 +1,172 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; Verify that strnlen calls with conditional expressions involving constant
+; string arguments and constant bounds are folded correctly.
+;
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+declare i64 @strnlen(i8*, i64)
+
+@s3 = constant [4 x i8] c"123\00"
+@s5 = constant [6 x i8] c"12345\00"
+@s5_3 = constant [10 x i8] c"12345\00678\00"
+@s6 = constant [7 x i8] c"123456\00"
+@s7 = constant [8 x i8] c"1234567\00"
+
+
+; Fold strnlen (%0 ? s3 : s5, 0) to 0.
+
+define i64 @fold_strnlen_s3_s5_0(i1 %0) {
+; CHECK-LABEL: @fold_strnlen_s3_s5_0(
+; CHECK-NEXT:    [[PTR:%.*]] = select i1 [[TMP0:%.*]], i8* getelementptr inbounds ([4 x i8], [4 x i8]* @s3, i64 0, i64 0), i8* getelementptr inbounds ([7 x i8], [7 x i8]* @s6, i64 0, i64 0)
+; CHECK-NEXT:    [[LEN:%.*]] = call i64 @strnlen(i8* [[PTR]], i64 0)
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+  %ptr = select i1 %0, i8* getelementptr ([4 x i8], [4 x i8]* @s3, i64 0, i64 0), i8* getelementptr ([7 x i8], [7 x i8]* @s6, i64 0, i64 0)
+
+  %len = call i64 @strnlen(i8* %ptr, i64 0)
+  ret i64 %len
+}
+
+
+; Fold strnlen (%0 ? s3 : s5, 1) to 1.
+
+define i64 @fold_strnlen_s3_s5_1(i1 %0) {
+; CHECK-LABEL: @fold_strnlen_s3_s5_1(
+; CHECK-NEXT:    [[PTR:%.*]] = select i1 [[TMP0:%.*]], i8* getelementptr inbounds ([4 x i8], [4 x i8]* @s3, i64 0, i64 0), i8* getelementptr inbounds ([7 x i8], [7 x i8]* @s6, i64 0, i64 0)
+; CHECK-NEXT:    [[LEN:%.*]] = call i64 @strnlen(i8* [[PTR]], i64 1)
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+  %ptr = select i1 %0, i8* getelementptr ([4 x i8], [4 x i8]* @s3, i64 0, i64 0), i8* getelementptr ([7 x i8], [7 x i8]* @s6, i64 0, i64 0)
+
+  %len = call i64 @strnlen(i8* %ptr, i64 1)
+  ret i64 %len
+}
+
+
+; Fold strnlen (%0 ? s3 : s5, 3) to 3.
+
+define i64 @fold_strnlen_s3_s5_3(i1 %0) {
+; CHECK-LABEL: @fold_strnlen_s3_s5_3(
+; CHECK-NEXT:    [[PTR:%.*]] = select i1 [[TMP0:%.*]], i8* getelementptr inbounds ([4 x i8], [4 x i8]* @s3, i64 0, i64 0), i8* getelementptr inbounds ([7 x i8], [7 x i8]* @s6, i64 0, i64 0)
+; CHECK-NEXT:    [[LEN:%.*]] = call i64 @strnlen(i8* [[PTR]], i64 3)
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+  %ptr = select i1 %0, i8* getelementptr ([4 x i8], [4 x i8]* @s3, i64 0, i64 0), i8* getelementptr ([7 x i8], [7 x i8]* @s6, i64 0, i64 0)
+
+  %len = call i64 @strnlen(i8* %ptr, i64 3)
+  ret i64 %len
+}
+
+
+; Fold strnlen (%0 ? s3 : s5, 4) to %0 ? 3 : 4.
+
+define i64 @fold_strnlen_s3_s5_4(i1 %0) {
+; CHECK-LABEL: @fold_strnlen_s3_s5_4(
+; CHECK-NEXT:    [[PTR:%.*]] = select i1 [[TMP0:%.*]], i8* getelementptr inbounds ([4 x i8], [4 x i8]* @s3, i64 0, i64 0), i8* getelementptr inbounds ([7 x i8], [7 x i8]* @s6, i64 0, i64 0)
+; CHECK-NEXT:    [[LEN:%.*]] = call i64 @strnlen(i8* [[PTR]], i64 4)
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+  %ptr = select i1 %0, i8* getelementptr ([4 x i8], [4 x i8]* @s3, i64 0, i64 0), i8* getelementptr ([7 x i8], [7 x i8]* @s6, i64 0, i64 0)
+
+  %len = call i64 @strnlen(i8* %ptr, i64 4)
+  ret i64 %len
+}
+
+
+; Fold strnlen (%0 ? s3 : s5, 5) to %0 ? 3 : 5.
+
+define i64 @fold_strnlen_s3_s5_5(i1 %0) {
+; CHECK-LABEL: @fold_strnlen_s3_s5_5(
+; CHECK-NEXT:    [[PTR:%.*]] = select i1 [[TMP0:%.*]], i8* getelementptr inbounds ([4 x i8], [4 x i8]* @s3, i64 0, i64 0), i8* getelementptr inbounds ([7 x i8], [7 x i8]* @s6, i64 0, i64 0)
+; CHECK-NEXT:    [[LEN:%.*]] = call i64 @strnlen(i8* [[PTR]], i64 5)
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+  %ptr = select i1 %0, i8* getelementptr ([4 x i8], [4 x i8]* @s3, i64 0, i64 0), i8* getelementptr ([7 x i8], [7 x i8]* @s6, i64 0, i64 0)
+
+  %len = call i64 @strnlen(i8* %ptr, i64 5)
+  ret i64 %len
+}
+
+
+; Fold strnlen (%0 ? s3 : s5, 6) to %0 ? 3 : 5.
+
+define i64 @fold_strnlen_s5_6(i1 %0) {
+; CHECK-LABEL: @fold_strnlen_s5_6(
+; CHECK-NEXT:    [[PTR:%.*]] = select i1 [[TMP0:%.*]], i8* getelementptr inbounds ([6 x i8], [6 x i8]* @s5, i64 0, i64 0), i8* getelementptr inbounds ([7 x i8], [7 x i8]* @s6, i64 0, i64 0)
+; CHECK-NEXT:    [[LEN:%.*]] = call i64 @strnlen(i8* [[PTR]], i64 6)
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+
+  %ptr = select i1 %0, i8* getelementptr ([6 x i8], [6 x i8]* @s5, i64 0, i64 0), i8* getelementptr ([7 x i8], [7 x i8]* @s6, i64 0, i64 0)
+
+  %len = call i64 @strnlen(i8* %ptr, i64 6)
+  ret i64 %len
+}
+
+
+; Fold strnlen(E, N) with E being two conditional expressions:
+;   strlen (x == 3 ? s3 : x == 5 ? s5 : s7, 4) to x == 3 ? 3 : 4.
+
+define i64 @fold_strnlen_s3_s5_s7_4(i32 %0) {
+; CHECK-LABEL: @fold_strnlen_s3_s5_s7_4(
+; CHECK-NEXT:    [[TMP2:%.*]] = icmp eq i32 [[TMP0:%.*]], 3
+; CHECK-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[TMP0]], 5
+; CHECK-NEXT:    [[TMP4:%.*]] = select i1 [[TMP3]], i8* getelementptr inbounds ([6 x i8], [6 x i8]* @s5, i64 0, i64 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* @s7, i64 0, i64 0)
+; CHECK-NEXT:    [[TMP5:%.*]] = select i1 [[TMP2]], i8* getelementptr inbounds ([4 x i8], [4 x i8]* @s3, i64 0, i64 0), i8* [[TMP4]]
+; CHECK-NEXT:    [[TMP6:%.*]] = tail call i64 @strnlen(i8* [[TMP5]], i64 4)
+; CHECK-NEXT:    ret i64 [[TMP6]]
+;
+
+  %2 = icmp eq i32 %0, 3
+  %3 = icmp eq i32 %0, 5
+  %4 = select i1 %3, i8* getelementptr ([6 x i8], [6 x i8]* @s5, i64 0, i64 0), i8* getelementptr ([8 x i8], [8 x i8]* @s7, i64 0, i64 0)
+  %5 = select i1 %2, i8* getelementptr ([4 x i8], [4 x i8]* @s3, i64 0, i64 0), i8* %4
+  %6 = tail call i64 @strnlen(i8* %5, i64 4)
+  ret i64 %6
+}
+
+
+; As above, fold strnlen(E, N) with E being two conditional expressions
+; but with N == 6:
+;   strlen (x == 3 ? s3 : x == 5 ? s5 : s7, 6) to x == 3 ? 3 : x == 5 ? 5 : 6.
+
+define i64 @fold_strnlen_s3_s5_s7_6(i32 %0) {
+; CHECK-LABEL: @fold_strnlen_s3_s5_s7_6(
+; CHECK-NEXT:    [[TMP2:%.*]] = icmp eq i32 [[TMP0:%.*]], 3
+; CHECK-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[TMP0]], 5
+; CHECK-NEXT:    [[TMP4:%.*]] = select i1 [[TMP3]], i8* getelementptr inbounds ([6 x i8], [6 x i8]* @s5, i64 0, i64 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* @s7, i64 0, i64 0)
+; CHECK-NEXT:    [[TMP5:%.*]] = select i1 [[TMP2]], i8* getelementptr inbounds ([4 x i8], [4 x i8]* @s3, i64 0, i64 0), i8* [[TMP4]]
+; CHECK-NEXT:    [[TMP6:%.*]] = tail call i64 @strnlen(i8* [[TMP5]], i64 6)
+; CHECK-NEXT:    ret i64 [[TMP6]]
+;
+
+  %2 = icmp eq i32 %0, 3
+  %3 = icmp eq i32 %0, 5
+  %4 = select i1 %3, i8* getelementptr ([6 x i8], [6 x i8]* @s5, i64 0, i64 0), i8* getelementptr ([8 x i8], [8 x i8]* @s7, i64 0, i64 0)
+  %5 = select i1 %2, i8* getelementptr ([4 x i8], [4 x i8]* @s3, i64 0, i64 0), i8* %4
+  %6 = tail call i64 @strnlen(i8* %5, i64 6)
+  ret i64 %6
+}
+
+
+; And again, fold strnlen(E, N) with E being two conditional expressions
+; but with N == 8:
+;   strlen (x == 3 ? s3 : x == 5 ? s5 : s7, 8) to x == 3 ? 3 : x == 5 ? 5 : 7.
+
+define i64 @fold_strnlen_s3_s5_s7_8(i32 %0) {
+; CHECK-LABEL: @fold_strnlen_s3_s5_s7_8(
+; CHECK-NEXT:    [[TMP2:%.*]] = icmp eq i32 [[TMP0:%.*]], 3
+; CHECK-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[TMP0]], 5
+; CHECK-NEXT:    [[TMP4:%.*]] = select i1 [[TMP3]], i8* getelementptr inbounds ([6 x i8], [6 x i8]* @s5, i64 0, i64 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* @s7, i64 0, i64 0)
+; CHECK-NEXT:    [[TMP5:%.*]] = select i1 [[TMP2]], i8* getelementptr inbounds ([4 x i8], [4 x i8]* @s3, i64 0, i64 0), i8* [[TMP4]]
+; CHECK-NEXT:    [[TMP6:%.*]] = tail call i64 @strnlen(i8* [[TMP5]], i64 8)
+; CHECK-NEXT:    ret i64 [[TMP6]]
+;
+
+  %2 = icmp eq i32 %0, 3
+  %3 = icmp eq i32 %0, 5
+  %4 = select i1 %3, i8* getelementptr ([6 x i8], [6 x i8]* @s5, i64 0, i64 0), i8* getelementptr ([8 x i8], [8 x i8]* @s7, i64 0, i64 0)
+  %5 = select i1 %2, i8* getelementptr ([4 x i8], [4 x i8]* @s3, i64 0, i64 0), i8* %4
+  %6 = tail call i64 @strnlen(i8* %5, i64 8)
+  ret i64 %6
+}
diff --git a/llvm/test/Transforms/InstCombine/strnlen-3.ll b/llvm/test/Transforms/InstCombine/strnlen-3.ll
new file mode 100644 (file)
index 0000000..33792f4
--- /dev/null
@@ -0,0 +1,233 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; Verify that strnlen calls with conditional expressions involving constant
+; string arguments with nonconstant offsets and constant bounds or constant
+; offsets and nonconstant bounds are folded correctly.
+;
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+declare i64 @strnlen(i8*, i64)
+
+@sx = external global [0 x i8]
+@a3 = constant [3 x i8] c"123"
+@s3 = constant [4 x i8] c"123\00"
+@s5 = constant [6 x i8] c"12345\00"
+@s5_3 = constant [10 x i8] c"12345\00abc\00"
+
+
+; Fold strnlen(sx + %0, 0) to 0.
+
+define i64 @fold_strnlen_sx_pi_0(i64 %0) {
+; CHECK-LABEL: @fold_strnlen_sx_pi_0(
+; CHECK-NEXT:    [[PTR:%.*]] = getelementptr [0 x i8], [0 x i8]* @sx, i64 0, i64 [[TMP0:%.*]]
+; CHECK-NEXT:    [[LEN:%.*]] = call i64 @strnlen(i8* [[PTR]], i64 0)
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+
+  %ptr = getelementptr [0 x i8], [0 x i8]* @sx, i64 0, i64 %0
+  %len = call i64 @strnlen(i8* %ptr, i64 0)
+  ret i64 %len
+}
+
+
+; Do not fold strnlen(sx + %0, %1).
+
+define i64 @call_strnlen_sx_pi_n(i64 %0, i64 %1) {
+; CHECK-LABEL: @call_strnlen_sx_pi_n(
+; CHECK-NEXT:    [[PTR:%.*]] = getelementptr inbounds [0 x i8], [0 x i8]* @sx, i64 0, i64 [[TMP0:%.*]]
+; CHECK-NEXT:    [[LEN:%.*]] = call i64 @strnlen(i8* nonnull [[PTR]], i64 [[TMP1:%.*]])
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+
+  %ptr = getelementptr inbounds [0 x i8], [0 x i8]* @sx, i64 0, i64 %0
+  %len = call i64 @strnlen(i8* %ptr, i64 %1)
+  ret i64 %len
+}
+
+
+; Fold strnlen(a3 + %0, 2) to min(3 - %0, 2).
+
+define i64 @call_strnlen_a3_pi_2(i64 %0) {
+; CHECK-LABEL: @call_strnlen_a3_pi_2(
+; CHECK-NEXT:    [[PTR:%.*]] = getelementptr inbounds [3 x i8], [3 x i8]* @a3, i64 0, i64 [[TMP0:%.*]]
+; CHECK-NEXT:    [[LEN:%.*]] = call i64 @strnlen(i8* nonnull [[PTR]], i64 2)
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+
+  %ptr = getelementptr inbounds [3 x i8], [3 x i8]* @a3, i64 0, i64 %0
+  %len = call i64 @strnlen(i8* %ptr, i64 2)
+  ret i64 %len
+}
+
+
+; Fold strnlen(a3 + %0, 3) to min(3 - %0, 3).
+
+define i64 @call_strnlen_a3_pi_3(i64 %0) {
+; CHECK-LABEL: @call_strnlen_a3_pi_3(
+; CHECK-NEXT:    [[PTR:%.*]] = getelementptr inbounds [3 x i8], [3 x i8]* @a3, i64 0, i64 [[TMP0:%.*]]
+; CHECK-NEXT:    [[LEN:%.*]] = call i64 @strnlen(i8* nonnull [[PTR]], i64 3)
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+
+  %ptr = getelementptr inbounds [3 x i8], [3 x i8]* @a3, i64 0, i64 %0
+  %len = call i64 @strnlen(i8* %ptr, i64 3)
+  ret i64 %len
+}
+
+
+; Fold strnlen(s3 + %0, 0) to 0.
+
+define i64 @fold_strnlen_s3_pi_0(i64 %0) {
+; CHECK-LABEL: @fold_strnlen_s3_pi_0(
+; CHECK-NEXT:    [[PTR:%.*]] = getelementptr inbounds [4 x i8], [4 x i8]* @s3, i64 0, i64 [[TMP0:%.*]]
+; CHECK-NEXT:    [[LEN:%.*]] = call i64 @strnlen(i8* nonnull [[PTR]], i64 0)
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+  %ptr = getelementptr inbounds [4 x i8], [4 x i8]* @s3, i64 0, i64 %0
+  %len = call i64 @strnlen(i8* %ptr, i64 0)
+  ret i64 %len
+}
+
+
+; Fold strnlen(s5 + %0, 0) to 0.
+
+define i64 @call_strnlen_s5_pi_0(i64 zeroext %0) {
+; CHECK-LABEL: @call_strnlen_s5_pi_0(
+; CHECK-NEXT:    [[LEN:%.*]] = call i64 @strnlen(i8* getelementptr inbounds ([6 x i8], [6 x i8]* @s5, i64 0, i64 0), i64 0)
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+  %ptr = getelementptr [6 x i8], [6 x i8]* @s5, i32 0, i32 0
+  %len = call i64 @strnlen(i8* %ptr, i64 0)
+  ret i64 %len
+}
+
+
+; Fold strnlen(s5_3 + %0, 0) to 0.
+
+define i64 @fold_strnlen_s5_3_pi_0(i64 zeroext %0) {
+; CHECK-LABEL: @fold_strnlen_s5_3_pi_0(
+; CHECK-NEXT:    [[PTR:%.*]] = getelementptr [10 x i8], [10 x i8]* @s5_3, i64 0, i64 [[TMP0:%.*]]
+; CHECK-NEXT:    [[LEN:%.*]] = call i64 @strnlen(i8* [[PTR]], i64 0)
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+  %ptr = getelementptr [10 x i8], [10 x i8]* @s5_3, i32 0, i64 %0
+  %len = call i64 @strnlen(i8* %ptr, i64 0)
+  ret i64 %len
+}
+
+
+; Do not fold strnlen(s5_3 + %0, %1).
+
+define i64 @fold_strnlen_s5_3_pi_n(i64 zeroext %0, i64 %1) {
+; CHECK-LABEL: @fold_strnlen_s5_3_pi_n(
+; CHECK-NEXT:    [[PTR:%.*]] = getelementptr inbounds [10 x i8], [10 x i8]* @s5_3, i64 0, i64 [[TMP0:%.*]]
+; CHECK-NEXT:    [[LEN:%.*]] = call i64 @strnlen(i8* nonnull [[PTR]], i64 [[TMP1:%.*]])
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+  %ptr = getelementptr inbounds [10 x i8], [10 x i8]* @s5_3, i32 0, i64 %0
+  %len = call i64 @strnlen(i8* %ptr, i64 %1)
+  ret i64 %len
+}
+
+
+; Fold strnlen(a3, %0) to min(sizeof(a3), %0)
+
+define i64 @fold_strnlen_a3_n(i64 %0) {
+; CHECK-LABEL: @fold_strnlen_a3_n(
+; CHECK-NEXT:    [[LEN:%.*]] = call i64 @strnlen(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @a3, i64 0, i64 0), i64 [[TMP0:%.*]])
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+
+  %ptr = getelementptr [3 x i8], [3 x i8]* @a3, i64 0, i64 0
+  %len = call i64 @strnlen(i8* %ptr, i64 %0)
+  ret i64 %len
+}
+
+
+; Fold strnlen(s3, %0) to min(strlen(s3), %0)
+
+define i64 @fold_strnlen_s3_n(i64 %0) {
+; CHECK-LABEL: @fold_strnlen_s3_n(
+; CHECK-NEXT:    [[LEN:%.*]] = call i64 @strnlen(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @s3, i64 0, i64 0), i64 [[TMP0:%.*]])
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+
+  %ptr = getelementptr [4 x i8], [4 x i8]* @s3, i64 0, i64 0
+  %len = call i64 @strnlen(i8* %ptr, i64 %0)
+  ret i64 %len
+}
+
+
+; Fold strnlen(a3 + %0, 2) to min(sizeof(a3) - %0, 2)
+
+define i64 @fold_strnlen_a3_pi_2(i64 %0) {
+; CHECK-LABEL: @fold_strnlen_a3_pi_2(
+; CHECK-NEXT:    [[PTR:%.*]] = getelementptr inbounds [3 x i8], [3 x i8]* @a3, i64 0, i64 [[TMP0:%.*]]
+; CHECK-NEXT:    [[LEN:%.*]] = call i64 @strnlen(i8* nonnull [[PTR]], i64 2)
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+
+  %ptr = getelementptr inbounds [3 x i8], [3 x i8]* @a3, i64 0, i64 %0
+  %len = call i64 @strnlen(i8* %ptr, i64 2)
+  ret i64 %len
+}
+
+
+; Fold strnlen(s3 + %0, 2) to min(strlen(s3) - %0, 2)
+
+define i64 @fold_strnlen_s3_pi_2(i64 %0) {
+; CHECK-LABEL: @fold_strnlen_s3_pi_2(
+; CHECK-NEXT:    [[PTR:%.*]] = getelementptr inbounds [4 x i8], [4 x i8]* @s3, i64 0, i64 [[TMP0:%.*]]
+; CHECK-NEXT:    [[LEN:%.*]] = call i64 @strnlen(i8* nonnull [[PTR]], i64 2)
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+
+  %ptr = getelementptr inbounds [4 x i8], [4 x i8]* @s3, i64 0, i64 %0
+  %len = call i64 @strnlen(i8* %ptr, i64 2)
+  ret i64 %len
+}
+
+
+; Fold strnlen(s3 + %0, 3) to min(strlen(s3) - %0, 3)
+
+define i64 @fold_strnlen_s3_pi_3(i64 %0) {
+; CHECK-LABEL: @fold_strnlen_s3_pi_3(
+; CHECK-NEXT:    [[PTR:%.*]] = getelementptr inbounds [4 x i8], [4 x i8]* @s3, i64 0, i64 [[TMP0:%.*]]
+; CHECK-NEXT:    [[LEN:%.*]] = call i64 @strnlen(i8* nonnull [[PTR]], i64 3)
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+
+  %ptr = getelementptr inbounds [4 x i8], [4 x i8]* @s3, i64 0, i64 %0
+  %len = call i64 @strnlen(i8* %ptr, i64 3)
+  ret i64 %len
+}
+
+
+; Fold strnlen(s3 + %0, %1) to min(strlen(s3) - %0, %1)
+
+define i64 @fold_strnlen_s3_pi_n(i64 %0, i64 %1) {
+; CHECK-LABEL: @fold_strnlen_s3_pi_n(
+; CHECK-NEXT:    [[PTR:%.*]] = getelementptr inbounds [4 x i8], [4 x i8]* @s3, i64 0, i64 [[TMP0:%.*]]
+; CHECK-NEXT:    [[LEN:%.*]] = call i64 @strnlen(i8* nonnull [[PTR]], i64 [[TMP1:%.*]])
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+
+  %ptr = getelementptr inbounds [4 x i8], [4 x i8]* @s3, i64 0, i64 %0
+  %len = call i64 @strnlen(i8* %ptr, i64 %1)
+  ret i64 %len
+}
+
+
+; Do not fold strnlen(s5_3 + %0, 2).  The result is in [0, 2] but there's
+; no simple way to derive its lower bound from the offset.
+
+define i64 @call_strnlen_s5_3_pi_2(i64 %0) {
+; CHECK-LABEL: @call_strnlen_s5_3_pi_2(
+; CHECK-NEXT:    [[PTR:%.*]] = getelementptr inbounds [10 x i8], [10 x i8]* @s5_3, i64 0, i64 [[TMP0:%.*]]
+; CHECK-NEXT:    [[LEN:%.*]] = call i64 @strnlen(i8* nonnull [[PTR]], i64 2)
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+
+  %ptr = getelementptr inbounds [10 x i8], [10 x i8]* @s5_3, i64 0, i64 %0
+  %len = call i64 @strnlen(i8* %ptr, i64 2)
+  ret i64 %len
+}
diff --git a/llvm/test/Transforms/InstCombine/strnlen-4.ll b/llvm/test/Transforms/InstCombine/strnlen-4.ll
new file mode 100644 (file)
index 0000000..96cb6cc
--- /dev/null
@@ -0,0 +1,85 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; Verify that strnlen calls with conditional expressions involving constant
+; string arguments with nonconstant bounds are folded correctly.
+;
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+declare i64 @strnlen(i8*, i64)
+
+@sx = external global [0 x i8]
+@s3 = constant [4 x i8] c"123\00"
+@s5 = constant [6 x i8] c"12345\00"
+@s5_3 = constant [10 x i8] c"12345\00abc\00"
+
+
+; Fold strnlen (%0 ? s3 + %1 : s5, %2) to min(%0 ? 3 : 5, %1) when
+; s3 + %1 is guaranteed to be within the bounds of s3.
+
+define i64 @fold_strnlen_s3_pi_s5_n(i1 %0, i64 %1, i64 %2) {
+; CHECK-LABEL: @fold_strnlen_s3_pi_s5_n(
+; CHECK-NEXT:    [[PTR:%.*]] = getelementptr inbounds [4 x i8], [4 x i8]* @s3, i64 0, i64 [[TMP1:%.*]]
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[TMP0:%.*]], i8* [[PTR]], i8* getelementptr inbounds ([6 x i8], [6 x i8]* @s5, i64 0, i64 0)
+; CHECK-NEXT:    [[LEN:%.*]] = call i64 @strnlen(i8* [[SEL]], i64 [[TMP2:%.*]])
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+
+  %ptr = getelementptr inbounds [4 x i8], [4 x i8]* @s3, i64 0, i64 %1
+  %sel = select i1 %0, i8* %ptr, i8* getelementptr ([6 x i8], [6 x i8]* @s5, i64 0, i64 0)
+  %len = call i64 @strnlen(i8* %sel, i64 %2)
+  ret i64 %len
+}
+
+
+; Do not fold the same expression as above when s3 + %i is not guaranteed
+; to be within the bounds of s3.  Also verify that the call is not marked
+; noundef, nonnull, or dereferenceable because a zero bound implies no
+; access.
+
+define i64 @call_strnlen_s3_pi_xbounds_s5_n(i1 %0, i64 %1, i64 %2) {
+; CHECK-LABEL: @call_strnlen_s3_pi_xbounds_s5_n(
+; CHECK-NEXT:    [[PTR:%.*]] = getelementptr [4 x i8], [4 x i8]* @s3, i64 0, i64 [[TMP1:%.*]]
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[TMP0:%.*]], i8* [[PTR]], i8* getelementptr inbounds ([6 x i8], [6 x i8]* @s5, i64 0, i64 0)
+; CHECK-NEXT:    [[LEN:%.*]] = call i64 @strnlen(i8* [[SEL]], i64 [[TMP2:%.*]])
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+
+  %ptr = getelementptr [4 x i8], [4 x i8]* @s3, i64 0, i64 %1
+  %sel = select i1 %0, i8* %ptr, i8* getelementptr ([6 x i8], [6 x i8]* @s5, i64 0, i64 0)
+  %len = call i64 @strnlen(i8* %sel, i64 %2)
+  ret i64 %len
+}
+
+
+; Do not fold strnlen(%0 ? s3 + %1 : sx, %1) when sx's length and size
+; are unknown.  This also verifies that the folder cleans up the IR after
+; successfully folding the first subexpression IR when folding the second
+; subexpression fails.
+
+define i64 @call_strnlen_s3_pi_sx_n(i1 %0, i64 %1, i64 %2) {
+; CHECK-LABEL: @call_strnlen_s3_pi_sx_n(
+; CHECK-NEXT:    [[PTR:%.*]] = getelementptr inbounds [4 x i8], [4 x i8]* @s3, i64 0, i64 [[TMP1:%.*]]
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[TMP0:%.*]], i8* [[PTR]], i8* getelementptr inbounds ([0 x i8], [0 x i8]* @sx, i64 0, i64 0)
+; CHECK-NEXT:    [[LEN:%.*]] = call i64 @strnlen(i8* [[SEL]], i64 [[TMP2:%.*]])
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+
+  %ptr = getelementptr inbounds [4 x i8], [4 x i8]* @s3, i64 0, i64 %1
+  %sel = select i1 %0, i8* %ptr, i8* getelementptr ([0 x i8], [0 x i8]* @sx, i64 0, i64 0)
+  %len = call i64 @strnlen(i8* %sel, i64 %2)
+  ret i64 %len
+}
+
+
+; Fold strnlen (%0 ? s3 : s5 + %1, %2) to min(%0 ? 3 : 5, %1).
+
+define i64 @fold_strnlen_s3_s5_pi_n(i1 %0, i64 %1, i64 %2) {
+; CHECK-LABEL: @fold_strnlen_s3_s5_pi_n(
+; CHECK-NEXT:    [[PTR:%.*]] = select i1 [[TMP0:%.*]], i8* getelementptr inbounds ([6 x i8], [6 x i8]* @s5, i64 0, i64 0), i8* getelementptr inbounds ([4 x i8], [4 x i8]* @s3, i64 0, i64 0)
+; CHECK-NEXT:    [[LEN:%.*]] = call i64 @strnlen(i8* [[PTR]], i64 [[TMP1:%.*]])
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+
+  %ptr = select i1 %0, i8* getelementptr ([6 x i8], [6 x i8]* @s5, i64 0, i64 0), i8* getelementptr ([4 x i8], [4 x i8]* @s3, i64 0, i64 0)
+  %len = call i64 @strnlen(i8* %ptr, i64 %1)
+  ret i64 %len
+}
diff --git a/llvm/test/Transforms/InstCombine/strnlen-5.ll b/llvm/test/Transforms/InstCombine/strnlen-5.ll
new file mode 100644 (file)
index 0000000..9887d29
--- /dev/null
@@ -0,0 +1,201 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; Verify that equality tests of strnlen calls against zero are folded
+; correctly.
+;
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+declare i64 @strnlen(i8*, i64)
+
+@ax = external global [0 x i8]
+@a5 = external global [5 x i8]
+@s5 = constant [6 x i8] c"12345\00"
+
+
+; Fold strnlen(ax, 0) == 0 to true.
+
+define i1 @fold_strnlen_ax_0_eqz() {
+; CHECK-LABEL: @fold_strnlen_ax_0_eqz(
+; CHECK-NEXT:    [[LEN:%.*]] = tail call i64 @strnlen(i8* getelementptr inbounds ([0 x i8], [0 x i8]* @ax, i64 0, i64 0), i64 0)
+; CHECK-NEXT:    [[EQZ:%.*]] = icmp eq i64 [[LEN]], 0
+; CHECK-NEXT:    ret i1 [[EQZ]]
+;
+
+  %ptr = getelementptr [0 x i8], [0 x i8]* @ax, i64 0, i64 0
+  %len = tail call i64 @strnlen(i8* %ptr, i64 0)
+  %eqz = icmp eq i64 %len, 0
+  ret i1 %eqz
+}
+
+
+; Fold strnlen(ax, 0) > 0 to false.
+
+define i1 @fold_strnlen_ax_0_gtz() {
+; CHECK-LABEL: @fold_strnlen_ax_0_gtz(
+; CHECK-NEXT:    [[LEN:%.*]] = tail call i64 @strnlen(i8* getelementptr inbounds ([0 x i8], [0 x i8]* @ax, i64 0, i64 0), i64 0)
+; CHECK-NEXT:    [[GTZ:%.*]] = icmp ne i64 [[LEN]], 0
+; CHECK-NEXT:    ret i1 [[GTZ]]
+;
+
+  %ptr = getelementptr [0 x i8], [0 x i8]* @ax, i64 0, i64 0
+  %len = tail call i64 @strnlen(i8* %ptr, i64 0)
+  %gtz = icmp ugt i64 %len, 0
+  ret i1 %gtz
+}
+
+
+; Fold strnlen(ax, 1) == 0 to *ax == 0.
+
+define i1 @fold_strnlen_ax_1_eqz() {
+; CHECK-LABEL: @fold_strnlen_ax_1_eqz(
+; CHECK-NEXT:    [[LEN:%.*]] = tail call i64 @strnlen(i8* getelementptr inbounds ([0 x i8], [0 x i8]* @ax, i64 0, i64 0), i64 1)
+; CHECK-NEXT:    [[EQZ:%.*]] = icmp eq i64 [[LEN]], 0
+; CHECK-NEXT:    ret i1 [[EQZ]]
+;
+
+  %ptr = getelementptr [0 x i8], [0 x i8]* @ax, i64 0, i64 0
+  %len = tail call i64 @strnlen(i8* %ptr, i64 1)
+  %eqz = icmp eq i64 %len, 0
+  ret i1 %eqz
+}
+
+
+; Fold strnlen(ax, 1) != 0 to *ax != 0.
+
+define i1 @fold_strnlen_ax_1_neqz() {
+; CHECK-LABEL: @fold_strnlen_ax_1_neqz(
+; CHECK-NEXT:    [[LEN:%.*]] = tail call i64 @strnlen(i8* getelementptr inbounds ([0 x i8], [0 x i8]* @ax, i64 0, i64 0), i64 1)
+; CHECK-NEXT:    [[NEZ:%.*]] = icmp ne i64 [[LEN]], 0
+; CHECK-NEXT:    ret i1 [[NEZ]]
+;
+
+  %ptr = getelementptr [0 x i8], [0 x i8]* @ax, i64 0, i64 0
+  %len = tail call i64 @strnlen(i8* %ptr, i64 1)
+  %nez = icmp ne i64 %len, 0
+  ret i1 %nez
+}
+
+
+; Fold strnlen(ax, 9) == 0 to *ax == 0.
+
+define i1 @fold_strnlen_ax_9_eqz() {
+; CHECK-LABEL: @fold_strnlen_ax_9_eqz(
+; CHECK-NEXT:    [[LEN:%.*]] = tail call i64 @strnlen(i8* getelementptr inbounds ([0 x i8], [0 x i8]* @ax, i64 0, i64 0), i64 9)
+; CHECK-NEXT:    [[EQZ:%.*]] = icmp eq i64 [[LEN]], 0
+; CHECK-NEXT:    ret i1 [[EQZ]]
+;
+
+  %ptr = getelementptr [0 x i8], [0 x i8]* @ax, i64 0, i64 0
+  %len = tail call i64 @strnlen(i8* %ptr, i64 9)
+  %eqz = icmp eq i64 %len, 0
+  ret i1 %eqz
+}
+
+
+; Do not fold strnlen(ax, %0) == 0 for %0 that might be zero.
+
+define i1 @call_strnlen_ax_n_eqz(i64 %0) {
+; CHECK-LABEL: @call_strnlen_ax_n_eqz(
+; CHECK-NEXT:    [[LEN:%.*]] = tail call i64 @strnlen(i8* getelementptr inbounds ([0 x i8], [0 x i8]* @ax, i64 0, i64 0), i64 [[TMP0:%.*]])
+; CHECK-NEXT:    [[EQZ:%.*]] = icmp eq i64 [[LEN]], 0
+; CHECK-NEXT:    ret i1 [[EQZ]]
+;
+
+  %ptr = getelementptr [0 x i8], [0 x i8]* @ax, i64 0, i64 0
+  %len = tail call i64 @strnlen(i8* %ptr, i64 %0)
+  %eqz = icmp eq i64 %len, 0
+  ret i1 %eqz
+}
+
+
+; Fold strnlen(ax, %0) == 0 to *ax == 0 for %0 that's not zero.
+
+define i1 @fold_strnlen_ax_nz_eqz(i64 %0) {
+; CHECK-LABEL: @fold_strnlen_ax_nz_eqz(
+; CHECK-NEXT:    [[MAX:%.*]] = or i64 [[TMP0:%.*]], 1
+; CHECK-NEXT:    [[LEN:%.*]] = tail call i64 @strnlen(i8* getelementptr inbounds ([0 x i8], [0 x i8]* @ax, i64 0, i64 0), i64 [[MAX]])
+; CHECK-NEXT:    [[EQZ:%.*]] = icmp eq i64 [[LEN]], 0
+; CHECK-NEXT:    ret i1 [[EQZ]]
+;
+
+  %max = or i64 %0, 1
+  %ptr = getelementptr [0 x i8], [0 x i8]* @ax, i64 0, i64 0
+  %len = tail call i64 @strnlen(i8* %ptr, i64 %max)
+  %eqz = icmp eq i64 %len, 0
+  ret i1 %eqz
+}
+
+
+; Fold strnlen(ax, %0) > 0 to *ax != 0 for %0 that's not zero.
+
+define i1 @fold_strnlen_ax_nz_gtz(i64 %0) {
+; CHECK-LABEL: @fold_strnlen_ax_nz_gtz(
+; CHECK-NEXT:    [[MAX:%.*]] = or i64 [[TMP0:%.*]], 1
+; CHECK-NEXT:    [[LEN:%.*]] = tail call i64 @strnlen(i8* getelementptr inbounds ([0 x i8], [0 x i8]* @ax, i64 0, i64 0), i64 [[MAX]])
+; CHECK-NEXT:    [[GTZ:%.*]] = icmp ne i64 [[LEN]], 0
+; CHECK-NEXT:    ret i1 [[GTZ]]
+;
+
+  %max = or i64 %0, 1
+  %ptr = getelementptr [0 x i8], [0 x i8]* @ax, i64 0, i64 0
+  %len = tail call i64 @strnlen(i8* %ptr, i64 %max)
+  %gtz = icmp ugt i64 %len, 0
+  ret i1 %gtz
+}
+
+
+; Fold strnlen(a5 + %0, %1) == 0 to a5[%0] == 0 for a nonconstant a5
+; and a nonzero %1.
+
+define i1 @fold_strnlen_a5_pi_nz_eqz(i64 %0, i64 %1) {
+; CHECK-LABEL: @fold_strnlen_a5_pi_nz_eqz(
+; CHECK-NEXT:    [[NZ:%.*]] = or i64 [[TMP1:%.*]], 1
+; CHECK-NEXT:    [[PTR:%.*]] = getelementptr inbounds [5 x i8], [5 x i8]* @a5, i64 0, i64 [[TMP0:%.*]]
+; CHECK-NEXT:    [[LEN:%.*]] = call i64 @strnlen(i8* nonnull [[PTR]], i64 [[NZ]])
+; CHECK-NEXT:    [[EQZ:%.*]] = icmp eq i64 [[LEN]], 0
+; CHECK-NEXT:    ret i1 [[EQZ]]
+;
+
+  %nz = or i64 %1, 1
+  %ptr = getelementptr inbounds [5 x i8], [5 x i8]* @a5, i64 0, i64 %0
+  %len = call i64 @strnlen(i8* nonnull %ptr, i64 %nz)
+  %eqz = icmp eq i64 %len, 0
+  ret i1 %eqz
+}
+
+
+; Fold strnlen(s5 + %0, %1) == 0 for a constant s5 and nonzero %1.
+; This is first folded to s5[%0] == 0 like the above and then finally
+; to %0 == 5.
+
+define i1 @fold_strnlen_s5_pi_nz_eqz(i64 %0, i64 %1) {
+; CHECK-LABEL: @fold_strnlen_s5_pi_nz_eqz(
+; CHECK-NEXT:    [[TMP3:%.*]] = or i64 [[TMP1:%.*]], 1
+; CHECK-NEXT:    [[TMP4:%.*]] = getelementptr inbounds [6 x i8], [6 x i8]* @s5, i64 0, i64 [[TMP0:%.*]]
+; CHECK-NEXT:    [[TMP5:%.*]] = call i64 @strnlen(i8* nonnull [[TMP4]], i64 [[TMP3]])
+; CHECK-NEXT:    [[TMP6:%.*]] = icmp eq i64 [[TMP5]], 0
+; CHECK-NEXT:    ret i1 [[TMP6]]
+;
+
+  %3 = or i64 %1, 1
+  %4 = getelementptr inbounds [6 x i8], [6 x i8]* @s5, i64 0, i64 %0
+  %5 = call i64 @strnlen(i8* nonnull %4, i64 %3)
+  %6 = icmp eq i64 %5, 0
+  ret i1 %6
+}
+
+
+; Do not fold strnlen(s5 + %0, %1) for a constant s5 when %1 might be zero.
+
+define i1 @call_strnlen_s5_pi_n_eqz(i64 %0, i64 %1) {
+; CHECK-LABEL: @call_strnlen_s5_pi_n_eqz(
+; CHECK-NEXT:    [[TMP3:%.*]] = getelementptr inbounds [6 x i8], [6 x i8]* @s5, i64 0, i64 [[TMP0:%.*]]
+; CHECK-NEXT:    [[TMP4:%.*]] = call i64 @strnlen(i8* nonnull [[TMP3]], i64 [[TMP1:%.*]])
+; CHECK-NEXT:    [[TMP5:%.*]] = icmp eq i64 [[TMP4]], 0
+; CHECK-NEXT:    ret i1 [[TMP5]]
+;
+
+  %3 = getelementptr inbounds [6 x i8], [6 x i8]* @s5, i64 0, i64 %0
+  %4 = call i64 @strnlen(i8* nonnull %3, i64 %1)
+  %5 = icmp eq i64 %4, 0
+  ret i1 %5
+}
diff --git a/llvm/test/Transforms/InstCombine/strnlen-6.ll b/llvm/test/Transforms/InstCombine/strnlen-6.ll
new file mode 100644 (file)
index 0000000..91c8582
--- /dev/null
@@ -0,0 +1,60 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; Verify that strnlen calls that aren't folded into constants are annotated
+; with noundef, nonnull, and dereferenceable only when maxlen is known to
+; to be nonzero.
+;
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+declare i64 @strnlen(i8*, i64)
+
+@ecp = external global i8*, align 8
+
+
+; Annotate strnlen(ecp, 3) call with noundef, nonnull, and dereferenceable
+; based on the access to *ecp.
+
+define i64 @deref_strnlen_ecp_3() {
+; CHECK-LABEL: @deref_strnlen_ecp_3(
+; CHECK-NEXT:    [[PTR:%.*]] = load i8*, i8** @ecp, align 8
+; CHECK-NEXT:    [[LEN:%.*]] = call i64 @strnlen(i8* [[PTR]], i64 3)
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+  %ptr = load i8*, i8** @ecp
+  %len = call i64 @strnlen(i8* %ptr, i64 3)
+  ret i64 %len
+}
+
+
+; Annotate strnlen(ecp, %n) call with nonzero %n with noundef, nonnull, and
+; dereferenceable based on the access to *ecp.
+
+define i64 @deref_strnlen_ecp_nz(i64 %n) {
+; CHECK-LABEL: @deref_strnlen_ecp_nz(
+; CHECK-NEXT:    [[NONZERO:%.*]] = or i64 [[N:%.*]], 1
+; CHECK-NEXT:    [[PTR:%.*]] = load i8*, i8** @ecp, align 8
+; CHECK-NEXT:    [[LEN:%.*]] = call i64 @strnlen(i8* [[PTR]], i64 [[NONZERO]])
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+  %nonzero = or i64 %n, 1
+  %ptr = load i8*, i8** @ecp
+  %len = call i64 @strnlen(i8* %ptr, i64 %nonzero)
+  ret i64 %len
+}
+
+
+
+; Do not annotate strnlen(ecp, %n) call with nonnull etc. because it need
+; not access *ecp.  (Strictly, every pointer function argument must be
+; noundef, so this is overly conservative.)
+
+
+define i64 @noderef_strnlen_ecp_n(i64 %n) {
+; CHECK-LABEL: @noderef_strnlen_ecp_n(
+; CHECK-NEXT:    [[PTR:%.*]] = load i8*, i8** @ecp, align 8
+; CHECK-NEXT:    [[LEN:%.*]] = call i64 @strnlen(i8* [[PTR]], i64 [[N:%.*]])
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+  %ptr = load i8*, i8** @ecp
+  %len = call i64 @strnlen(i8* %ptr, i64 %n)
+  ret i64 %len
+}
diff --git a/llvm/test/Transforms/InstCombine/wcslen-5.ll b/llvm/test/Transforms/InstCombine/wcslen-5.ll
new file mode 100644 (file)
index 0000000..106867b
--- /dev/null
@@ -0,0 +1,153 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; Verify that wcslen calls with conditional expressions involving constant
+; string arguments with nonconstant offsets are folded as expected.  See
+; strlen-4.ll for the corresponding strlen test.
+;
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+declare i64 @wcslen(i32*)
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!llvm.module.flags = !{!0}
+
+@ws3 = constant [4 x i32] [i32 1, i32 2, i32 3, i32 0]
+@ws5 = constant [6 x i32] [i32 1, i32 2, i32 3, i32 4, i32 5, i32 0]
+@ws5_3 = constant [10 x i32] [i32 1, i32 2, i32 3, i32 4, i32 5, i32 0, i32 6, i32 7, i32 8, i32 0]
+
+
+; Fold wcslen (x ? s3 + i: s5) to x ? 3 - i : 5.
+
+define dso_local i64 @fold_wcslen_s3_pi_s5(i1 zeroext %0, i64 %1) {
+; CHECK-LABEL: @fold_wcslen_s3_pi_s5(
+; CHECK-NEXT:    [[PS3_PI:%.*]] = getelementptr inbounds [4 x i32], [4 x i32]* @ws3, i64 0, i64 [[TMP1:%.*]]
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[TMP0:%.*]], i32* [[PS3_PI]], i32* getelementptr inbounds ([6 x i32], [6 x i32]* @ws5, i64 0, i64 0)
+; CHECK-NEXT:    [[LEN:%.*]] = tail call i64 @wcslen(i32* [[SEL]])
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+
+  %ps3_pi = getelementptr inbounds [4 x i32], [4 x i32]* @ws3, i64 0, i64 %1
+  %ps5 = getelementptr inbounds [6 x i32], [6 x i32]* @ws5, i64 0, i64 0
+  %sel = select i1 %0, i32* %ps3_pi, i32* %ps5
+  %len = tail call i64 @wcslen(i32* %sel)
+  ret i64 %len
+}
+
+
+; More complex expressions like the one below are not handled yet.
+; Fold: wcslen (x ? s3 + i + 1 : s5 + j + 2) to x ? 2 - i : 3 - j.
+
+define dso_local i64 @fold_wcslen_s3_pi_p1_s5(i1 zeroext %0, i64 %1) {
+; XFAIL-CHECK-LABEL: @fold_wcslen_s3_pi_p1_s5(
+; XFAIL-CHECK-NEXT:    [[DIF_I:%.*]] = sub i64 2, %1
+; XFAIL-CHECK-NEXT:    [[SEL:%.*]] = select i1 %0, i64 [[DIF_I]], i64 5
+; XFAIL-CHECK-NEXT:    ret i64 [[SEL]]
+; CHECK-LABEL: @fold_wcslen_s3_pi_p1_s5(
+; CHECK-NEXT:    [[PS3_PI:%.*]] = getelementptr inbounds [4 x i32], [4 x i32]* @ws3, i64 0, i64 [[TMP1:%.*]]
+; CHECK-NEXT:    [[PS3_PI_P1:%.*]] = getelementptr inbounds i32, i32* [[PS3_PI]], i64 1
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[TMP0:%.*]], i32* [[PS3_PI_P1]], i32* getelementptr inbounds ([6 x i32], [6 x i32]* @ws5, i64 0, i64 0)
+; CHECK-NEXT:    [[LEN:%.*]] = tail call i64 @wcslen(i32* [[SEL]])
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+
+  %ps3_pi = getelementptr inbounds [4 x i32], [4 x i32]* @ws3, i64 0, i64 %1
+  %ps3_pi_p1 = getelementptr inbounds i32, i32* %ps3_pi, i64 1
+  %ps5 = getelementptr inbounds [6 x i32], [6 x i32]* @ws5, i64 0, i64 0
+  %sel = select i1 %0, i32* %ps3_pi_p1, i32* %ps5
+  %len = tail call i64 @wcslen(i32* %sel)
+  ret i64 %len
+}
+
+
+; Avoid folding calls with conditional expressions involving constant
+; string arguments with embedded nuls such as:
+;   wcslen (x ? s5_3 + i : s5).
+
+define dso_local i64 @call_wcslen_s5_3_pi_s5(i1 zeroext %0, i64 %1) {
+; CHECK-LABEL: @call_wcslen_s5_3_pi_s5(
+; CHECK-NEXT:    [[PS5_3_PI:%.*]] = getelementptr inbounds [10 x i32], [10 x i32]* @ws5_3, i64 0, i64 [[TMP1:%.*]]
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[TMP0:%.*]], i32* [[PS5_3_PI]], i32* getelementptr inbounds ([6 x i32], [6 x i32]* @ws5, i64 0, i64 0)
+; CHECK-NEXT:    [[LEN:%.*]] = tail call i64 @wcslen(i32* [[SEL]])
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+
+  %ps5_3_pi = getelementptr inbounds [10 x i32], [10 x i32]* @ws5_3, i64 0, i64 %1
+  %ps5 = getelementptr inbounds [6 x i32], [6 x i32]* @ws5, i64 0, i64 0
+  %sel = select i1 %0, i32* %ps5_3_pi, i32* %ps5
+  %len = tail call i64 @wcslen(i32* %sel)
+  ret i64 %len
+}
+
+
+; But do fold wcslen (x ? s5_3 : s5 + j) to x ? 5 : 5 - j.
+
+define dso_local i64 @call_wcslen_s5_3_s5_pj(i1 zeroext %0, i64 %1) {
+; CHECK-LABEL: @call_wcslen_s5_3_s5_pj(
+; CHECK-NEXT:    [[PS5:%.*]] = getelementptr inbounds [6 x i32], [6 x i32]* @ws5, i64 0, i64 [[TMP1:%.*]]
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[TMP0:%.*]], i32* getelementptr inbounds ([10 x i32], [10 x i32]* @ws5_3, i64 0, i64 0), i32* [[PS5]]
+; CHECK-NEXT:    [[LEN:%.*]] = tail call i64 @wcslen(i32* [[SEL]])
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+
+  %ps5_3_pi = getelementptr inbounds [10 x i32], [10 x i32]* @ws5_3, i64 0, i64 0
+  %ps5 = getelementptr inbounds [6 x i32], [6 x i32]* @ws5, i64 0, i64 %1
+  %sel = select i1 %0, i32* %ps5_3_pi, i32* %ps5
+  %len = tail call i64 @wcslen(i32* %sel)
+  ret i64 %len
+}
+
+
+; Fold wcslen (x ? s3: s5 + j) to x ? 3 : 5 - j.
+
+define dso_local i64 @fold_wcslen_s3_s5_pj(i1 zeroext %0, i64 %1) {
+; CHECK-LABEL: @fold_wcslen_s3_s5_pj(
+; CHECK-NEXT:    [[PS5_PJ:%.*]] = getelementptr inbounds [6 x i32], [6 x i32]* @ws5, i64 0, i64 [[TMP1:%.*]]
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[TMP0:%.*]], i32* getelementptr inbounds ([4 x i32], [4 x i32]* @ws3, i64 0, i64 0), i32* [[PS5_PJ]]
+; CHECK-NEXT:    [[LEN:%.*]] = tail call i64 @wcslen(i32* [[SEL]])
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+
+  %ps3 = getelementptr inbounds [4 x i32], [4 x i32]* @ws3, i64 0, i64 0
+  %ps5_pj = getelementptr inbounds [6 x i32], [6 x i32]* @ws5, i64 0, i64 %1
+  %sel = select i1 %0, i32* %ps3, i32* %ps5_pj
+  %len = tail call i64 @wcslen(i32* %sel)
+  ret i64 %len
+}
+
+
+; Same as above, avoid folding calls with conditional expressions involving
+; constant string arguments with embedded nuls such as:
+;   wcslen (x ? s3 : s5_3 + j).
+
+define dso_local i64 @call_wcslen_s3_s5_3_pj(i1 zeroext %0, i64 %1) {
+; CHECK-LABEL: @call_wcslen_s3_s5_3_pj(
+; CHECK-NEXT:    [[PS5_3_PJ:%.*]] = getelementptr inbounds [10 x i32], [10 x i32]* @ws5_3, i64 0, i64 [[TMP1:%.*]]
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[TMP0:%.*]], i32* getelementptr inbounds ([4 x i32], [4 x i32]* @ws3, i64 0, i64 0), i32* [[PS5_3_PJ]]
+; CHECK-NEXT:    [[LEN:%.*]] = tail call i64 @wcslen(i32* [[SEL]])
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+
+  %ps3 = getelementptr inbounds [4 x i32], [4 x i32]* @ws3, i64 0, i64 0
+  %ps5_3_pj = getelementptr inbounds [10 x i32], [10 x i32]* @ws5_3, i64 0, i64 %1
+  %sel = select i1 %0, i32* %ps3, i32* %ps5_3_pj
+  %len = tail call i64 @wcslen(i32* %sel)
+  ret i64 %len
+}
+
+
+; Fold wcslen (x ? s3 + i: s5 + j) to x ? 3 - i : 5 - j.
+
+define dso_local i64 @fold_wcslen_s3_pi_s5_pj(i1 zeroext %0, i64 %1, i64 %2) {
+; CHECK-LABEL: @fold_wcslen_s3_pi_s5_pj(
+; CHECK-NEXT:    [[PS3_PI:%.*]] = getelementptr inbounds [4 x i32], [4 x i32]* @ws3, i64 0, i64 [[TMP1:%.*]]
+; CHECK-NEXT:    [[PS5_PJ:%.*]] = getelementptr inbounds [6 x i32], [6 x i32]* @ws5, i64 0, i64 [[TMP2:%.*]]
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[TMP0:%.*]], i32* [[PS3_PI]], i32* [[PS5_PJ]]
+; CHECK-NEXT:    [[LEN:%.*]] = tail call i64 @wcslen(i32* nonnull [[SEL]])
+; CHECK-NEXT:    ret i64 [[LEN]]
+;
+
+  %ps3_pi = getelementptr inbounds [4 x i32], [4 x i32]* @ws3, i64 0, i64 %1
+  %ps5_pj = getelementptr inbounds [6 x i32], [6 x i32]* @ws5, i64 0, i64 %2
+  %sel = select i1 %0, i32* %ps3_pi, i32* %ps5_pj
+  %len = tail call i64 @wcslen(i32* %sel)
+  ret i64 %len
+}