[InstCombine] Fold strnlen with a bound of zero and one.
authorMartin Sebor <msebor@redhat.com>
Tue, 26 Apr 2022 17:48:43 +0000 (11:48 -0600)
committerMartin Sebor <msebor@redhat.com>
Tue, 26 Apr 2022 20:02:50 +0000 (14:02 -0600)
Reviewed By: nikic

Differential Revision: https://reviews.llvm.org/D123816

llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h
llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
llvm/test/Transforms/InstCombine/strnlen-1.ll
llvm/test/Transforms/InstCombine/strnlen-2.ll
llvm/test/Transforms/InstCombine/strnlen-3.ll
llvm/test/Transforms/InstCombine/strnlen-5.ll

index 7eca162..90a208a 100644 (file)
@@ -237,8 +237,9 @@ private:
   /// function by checking for an existing function with name FuncName + f
   bool hasFloatVersion(StringRef FuncName);
 
-  /// Shared code to optimize strlen+wcslen.
-  Value *optimizeStringLength(CallInst *CI, IRBuilderBase &B, unsigned CharSize);
+  /// Shared code to optimize strlen+wcslen and strnlen+wcsnlen.
+  Value *optimizeStringLength(CallInst *CI, IRBuilderBase &B, unsigned CharSize,
+                              Value *Bound = nullptr);
 };
 } // End llvm namespace
 
index 4556327..8d59a5e 100644 (file)
@@ -629,9 +629,29 @@ Value *LibCallSimplifier::optimizeStrNCpy(CallInst *CI, IRBuilderBase &B) {
 }
 
 Value *LibCallSimplifier::optimizeStringLength(CallInst *CI, IRBuilderBase &B,
-                                               unsigned CharSize) {
+                                               unsigned CharSize,
+                                               Value *Bound) {
   Value *Src = CI->getArgOperand(0);
 
+  if (Bound) {
+    if (ConstantInt *BoundCst = dyn_cast<ConstantInt>(Bound)) {
+      if (BoundCst->isZero())
+        // Fold strnlen(s, 0) -> 0 for any s, constant or otherwise.
+        return ConstantInt::get(CI->getType(), 0);
+
+      if (BoundCst->isOne()) {
+        // Fold strnlen(s, 1) -> *s ? 1 : 0 for any s.
+        Type *CharTy = B.getIntNTy(CharSize);
+        Value *CharVal = B.CreateLoad(CharTy, Src, "strnlen.char0");
+        Value *ZeroChar = ConstantInt::get(CharTy, 0);
+        Value *Cmp = B.CreateICmpNE(CharVal, ZeroChar, "strnlen.char0cmp");
+        return B.CreateZExt(Cmp, CI->getType());
+      }
+    }
+    // Otherwise punt for strnlen for now.
+    return nullptr;
+  }
+
   // Constant folding: strlen("xyz") -> 3
   if (uint64_t Len = GetStringLength(Src, CharSize))
     return ConstantInt::get(CI->getType(), Len - 1);
@@ -719,6 +739,9 @@ Value *LibCallSimplifier::optimizeStrLen(CallInst *CI, IRBuilderBase &B) {
 
 Value *LibCallSimplifier::optimizeStrNLen(CallInst *CI, IRBuilderBase &B) {
   Value *Bound = CI->getArgOperand(1);
+  if (Value *V = optimizeStringLength(CI, B, 8, Bound))
+    return V;
+
   if (isKnownNonZero(Bound, DL))
     annotateNonNullNoUndefBasedOnAccess(CI, 0);
   return nullptr;
index f8fabbe..d07e8dd 100644 (file)
@@ -6,6 +6,7 @@
 
 declare i64 @strnlen(i8*, i64)
 
+@ax = external global [0 x i8]
 @s5 = constant [6 x i8] c"12345\00"
 @s5_3 = constant [9 x i8] c"12345\00xyz"
 
@@ -51,12 +52,38 @@ define i64 @access_strnlen_p_nz(i8* %ptr, i64 %n) {
 }
 
 
+; Fold strnlen(ax, 0) to 0.
+
+define i64 @fold_strnlen_ax_0() {
+; CHECK-LABEL: @fold_strnlen_ax_0(
+; CHECK-NEXT:    ret i64 0
+;
+  %ptr = getelementptr [0 x i8], [0 x i8]* @ax, i32 0, i32 0
+  %len = call i64 @strnlen(i8* %ptr, i64 0)
+  ret i64 %len
+}
+
+
+; Fold strnlen(ax, 1) to *ax ? 1 : 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]]
+;
+  %ptr = getelementptr [0 x i8], [0 x i8]* @ax, i32 0, i32 0
+  %len = call i64 @strnlen(i8* %ptr, i64 1)
+  ret i64 %len
+}
+
+
 ; 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]]
+; CHECK-NEXT:    ret i64 0
 ;
   %ptr = getelementptr [6 x i8], [6 x i8]* @s5, i32 0, i32 0
   %len = call i64 @strnlen(i8* %ptr, i64 0)
index a58ea6b..2c16d85 100644 (file)
@@ -17,9 +17,7 @@ declare i64 @strnlen(i8*, i64)
 
 define i64 @fold_strnlen_s3_s5_0(i1 %C) {
 ; CHECK-LABEL: @fold_strnlen_s3_s5_0(
-; CHECK-NEXT:    [[PTR:%.*]] = select i1 [[C:%.*]], 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]]
+; CHECK-NEXT:    ret i64 0
 ;
   %ptr = select i1 %C, 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)
 
