Index: llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h =================================================================== --- llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h +++ llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h @@ -237,8 +237,9 @@ /// 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: llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp =================================================================== --- llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp +++ llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp @@ -629,9 +629,29 @@ } 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(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::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: llvm/test/Transforms/InstCombine/strnlen-1.ll =================================================================== --- llvm/test/Transforms/InstCombine/strnlen-1.ll +++ llvm/test/Transforms/InstCombine/strnlen-1.ll @@ -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 @@ } +; 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: llvm/test/Transforms/InstCombine/strnlen-2.ll =================================================================== --- llvm/test/Transforms/InstCombine/strnlen-2.ll +++ llvm/test/Transforms/InstCombine/strnlen-2.ll @@ -17,9 +17,7 @@ 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_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) @@ -92,9 +88,8 @@ define i64 @fold_strnlen_s5_6(i1 %C) { ; CHECK-LABEL: @fold_strnlen_s5_6( -; CHECK-NEXT: [[PTR:%.*]] = select i1 [[C:%.*]], 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* noundef nonnull dereferenceable(1) [[PTR]], i64 6) -; CHECK-NEXT: ret i64 [[LEN]] +; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[C:%.*]], i64 3, i64 5 +; CHECK-NEXT: ret i64 [[TMP1]] ; %ptr = select i1 %C, 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) Index: llvm/test/Transforms/InstCombine/strnlen-3.ll =================================================================== --- llvm/test/Transforms/InstCombine/strnlen-3.ll +++ llvm/test/Transforms/InstCombine/strnlen-3.ll @@ -18,9 +18,7 @@ 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 @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 @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 @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: llvm/test/Transforms/InstCombine/strnlen-5.ll =================================================================== --- llvm/test/Transforms/InstCombine/strnlen-5.ll +++ llvm/test/Transforms/InstCombine/strnlen-5.ll @@ -15,9 +15,7 @@ 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_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_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 ne 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_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_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