diff --git a/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h b/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h --- a/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h +++ b/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h @@ -161,6 +161,7 @@ Value *optimizeStrNDup(CallInst *CI, IRBuilderBase &B); Value *optimizeStrCpy(CallInst *CI, IRBuilderBase &B); Value *optimizeStpCpy(CallInst *CI, IRBuilderBase &B); + Value *optimizeStrLCpy(CallInst *CI, IRBuilderBase &B); Value *optimizeStrNCpy(CallInst *CI, IRBuilderBase &B); Value *optimizeStrLen(CallInst *CI, IRBuilderBase &B); Value *optimizeStrNLen(CallInst *CI, IRBuilderBase &B); diff --git a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp --- a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp @@ -688,6 +688,79 @@ return DstEnd; } +// Optimize a call to size_t strlcpy(char*, const char*, size_t). + +Value *LibCallSimplifier::optimizeStrLCpy(CallInst *CI, IRBuilderBase &B) { + Value *Size = CI->getArgOperand(2); + if (isKnownNonZero(Size, DL)) { + // Like snprintf, the function stores into the destination only when + // the size argument is nonzero. + annotateNonNullNoUndefBasedOnAccess(CI, 0); + annotateNonNullNoUndefBasedOnAccess(CI, 1); + } + + uint64_t NBytes; + if (ConstantInt *SizeC = dyn_cast(Size)) + NBytes = SizeC->getZExtValue(); + else + return nullptr; + + Value *Dst = CI->getArgOperand(0); + Value *Src = CI->getArgOperand(1); + if (NBytes <= 1) { + if (NBytes == 1) + // For a call to strlcpy(D, S, 1) first store the nul in *D. + B.CreateStore(B.getInt8(0), Dst); + + // Transform strlcpy(D, S, 0) to a call to strlen(S). + return copyFlags(*CI, emitStrLen(Src, B, DL, TLI)); + } + + // Try to determine the length of the source string. + uint64_t SrcLen = GetStringLength(Src); + if (!SrcLen--) // Unbias length. + return nullptr; + + // Set if the source string is copied including the terminating nul. + bool NulTerm = false; + if (NBytes > SrcLen + 1) { + NBytes = SrcLen + 1; + NulTerm = true; + } + else + --NBytes; + + if (SrcLen == 0) { + // Transform strlcpy(D, "", N) to (*D = '\0, 0). + B.CreateStore(B.getInt8(0), Dst); + return ConstantInt::get(CI->getType(), 0); + } + + Function *Callee = CI->getCalledFunction(); + Type *PT = Callee->getFunctionType()->getParamType(0); + // Transform strlcpy(D, S, N) to memcpy(D, S, N') where N' is the lower + // bound on strlen(S) + 1 and N, optionally followed by a nul store to + // D[N' - 1] if necessary. + CallInst *NewCI = B.CreateMemCpy(Dst, Align(1), Src, Align(1), + ConstantInt::get(DL.getIntPtrType(PT), NBytes)); + NewCI->setAttributes(CI->getAttributes()); + NewCI->removeRetAttrs(AttributeFuncs::typeIncompatible(NewCI->getType())); + copyFlags(*CI, NewCI); + + if (!NulTerm) { + Value *EndOff = ConstantInt::get(CI->getType(), NBytes); + Value *EndPtr = B.CreateInBoundsGEP(B.getInt8Ty(), Dst, EndOff); + B.CreateStore(B.getInt8(0), EndPtr); + } + + // Like snprintf, strlcpy returns the number of nonzero bytes that would + // have been copied if the bound had been sufficiently big (which in this + // case is strlen(Src)). + return ConstantInt::get(CI->getType(), SrcLen); +} + +// Optimize a call to strncpy. + Value *LibCallSimplifier::optimizeStrNCpy(CallInst *CI, IRBuilderBase &B) { Function *Callee = CI->getCalledFunction(); Value *Dst = CI->getArgOperand(0); @@ -3194,6 +3267,8 @@ return optimizeStrCpy(CI, Builder); case LibFunc_stpcpy: return optimizeStpCpy(CI, Builder); + case LibFunc_strlcpy: + return optimizeStrLCpy(CI, Builder); case LibFunc_strncpy: return optimizeStrNCpy(CI, Builder); case LibFunc_strlen: diff --git a/llvm/test/Transforms/InstCombine/fortify-folding.ll b/llvm/test/Transforms/InstCombine/fortify-folding.ll --- a/llvm/test/Transforms/InstCombine/fortify-folding.ll +++ b/llvm/test/Transforms/InstCombine/fortify-folding.ll @@ -246,7 +246,7 @@ define i64 @test_strlcpy() { ; CHECK-LABEL: @test_strlcpy( -; CHECK-NEXT: [[STRLCPY:%.*]] = call i64 @strlcpy(i8* getelementptr inbounds ([60 x i8], [60 x i8]* @a, i64 0, i64 0), i8* getelementptr inbounds ([60 x i8], [60 x i8]* @b, i64 0, i64 0), i64 22) +; CHECK-NEXT: [[STRLCPY:%.*]] = call i64 @strlcpy(i8* noundef nonnull dereferenceable(1) getelementptr inbounds ([60 x i8], [60 x i8]* @a, i64 0, i64 0), i8* noundef nonnull dereferenceable(1) getelementptr inbounds ([60 x i8], [60 x i8]* @b, i64 0, i64 0), i64 22) ; CHECK-NEXT: ret i64 [[STRLCPY]] ; %dst = getelementptr inbounds [60 x i8], [60 x i8]* @a, i32 0, i32 0 @@ -268,7 +268,7 @@ define i64 @test_strlcpy_tail() { ; CHECK-LABEL: @test_strlcpy_tail( -; CHECK-NEXT: [[STRLCPY:%.*]] = tail call i64 @strlcpy(i8* getelementptr inbounds ([60 x i8], [60 x i8]* @a, i64 0, i64 0), i8* getelementptr inbounds ([60 x i8], [60 x i8]* @b, i64 0, i64 0), i64 22) +; CHECK-NEXT: [[STRLCPY:%.*]] = tail call i64 @strlcpy(i8* noundef nonnull dereferenceable(1) getelementptr inbounds ([60 x i8], [60 x i8]* @a, i64 0, i64 0), i8* noundef nonnull dereferenceable(1) getelementptr inbounds ([60 x i8], [60 x i8]* @b, i64 0, i64 0), i64 22) ; CHECK-NEXT: ret i64 [[STRLCPY]] ; %dst = getelementptr inbounds [60 x i8], [60 x i8]* @a, i32 0, i32 0 diff --git a/llvm/test/Transforms/InstCombine/strlcpy-1.ll b/llvm/test/Transforms/InstCombine/strlcpy-1.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/strlcpy-1.ll @@ -0,0 +1,324 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; Test that the strncpy library call simplifier works correctly. +; +; RUN: opt < %s -data-layout="E" -passes=instcombine -S | FileCheck %s --check-prefixes=ANY,BE +; RUN: opt < %s -data-layout="e" -passes=instcombine -S | FileCheck %s --check-prefixes=ANY,LE + +declare i64 @strlcpy(i8*, i8*, i64) + +@s4 = constant [5 x i8] c"1234\00" + + +; Verify that strlcpy(D, "", N) calls are transformed to a nul store +; to *D for nonzero N and folded to zero for all values of N. + +define void @fold_strlcpy_s0(i8* %dst, i64* %pn) { +; ANY-LABEL: @fold_strlcpy_s0( +; ANY-NEXT: store i64 0, i64* [[PN:%.*]], align 4 +; ANY-NEXT: store i8 0, i8* [[DST:%.*]], align 1 +; ANY-NEXT: [[PNS0_1:%.*]] = getelementptr i64, i64* [[PN]], i64 1 +; ANY-NEXT: store i64 0, i64* [[PNS0_1]], align 4 +; ANY-NEXT: store i8 0, i8* [[DST]], align 1 +; ANY-NEXT: [[PNS0_M1:%.*]] = getelementptr i64, i64* [[PN]], i64 2 +; ANY-NEXT: store i64 0, i64* [[PNS0_M1]], align 4 +; ANY-NEXT: ret void +; + %ps0 = getelementptr [5 x i8], [5 x i8]* @s4, i32 0, i32 4 + +; Fold strlcpy(D, "", 0) to just 0. + %ns0_0 = call i64 @strlcpy(i8* %dst, i8* %ps0, i64 0) + %pns0_0 = getelementptr i64, i64* %pn, i64 0 + store i64 %ns0_0, i64* %pns0_0 + +; Transform strlcpy(D, "", 1) to *D = '\0, 0. + %ns0_1 = call i64 @strlcpy(i8* %dst, i8* %ps0, i64 1) + %pns0_1 = getelementptr i64, i64* %pn, i64 1 + store i64 %ns0_1, i64* %pns0_1 + +; Transform strlcpy(D, "", SIZE_MAX) to *D = '\0, 0. + %ns0_m1 = call i64 @strlcpy(i8* %dst, i8* %ps0, i64 -1) + %pns0_m1 = getelementptr i64, i64* %pn, i64 2 + store i64 %ns0_m1, i64* %pns0_m1 + + ret void +} + + +; Verify that strlcpy(D, "4", N) calls are transformed to a store to +; D[0] for nonzero N (and a nul store to D[1] for N greater than 1) +; and folded to 1 for all values of N. + +define void @fold_strlcpy_s1(i8* %dst, i64* %pn) { +; BE-LABEL: @fold_strlcpy_s1( +; BE-NEXT: store i64 1, i64* [[PN:%.*]], align 4 +; BE-NEXT: store i8 0, i8* [[DST:%.*]], align 1 +; BE-NEXT: [[PNS1_1:%.*]] = getelementptr i64, i64* [[PN]], i64 1 +; BE-NEXT: store i64 1, i64* [[PNS1_1]], align 4 +; BE-NEXT: store i8 52, i8* [[DST]], align 1 +; BE-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 1 +; BE-NEXT: store i8 0, i8* [[TMP1]], align 1 +; BE-NEXT: [[PNS1_2:%.*]] = getelementptr i64, i64* [[PN]], i64 2 +; BE-NEXT: store i64 1, i64* [[PNS1_2]], align 4 +; BE-NEXT: [[TMP2:%.*]] = bitcast i8* [[DST]] to i16* +; BE-NEXT: store i16 13312, i16* [[TMP2]], align 1 +; BE-NEXT: [[PNS1_3:%.*]] = getelementptr i64, i64* [[PN]], i64 3 +; BE-NEXT: store i64 1, i64* [[PNS1_3]], align 4 +; BE-NEXT: [[TMP3:%.*]] = bitcast i8* [[DST]] to i16* +; BE-NEXT: store i16 13312, i16* [[TMP3]], align 1 +; BE-NEXT: [[PNS1_M1:%.*]] = getelementptr i64, i64* [[PN]], i64 4 +; BE-NEXT: store i64 1, i64* [[PNS1_M1]], align 4 +; BE-NEXT: ret void +; +; LE-LABEL: @fold_strlcpy_s1( +; LE-NEXT: store i64 1, i64* [[PN:%.*]], align 4 +; LE-NEXT: store i8 0, i8* [[DST:%.*]], align 1 +; LE-NEXT: [[PNS1_1:%.*]] = getelementptr i64, i64* [[PN]], i64 1 +; LE-NEXT: store i64 1, i64* [[PNS1_1]], align 4 +; LE-NEXT: store i8 52, i8* [[DST]], align 1 +; LE-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 1 +; LE-NEXT: store i8 0, i8* [[TMP1]], align 1 +; LE-NEXT: [[PNS1_2:%.*]] = getelementptr i64, i64* [[PN]], i64 2 +; LE-NEXT: store i64 1, i64* [[PNS1_2]], align 4 +; LE-NEXT: [[TMP2:%.*]] = bitcast i8* [[DST]] to i16* +; LE-NEXT: store i16 52, i16* [[TMP2]], align 1 +; LE-NEXT: [[PNS1_3:%.*]] = getelementptr i64, i64* [[PN]], i64 3 +; LE-NEXT: store i64 1, i64* [[PNS1_3]], align 4 +; LE-NEXT: [[TMP3:%.*]] = bitcast i8* [[DST]] to i16* +; LE-NEXT: store i16 52, i16* [[TMP3]], align 1 +; LE-NEXT: [[PNS1_M1:%.*]] = getelementptr i64, i64* [[PN]], i64 4 +; LE-NEXT: store i64 1, i64* [[PNS1_M1]], align 4 +; LE-NEXT: ret void +; + +; Fold strlcpy(D, "4", 0) to 1. + %ps1 = getelementptr [5 x i8], [5 x i8]* @s4, i32 0, i32 3 + %ns1_0 = call i64 @strlcpy(i8* %dst, i8* %ps1, i64 0) + %pns1_0 = getelementptr i64, i64* %pn, i32 0 + store i64 %ns1_0, i64* %pns1_0 + +; Transform strlcpy(D, "4", 1) to *D = '\0', 1. + %ns1_1 = call i64 @strlcpy(i8* %dst, i8* %ps1, i64 1) + %pns1_1 = getelementptr i64, i64* %pn, i32 1 + store i64 %ns1_1, i64* %pns1_1 + +; Transform strlcpy(D, "4", 2) to D[0] = '\4, D[1] = '\0', 1. + %ns1_2 = call i64 @strlcpy(i8* %dst, i8* %ps1, i64 2) + %pns1_2 = getelementptr i64, i64* %pn, i32 2 + store i64 %ns1_2, i64* %pns1_2 + +; Transform strlcpy(D, "4", 3) to D[0] = '\4, D[1] = '\0', 1.. + %ns1_3 = call i64 @strlcpy(i8* %dst, i8* %ps1, i64 3) + %pns1_3 = getelementptr i64, i64* %pn, i32 3 + store i64 %ns1_3, i64* %pns1_3 + +; Transform strlcpy(D, "4", SIZE_MAX) to D[0] = '\4, D[1] = '\0', 1. + %ns1_m1 = call i64 @strlcpy(i8* %dst, i8* %ps1, i64 -1) + %pns1_m1 = getelementptr i64, i64* %pn, i32 4 + store i64 %ns1_m1, i64* %pns1_m1 + + ret void +} + + +; Verify that strlcpy(D, "1234", N) calls are transformed to a copy of +; the N - 1 leading characters of the string to D and folded to 4 for +; all values of N. + +define void @fold_strlcpy_s5(i8* %dst, i64* %pn) { +; BE-LABEL: @fold_strlcpy_s5( +; BE-NEXT: store i64 4, i64* [[PN:%.*]], align 4 +; BE-NEXT: store i8 0, i8* [[DST:%.*]], align 1 +; BE-NEXT: [[PNS4_1:%.*]] = getelementptr i64, i64* [[PN]], i64 1 +; BE-NEXT: store i64 4, i64* [[PNS4_1]], align 4 +; BE-NEXT: store i8 49, i8* [[DST]], align 1 +; BE-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 1 +; BE-NEXT: store i8 0, i8* [[TMP1]], align 1 +; BE-NEXT: [[PNS4_2:%.*]] = getelementptr i64, i64* [[PN]], i64 2 +; BE-NEXT: store i64 4, i64* [[PNS4_2]], align 4 +; BE-NEXT: [[TMP2:%.*]] = bitcast i8* [[DST]] to i16* +; BE-NEXT: store i16 12594, i16* [[TMP2]], align 1 +; BE-NEXT: [[TMP3:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 2 +; BE-NEXT: store i8 0, i8* [[TMP3]], align 1 +; BE-NEXT: [[PNS4_3:%.*]] = getelementptr i64, i64* [[PN]], i64 3 +; BE-NEXT: store i64 4, i64* [[PNS4_3]], align 4 +; BE-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 1 dereferenceable(3) [[DST]], i8* noundef nonnull align 1 dereferenceable(3) getelementptr inbounds ([5 x i8], [5 x i8]* @s4, i64 0, i64 0), i64 3, i1 false) +; BE-NEXT: [[TMP4:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 3 +; BE-NEXT: store i8 0, i8* [[TMP4]], align 1 +; BE-NEXT: [[PNS4_4:%.*]] = getelementptr i64, i64* [[PN]], i64 4 +; BE-NEXT: store i64 4, i64* [[PNS4_4]], align 4 +; BE-NEXT: [[TMP5:%.*]] = bitcast i8* [[DST]] to i32* +; BE-NEXT: store i32 825373492, i32* [[TMP5]], align 1 +; BE-NEXT: [[TMP6:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 4 +; BE-NEXT: store i8 0, i8* [[TMP6]], align 1 +; BE-NEXT: [[PNS4_5:%.*]] = getelementptr i64, i64* [[PN]], i64 5 +; BE-NEXT: store i64 4, i64* [[PNS4_5]], align 4 +; BE-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 1 dereferenceable(5) [[DST]], i8* noundef nonnull align 1 dereferenceable(5) getelementptr inbounds ([5 x i8], [5 x i8]* @s4, i64 0, i64 0), i64 5, i1 false) +; BE-NEXT: [[PNS4_M1:%.*]] = getelementptr i64, i64* [[PN]], i64 2 +; BE-NEXT: store i64 4, i64* [[PNS4_M1]], align 4 +; BE-NEXT: ret void +; +; LE-LABEL: @fold_strlcpy_s5( +; LE-NEXT: store i64 4, i64* [[PN:%.*]], align 4 +; LE-NEXT: store i8 0, i8* [[DST:%.*]], align 1 +; LE-NEXT: [[PNS4_1:%.*]] = getelementptr i64, i64* [[PN]], i64 1 +; LE-NEXT: store i64 4, i64* [[PNS4_1]], align 4 +; LE-NEXT: store i8 49, i8* [[DST]], align 1 +; LE-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 1 +; LE-NEXT: store i8 0, i8* [[TMP1]], align 1 +; LE-NEXT: [[PNS4_2:%.*]] = getelementptr i64, i64* [[PN]], i64 2 +; LE-NEXT: store i64 4, i64* [[PNS4_2]], align 4 +; LE-NEXT: [[TMP2:%.*]] = bitcast i8* [[DST]] to i16* +; LE-NEXT: store i16 12849, i16* [[TMP2]], align 1 +; LE-NEXT: [[TMP3:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 2 +; LE-NEXT: store i8 0, i8* [[TMP3]], align 1 +; LE-NEXT: [[PNS4_3:%.*]] = getelementptr i64, i64* [[PN]], i64 3 +; LE-NEXT: store i64 4, i64* [[PNS4_3]], align 4 +; LE-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 1 dereferenceable(3) [[DST]], i8* noundef nonnull align 1 dereferenceable(3) getelementptr inbounds ([5 x i8], [5 x i8]* @s4, i64 0, i64 0), i64 3, i1 false) +; LE-NEXT: [[TMP4:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 3 +; LE-NEXT: store i8 0, i8* [[TMP4]], align 1 +; LE-NEXT: [[PNS4_4:%.*]] = getelementptr i64, i64* [[PN]], i64 4 +; LE-NEXT: store i64 4, i64* [[PNS4_4]], align 4 +; LE-NEXT: [[TMP5:%.*]] = bitcast i8* [[DST]] to i32* +; LE-NEXT: store i32 875770417, i32* [[TMP5]], align 1 +; LE-NEXT: [[TMP6:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 4 +; LE-NEXT: store i8 0, i8* [[TMP6]], align 1 +; LE-NEXT: [[PNS4_5:%.*]] = getelementptr i64, i64* [[PN]], i64 5 +; LE-NEXT: store i64 4, i64* [[PNS4_5]], align 4 +; LE-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 1 dereferenceable(5) [[DST]], i8* noundef nonnull align 1 dereferenceable(5) getelementptr inbounds ([5 x i8], [5 x i8]* @s4, i64 0, i64 0), i64 5, i1 false) +; LE-NEXT: [[PNS4_M1:%.*]] = getelementptr i64, i64* [[PN]], i64 2 +; LE-NEXT: store i64 4, i64* [[PNS4_M1]], align 4 +; LE-NEXT: ret void +; + %ps4 = getelementptr [5 x i8], [5 x i8]* @s4, i32 0, i32 0 + +; Fold strlcpy(D, "1234", 0) to 4. + %ns4_0 = call i64 @strlcpy(i8* %dst, i8* %ps4, i64 0) + %pns4_0 = getelementptr i64, i64* %pn, i32 0 + store i64 %ns4_0, i64* %pns4_0 + +; Transform strlcpy(D, "1234", 1) to *D = '\0', 4. + %ns4_1 = call i64 @strlcpy(i8* %dst, i8* %ps4, i64 1) + %pns4_1 = getelementptr i64, i64* %pn, i32 1 + store i64 %ns4_1, i64* %pns4_1 + +; Transform strlcpy(D, "1234", 2) to D[0] = '1', D[1] = '\0', 4. + %ns4_2 = call i64 @strlcpy(i8* %dst, i8* %ps4, i64 2) + %pns4_2 = getelementptr i64, i64* %pn, i32 2 + store i64 %ns4_2, i64* %pns4_2 + +; Transform strlcpy(D, S="1234", 3) to memcpy(D, S, 2), D[2] = '\0', 4. + %ns4_3 = call i64 @strlcpy(i8* %dst, i8* %ps4, i64 3) + %pns4_3 = getelementptr i64, i64* %pn, i32 3 + store i64 %ns4_3, i64* %pns4_3 + +; Transform strlcpy(D, S="1234", 4) to memcpy(D, S, 3), D[3] = '\0', 4. + %ns4_4 = call i64 @strlcpy(i8* %dst, i8* %ps4, i64 4) + %pns4_4 = getelementptr i64, i64* %pn, i32 4 + store i64 %ns4_4, i64* %pns4_4 + +; Transform strlcpy(D, S="1234", 5) to memcpy(D, S, 5), 4. + %ns4_5 = call i64 @strlcpy(i8* %dst, i8* %ps4, i64 5) + %pns4_5 = getelementptr i64, i64* %pn, i32 5 + store i64 %ns4_5, i64* %pns4_5 + +; Transform strlcpy(D, S="1234", SIZE_MAX) to memcpy(D, S, 5), 4. + %ns4_m1 = call i64 @strlcpy(i8* %dst, i8* %ps4, i64 -1) + %pns4_m1 = getelementptr i64, i64* %pn, i32 2 + store i64 %ns4_m1, i64* %pns4_m1 + + ret void +} + +; Verify that strlcpy(D, S, 1) calls are transformed into a nul store +; to *D, strlcpy(D, S, 0) to a no-op, and the result of both folded +; to strlen(S). + +define void @fold_strlcpy_s_0(i8* %dst, i8* %s, i64 %n, i64* %pn) { +; ANY-LABEL: @fold_strlcpy_s_0( +; ANY-NEXT: store i8 0, i8* [[DST:%.*]], align 1 +; ANY-NEXT: [[STRLEN:%.*]] = call i64 @strlen(i8* noundef nonnull dereferenceable(1) [[S:%.*]]) +; ANY-NEXT: store i64 [[STRLEN]], i64* [[PN:%.*]], align 4 +; ANY-NEXT: [[STRLEN1:%.*]] = call i64 @strlen(i8* noundef nonnull dereferenceable(1) [[S]]) +; ANY-NEXT: [[PNS_0:%.*]] = getelementptr i64, i64* [[PN]], i64 1 +; ANY-NEXT: store i64 [[STRLEN1]], i64* [[PNS_0]], align 4 +; ANY-NEXT: [[STRLEN2:%.*]] = call i64 @strlen(i8* noundef nonnull dereferenceable(1) [[S]]) +; ANY-NEXT: [[PN0_S_0:%.*]] = getelementptr i64, i64* [[PN]], i64 2 +; ANY-NEXT: store i64 [[STRLEN2]], i64* [[PN0_S_0]], align 4 +; ANY-NEXT: ret void +; + %ns_1 = call i64 @strlcpy(i8* %dst, i8* %s, i64 1) + %pns_1 = getelementptr i64, i64* %pn, i32 0 + store i64 %ns_1, i64* %pns_1 + + %ns_0 = call i64 @strlcpy(i8* %dst, i8* %s, i64 0) + %pns_0 = getelementptr i64, i64* %pn, i32 1 + store i64 %ns_0, i64* %pns_0 + + ; Verify that calling strlcpy with a null destination is also folded + ; (to match a possible extension of some implementations that emulate + ; snprintf(0, 0, "%s", S)). + %n0_s_0 = call i64 @strlcpy(i8* null, i8* %s, i64 0) + %pn0_s_0 = getelementptr i64, i64* %pn, i32 2 + store i64 %n0_s_0, i64* %pn0_s_0 + + ret void +} + + +; Verify that strlcpy(D, S, N) calls are left alone when S and/or N are +; not known (except for the cases handled above). Also verify that they +; are annotated with the dereferenceable attribute only with nonzero N. + +define void @call_strlcpy_s0_n(i8* %dst, i8* %s, i64 %n, i64* %pn) { +; ANY-LABEL: @call_strlcpy_s0_n( +; ANY-NEXT: [[NS_2:%.*]] = call i64 @strlcpy(i8* noundef nonnull dereferenceable(1) [[DST:%.*]], i8* noundef nonnull dereferenceable(1) [[S:%.*]], i64 2) +; ANY-NEXT: store i64 [[NS_2]], i64* [[PN:%.*]], align 4 +; ANY-NEXT: [[NS_N:%.*]] = call i64 @strlcpy(i8* [[DST]], i8* [[S]], i64 [[N:%.*]]) +; ANY-NEXT: [[PNS_N:%.*]] = getelementptr i64, i64* [[PN]], i64 1 +; ANY-NEXT: store i64 [[NS_N]], i64* [[PNS_N]], align 4 +; ANY-NEXT: [[NZ:%.*]] = or i64 [[N]], 1 +; ANY-NEXT: [[NS_NZ:%.*]] = call i64 @strlcpy(i8* noundef nonnull dereferenceable(1) [[DST]], i8* noundef nonnull dereferenceable(1) [[S]], i64 [[NZ]]) +; ANY-NEXT: [[PNS_NZ:%.*]] = getelementptr i64, i64* [[PN]], i64 2 +; ANY-NEXT: store i64 [[NS_NZ]], i64* [[PNS_NZ]], align 4 +; ANY-NEXT: [[NS0_N:%.*]] = call i64 @strlcpy(i8* [[DST]], i8* getelementptr inbounds ([5 x i8], [5 x i8]* @s4, i64 0, i64 4), i64 [[N]]) +; ANY-NEXT: [[PNS0_N:%.*]] = getelementptr i64, i64* [[PN]], i64 3 +; ANY-NEXT: store i64 [[NS0_N]], i64* [[PNS0_N]], align 4 +; ANY-NEXT: [[NS1_N:%.*]] = call i64 @strlcpy(i8* [[DST]], i8* getelementptr inbounds ([5 x i8], [5 x i8]* @s4, i64 0, i64 3), i64 [[N]]) +; ANY-NEXT: [[PNS1_N:%.*]] = getelementptr i64, i64* [[PN]], i64 4 +; ANY-NEXT: store i64 [[NS1_N]], i64* [[PNS1_N]], align 4 +; ANY-NEXT: [[NS4_N:%.*]] = call i64 @strlcpy(i8* [[DST]], i8* getelementptr inbounds ([5 x i8], [5 x i8]* @s4, i64 0, i64 0), i64 [[N]]) +; ANY-NEXT: [[PNS4_N:%.*]] = getelementptr i64, i64* [[PN]], i64 5 +; ANY-NEXT: store i64 [[NS4_N]], i64* [[PNS4_N]], align 4 +; ANY-NEXT: ret void +; + %ns_2 = call i64 @strlcpy(i8* %dst, i8* %s, i64 2) + %pns_2 = getelementptr i64, i64* %pn, i32 0 + store i64 %ns_2, i64* %pns_2 + + %ns_n = call i64 @strlcpy(i8* %dst, i8* %s, i64 %n) + %pns_n = getelementptr i64, i64* %pn, i32 1 + store i64 %ns_n, i64* %pns_n + + %nz = or i64 %n, 1 + %ns_nz = call i64 @strlcpy(i8* %dst, i8* %s, i64 %nz) + %pns_nz = getelementptr i64, i64* %pn, i32 2 + store i64 %ns_nz, i64* %pns_nz + + + %ps0 = getelementptr [5 x i8], [5 x i8]* @s4, i32 0, i32 4 + %ns0_n = call i64 @strlcpy(i8* %dst, i8* %ps0, i64 %n) + %pns0_n = getelementptr i64, i64* %pn, i32 3 + store i64 %ns0_n, i64* %pns0_n + + %ps1 = getelementptr [5 x i8], [5 x i8]* @s4, i32 0, i32 3 + %ns1_n = call i64 @strlcpy(i8* %dst, i8* %ps1, i64 %n) + %pns1_n = getelementptr i64, i64* %pn, i32 4 + store i64 %ns1_n, i64* %pns1_n + + %ps4 = getelementptr [5 x i8], [5 x i8]* @s4, i32 0, i32 0 + %ns4_n = call i64 @strlcpy(i8* %dst, i8* %ps4, i64 %n) + %pns4_n = getelementptr i64, i64* %pn, i32 5 + store i64 %ns4_n, i64* %pns4_n + + ret void +}