@@ -32,9 +30,7 @@ define i64 @fold_strnlen_s3_s5_0(i1 %C) {
 
 define i64 @fold_strnlen_s3_s5_1(i1 %C) {
 ; CHECK-LABEL: @fold_strnlen_s3_s5_1(
-; CHECK-NEXT:    [[PTR:%.*]] = select i1 [[C:%.*]], 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* noundef nonnull dereferenceable(1) [[PTR]], i64 1)
-; CHECK-NEXT:    ret i64 [[LEN]]
+; CHECK-NEXT:    ret i64 1
 ;
   %ptr = select i1 %C, 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)
 
index 9d1004a..585a69d 100644 (file)
@@ -18,9 +18,7 @@ declare i64 @strnlen(i8*, i64)
 
 define i64 @fold_strnlen_sx_pi_0(i64 %i) {
 ; CHECK-LABEL: @fold_strnlen_sx_pi_0(
-; CHECK-NEXT:    [[PTR:%.*]] = getelementptr [0 x i8], [0 x i8]* @sx, i64 0, i64 [[I:%.*]]
-; CHECK-NEXT:    [[LEN:%.*]] = call i64 @strnlen(i8* [[PTR]], i64 0)
-; CHECK-NEXT:    ret i64 [[LEN]]
+; CHECK-NEXT:    ret i64 0
 ;
 
   %ptr = getelementptr [0 x i8], [0 x i8]* @sx, i64 0, i64 %i
@@ -78,9 +76,7 @@ define i64 @call_strnlen_a3_pi_3(i64 %i) {
 
 define i64 @fold_strnlen_s3_pi_0(i64 %i) {
 ; CHECK-LABEL: @fold_strnlen_s3_pi_0(
-; CHECK-NEXT:    [[PTR:%.*]] = getelementptr inbounds [4 x i8], [4 x i8]* @s3, i64 0, i64 [[I:%.*]]
-; CHECK-NEXT:    [[LEN:%.*]] = call i64 @strnlen(i8* nonnull [[PTR]], i64 0)
-; CHECK-NEXT:    ret i64 [[LEN]]
+; CHECK-NEXT:    ret i64 0
 ;
   %ptr = getelementptr inbounds [4 x i8], [4 x i8]* @s3, i64 0, i64 %i
   %len = call i64 @strnlen(i8* %ptr, i64 0)
@@ -92,8 +88,7 @@ define i64 @fold_strnlen_s3_pi_0(i64 %i) {
 
 define i64 @call_strnlen_s5_pi_0(i64 zeroext %i) {
 ; 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]]
+; CHECK-NEXT:    ret i64 0
 ;
   %ptr = getelementptr [6 x i8], [6 x i8]* @s5, i32 0, i32 0
   %len = call i64 @strnlen(i8* %ptr, i64 0)
@@ -105,9 +100,7 @@ define i64 @call_strnlen_s5_pi_0(i64 zeroext %i) {
 
 define i64 @fold_strnlen_s5_3_pi_0(i64 zeroext %i) {
 ; CHECK-LABEL: @fold_strnlen_s5_3_pi_0(
-; CHECK-NEXT:    [[PTR:%.*]] = getelementptr [10 x i8], [10 x i8]* @s5_3, i64 0, i64 [[I:%.*]]
-; CHECK-NEXT:    [[LEN:%.*]] = call i64 @strnlen(i8* [[PTR]], i64 0)
-; CHECK-NEXT:    ret i64 [[LEN]]
+; CHECK-NEXT:    ret i64 0
 ;
   %ptr = getelementptr [10 x i8], [10 x i8]* @s5_3, i32 0, i64 %i
   %len = call i64 @strnlen(i8* %ptr, i64 0)
index 125ffda..790ad12 100644 (file)
@@ -15,9 +15,7 @@ declare i64 @strnlen(i8*, i64)
 
 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]]
+; CHECK-NEXT:    ret i1 true
 ;
 
   %ptr = getelementptr [0 x i8], [0 x i8]* @ax, i64 0, i64 0
@@ -31,9 +29,7 @@ define i1 @fold_strnlen_ax_0_eqz() {
 
 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]]
+; CHECK-NEXT:    ret i1 false
 ;
 
   %ptr = getelementptr [0 x i8], [0 x i8]* @ax, i64 0, i64 0
@@ -47,9 +43,9 @@ define i1 @fold_strnlen_ax_0_gtz() {
 
 define i1 @fold_strnlen_ax_1_eqz() {
 ; CHECK-LABEL: @fold_strnlen_ax_1_eqz(
-; CHECK-NEXT:    [[LEN:%.*]] = tail call i64 @strnlen(i8* noundef nonnull dereferenceable(1) 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]]
+; 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 eq i8 [[STRNLEN_CHAR0]], 0
+; CHECK-NEXT:    ret i1 [[STRNLEN_CHAR0CMP_NOT]]
 ;
 
   %ptr = getelementptr [0 x i8], [0 x i8]* @ax, i64 0, i64 0
@@ -63,9 +59,9 @@ define i1 @fold_strnlen_ax_1_eqz() {
 
 define i1 @fold_strnlen_ax_1_neqz() {
 ; CHECK-LABEL: @fold_strnlen_ax_1_neqz(
-; CHECK-NEXT:    [[LEN:%.*]] = tail call i64 @strnlen(i8* noundef nonnull dereferenceable(1) 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]]
+; 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:%.*]] = icmp ne i8 [[STRNLEN_CHAR0]], 0
+; CHECK-NEXT:    ret i1 [[STRNLEN_CHAR0CMP]]
 ;
 
   %ptr = getelementptr [0 x i8], [0 x i8]* @ax, i64 0, i64 0