[llvm] 345514e - [InstCombine] Add support for strlcpy folding
Martin Sebor via llvm-commits
llvm-commits at lists.llvm.org
Tue Aug 16 15:44:24 PDT 2022
Author: Martin Sebor
Date: 2022-08-16T16:43:40-06:00
New Revision: 345514e991ae854c38efd848dfd2e3eee03897df
URL: https://github.com/llvm/llvm-project/commit/345514e991ae854c38efd848dfd2e3eee03897df
DIFF: https://github.com/llvm/llvm-project/commit/345514e991ae854c38efd848dfd2e3eee03897df.diff
LOG: [InstCombine] Add support for strlcpy folding
Reviewed By: efriedma
Differential Revision: https://reviews.llvm.org/D130666
Added:
llvm/test/Transforms/InstCombine/strlcpy-1.ll
Modified:
llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h
llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
llvm/test/Transforms/InstCombine/fortify-folding.ll
Removed:
################################################################################
diff --git a/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h b/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h
index f0187fb20adda..2817f2bcfbb9f 100644
--- a/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h
+++ b/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h
@@ -161,6 +161,7 @@ class LibCallSimplifier {
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
index 30f95cddff737..94ce41ac19369 100644
--- a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
@@ -692,6 +692,89 @@ Value *LibCallSimplifier::optimizeStpCpy(CallInst *CI, IRBuilderBase &B) {
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);
+ // The function reads the source argument regardless of Size (it returns
+ // its length).
+ annotateNonNullNoUndefBasedOnAccess(CI, 1);
+
+ uint64_t NBytes;
+ if (ConstantInt *SizeC = dyn_cast<ConstantInt>(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 a 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, substituting its size
+ // when it's not nul-terminated (as it's required to be) to avoid
+ // reading past its end.
+ StringRef Str;
+ if (!getConstantStringInfo(Src, Str, 0, /*TrimAtNul=*/false))
+ return nullptr;
+
+ uint64_t SrcLen = Str.find('\0');
+ // Set if the terminating nul should be copied by the call to memcpy
+ // below.
+ bool NulTerm = SrcLen < NBytes;
+
+ if (NulTerm)
+ // Overwrite NBytes with the number of bytes to copy, including
+ // the terminating nul.
+ NBytes = SrcLen + 1;
+ else {
+ // Set the length of the source for the function to return to its
+ // size, and cap NBytes at the same.
+ SrcLen = std::min(SrcLen, Str.size());
+ NBytes = std::min(NBytes - 1, SrcLen);
+ }
+
+ 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);
@@ -3264,6 +3347,8 @@ Value *LibCallSimplifier::optimizeStringMemoryLibCall(CallInst *CI,
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
index ee51186aa6051..f5e761b01fd4c 100644
--- a/llvm/test/Transforms/InstCombine/fortify-folding.ll
+++ b/llvm/test/Transforms/InstCombine/fortify-folding.ll
@@ -246,7 +246,7 @@ define i8* @test_strncat_tail() {
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_not_strlcpy() {
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
index 0000000000000..a8ddcac2ca110
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/strlcpy-1.ll
@@ -0,0 +1,357 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; Assertions have been autogenerated by utils/update_test_checks.py
+;
+; 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
+;
+; Test that the strncpy library call simplifier works correctly.
+
+declare i64 @strlcpy(i8*, i8*, i64)
+
+declare void @sink(i8*, i64)
+
+
+ at 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) {
+; ANY-LABEL: @fold_strlcpy_s0(
+; ANY-NEXT: call void @sink(i8* [[DST:%.*]], i64 0)
+; ANY-NEXT: store i8 0, i8* [[DST]], align 1
+; ANY-NEXT: call void @sink(i8* nonnull [[DST]], i64 0)
+; ANY-NEXT: store i8 0, i8* [[DST]], align 1
+; ANY-NEXT: call void @sink(i8* nonnull [[DST]], i64 0)
+; 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)
+ call void @sink(i8* %dst, i64 %ns0_0)
+
+; Transform strlcpy(D, "", 1) to *D = '\0, 0.
+ %ns0_1 = call i64 @strlcpy(i8* %dst, i8* %ps0, i64 1)
+ call void @sink(i8* %dst, i64 %ns0_1)
+
+; Transform strlcpy(D, "", SIZE_MAX) to *D = '\0, 0.
+ %ns0_m1 = call i64 @strlcpy(i8* %dst, i8* %ps0, i64 -1)
+ call void @sink(i8* %dst, i64 %ns0_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) {
+; BE-LABEL: @fold_strlcpy_s1(
+; BE-NEXT: call void @sink(i8* [[DST:%.*]], i64 1)
+; BE-NEXT: store i8 0, i8* [[DST]], align 1
+; BE-NEXT: call void @sink(i8* nonnull [[DST]], i64 1)
+; BE-NEXT: [[TMP1:%.*]] = bitcast i8* [[DST]] to i16*
+; BE-NEXT: store i16 13312, i16* [[TMP1]], align 1
+; BE-NEXT: call void @sink(i8* nonnull [[DST]], i64 1)
+; BE-NEXT: [[TMP2:%.*]] = bitcast i8* [[DST]] to i16*
+; BE-NEXT: store i16 13312, i16* [[TMP2]], align 1
+; BE-NEXT: call void @sink(i8* nonnull [[DST]], i64 1)
+; BE-NEXT: [[TMP3:%.*]] = bitcast i8* [[DST]] to i16*
+; BE-NEXT: store i16 13312, i16* [[TMP3]], align 1
+; BE-NEXT: call void @sink(i8* nonnull [[DST]], i64 1)
+; BE-NEXT: ret void
+;
+; LE-LABEL: @fold_strlcpy_s1(
+; LE-NEXT: call void @sink(i8* [[DST:%.*]], i64 1)
+; LE-NEXT: store i8 0, i8* [[DST]], align 1
+; LE-NEXT: call void @sink(i8* nonnull [[DST]], i64 1)
+; LE-NEXT: [[TMP1:%.*]] = bitcast i8* [[DST]] to i16*
+; LE-NEXT: store i16 52, i16* [[TMP1]], align 1
+; LE-NEXT: call void @sink(i8* nonnull [[DST]], i64 1)
+; LE-NEXT: [[TMP2:%.*]] = bitcast i8* [[DST]] to i16*
+; LE-NEXT: store i16 52, i16* [[TMP2]], align 1
+; LE-NEXT: call void @sink(i8* nonnull [[DST]], i64 1)
+; LE-NEXT: [[TMP3:%.*]] = bitcast i8* [[DST]] to i16*
+; LE-NEXT: store i16 52, i16* [[TMP3]], align 1
+; LE-NEXT: call void @sink(i8* nonnull [[DST]], i64 1)
+; LE-NEXT: ret void
+;
+ %ps1 = getelementptr [5 x i8], [5 x i8]* @s4, i32 0, i32 3
+
+; Fold strlcpy(D, "4", 0) to 1.
+ %ns1_0 = call i64 @strlcpy(i8* %dst, i8* %ps1, i64 0)
+ call void @sink(i8* %dst, i64 %ns1_0)
+
+; Transform strlcpy(D, "4", 1) to *D = '\0', 1.
+ %ns1_1 = call i64 @strlcpy(i8* %dst, i8* %ps1, i64 1)
+ call void @sink(i8* %dst, i64 %ns1_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)
+ call void @sink(i8* %dst, i64 %ns1_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)
+ call void @sink(i8* %dst, i64 %ns1_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)
+ call void @sink(i8* %dst, i64 %ns1_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) {
+; BE-LABEL: @fold_strlcpy_s5(
+; BE-NEXT: call void @sink(i8* [[DST:%.*]], i64 4)
+; BE-NEXT: store i8 0, i8* [[DST]], align 1
+; BE-NEXT: call void @sink(i8* nonnull [[DST]], i64 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: call void @sink(i8* nonnull [[DST]], i64 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: call void @sink(i8* nonnull [[DST]], i64 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: call void @sink(i8* nonnull [[DST]], i64 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: call void @sink(i8* nonnull [[DST]], i64 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: call void @sink(i8* nonnull [[DST]], i64 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: call void @sink(i8* nonnull [[DST]], i64 4)
+; BE-NEXT: ret void
+;
+; LE-LABEL: @fold_strlcpy_s5(
+; LE-NEXT: call void @sink(i8* [[DST:%.*]], i64 4)
+; LE-NEXT: store i8 0, i8* [[DST]], align 1
+; LE-NEXT: call void @sink(i8* nonnull [[DST]], i64 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: call void @sink(i8* nonnull [[DST]], i64 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: call void @sink(i8* nonnull [[DST]], i64 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: call void @sink(i8* nonnull [[DST]], i64 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: call void @sink(i8* nonnull [[DST]], i64 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: call void @sink(i8* nonnull [[DST]], i64 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: call void @sink(i8* nonnull [[DST]], i64 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)
+ call void @sink(i8* %dst, i64 %ns4_0)
+
+; Transform strlcpy(D, "1234", 1) to *D = '\0', 4.
+ %ns4_1 = call i64 @strlcpy(i8* %dst, i8* %ps4, i64 1)
+ call void @sink(i8* %dst, i64 %ns4_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)
+ call void @sink(i8* %dst, i64 %ns4_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)
+ call void @sink(i8* %dst, i64 %ns4_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)
+ call void @sink(i8* %dst, i64 %ns4_4)
+
+; Transform strlcpy(D, S="1234", 5) to memcpy(D, S, 5), 4.
+ %ns4_5 = call i64 @strlcpy(i8* %dst, i8* %ps4, i64 5)
+ call void @sink(i8* %dst, i64 %ns4_5)
+
+; Transform strlcpy(D, S="1234", 9) to memcpy(D, S, 5), 4.
+ %ns4_9 = call i64 @strlcpy(i8* %dst, i8* %ps4, i64 5)
+ call void @sink(i8* %dst, i64 %ns4_9)
+
+; Transform strlcpy(D, S="1234", SIZE_MAX) to memcpy(D, S, 5), 4.
+ %ns4_m1 = call i64 @strlcpy(i8* %dst, i8* %ps4, i64 -1)
+ call void @sink(i8* %dst, i64 %ns4_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) {
+; 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: call void @sink(i8* nonnull [[DST]], i64 [[STRLEN]])
+; ANY-NEXT: [[STRLEN1:%.*]] = call i64 @strlen(i8* noundef nonnull dereferenceable(1) [[S]])
+; ANY-NEXT: call void @sink(i8* nonnull [[DST]], i64 [[STRLEN1]])
+; ANY-NEXT: [[STRLEN2:%.*]] = call i64 @strlen(i8* noundef nonnull dereferenceable(1) [[S]])
+; ANY-NEXT: call void @sink(i8* nonnull [[DST]], i64 [[STRLEN2]])
+; ANY-NEXT: ret void
+;
+; Transform strlcpy(D, S, 1) to *D = '\0', strlen(S).
+ %ns_1 = call i64 @strlcpy(i8* %dst, i8* %s, i64 1)
+ call void @sink(i8* %dst, i64 %ns_1)
+
+; For strlcpy(D, S, 0) to strlen(S).
+ %ns_0 = call i64 @strlcpy(i8* %dst, i8* %s, i64 0)
+ call void @sink(i8* %dst, i64 %ns_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)
+ call void @sink(i8* %dst, i64 %n0_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
+; annotate the destination argument with the dereferenceable attribute
+; only with nonzero N.
+
+define void @call_strlcpy_s0_n(i8* %dst, i8* %s, i64 %n) {
+; 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: call void @sink(i8* [[DST]], i64 [[NS_2]])
+; ANY-NEXT: [[NS_N:%.*]] = call i64 @strlcpy(i8* [[DST]], i8* noundef nonnull dereferenceable(1) [[S]], i64 [[N:%.*]])
+; ANY-NEXT: call void @sink(i8* [[DST]], i64 [[NS_N]])
+; 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: call void @sink(i8* [[DST]], i64 [[NS_NZ]])
+; ANY-NEXT: [[NS0_N:%.*]] = call i64 @strlcpy(i8* [[DST]], i8* noundef nonnull dereferenceable(1) getelementptr inbounds ([5 x i8], [5 x i8]* @s4, i64 0, i64 4), i64 [[N]])
+; ANY-NEXT: call void @sink(i8* [[DST]], i64 [[NS0_N]])
+; ANY-NEXT: [[NS1_N:%.*]] = call i64 @strlcpy(i8* [[DST]], i8* noundef nonnull dereferenceable(1) getelementptr inbounds ([5 x i8], [5 x i8]* @s4, i64 0, i64 3), i64 [[N]])
+; ANY-NEXT: call void @sink(i8* [[DST]], i64 [[NS1_N]])
+; ANY-NEXT: [[NS4_N:%.*]] = call i64 @strlcpy(i8* [[DST]], i8* noundef nonnull dereferenceable(1) getelementptr inbounds ([5 x i8], [5 x i8]* @s4, i64 0, i64 0), i64 [[N]])
+; ANY-NEXT: call void @sink(i8* [[DST]], i64 [[NS4_N]])
+; ANY-NEXT: ret void
+;
+ %ns_2 = call i64 @strlcpy(i8* %dst, i8* %s, i64 2)
+ call void @sink(i8* %dst, i64 %ns_2)
+
+ %ns_n = call i64 @strlcpy(i8* %dst, i8* %s, i64 %n)
+ call void @sink(i8* %dst, i64 %ns_n)
+
+ %nz = or i64 %n, 1
+ %ns_nz = call i64 @strlcpy(i8* %dst, i8* %s, i64 %nz)
+ call void @sink(i8* %dst, i64 %ns_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)
+ call void @sink(i8* %dst, i64 %ns0_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)
+ call void @sink(i8* %dst, i64 %ns1_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)
+ call void @sink(i8* %dst, i64 %ns4_n)
+
+ ret void
+}
+
+
+ at a5 = constant [5 x i8] c"12345"
+
+; Verify that the transformation behaves reasonably even when the source
+; array is not a nul-terminated string as it's required to be (and doesn't
+; for example attempt to read past its end). All the calls below are
+; undefined so technically reading past the end would be fine but it's
+; easy to avoid.
+
+define void @fold_strlcpy_a5(i8* %dst, i64 %n) {
+; BE-LABEL: @fold_strlcpy_a5(
+; BE-NEXT: call void @sink(i8* [[DST:%.*]], i64 5)
+; BE-NEXT: store i8 0, i8* [[DST]], align 1
+; BE-NEXT: call void @sink(i8* nonnull [[DST]], i64 5)
+; 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: call void @sink(i8* nonnull [[DST]], i64 5)
+; BE-NEXT: [[TMP2:%.*]] = bitcast i8* [[DST]] to i32*
+; BE-NEXT: store i32 825373492, i32* [[TMP2]], align 1
+; BE-NEXT: [[TMP3:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 4
+; BE-NEXT: store i8 0, i8* [[TMP3]], align 1
+; BE-NEXT: call void @sink(i8* nonnull [[DST]], i64 5)
+; 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]* @a5, i64 0, i64 0), i64 5, i1 false)
+; BE-NEXT: [[TMP4:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 5
+; BE-NEXT: store i8 0, i8* [[TMP4]], align 1
+; BE-NEXT: call void @sink(i8* nonnull [[DST]], i64 5)
+; 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]* @a5, i64 0, i64 0), i64 5, i1 false)
+; BE-NEXT: [[TMP5:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 5
+; BE-NEXT: store i8 0, i8* [[TMP5]], align 1
+; BE-NEXT: call void @sink(i8* nonnull [[DST]], i64 5)
+; BE-NEXT: ret void
+;
+; LE-LABEL: @fold_strlcpy_a5(
+; LE-NEXT: call void @sink(i8* [[DST:%.*]], i64 5)
+; LE-NEXT: store i8 0, i8* [[DST]], align 1
+; LE-NEXT: call void @sink(i8* nonnull [[DST]], i64 5)
+; 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: call void @sink(i8* nonnull [[DST]], i64 5)
+; LE-NEXT: [[TMP2:%.*]] = bitcast i8* [[DST]] to i32*
+; LE-NEXT: store i32 875770417, i32* [[TMP2]], align 1
+; LE-NEXT: [[TMP3:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 4
+; LE-NEXT: store i8 0, i8* [[TMP3]], align 1
+; LE-NEXT: call void @sink(i8* nonnull [[DST]], i64 5)
+; 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]* @a5, i64 0, i64 0), i64 5, i1 false)
+; LE-NEXT: [[TMP4:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 5
+; LE-NEXT: store i8 0, i8* [[TMP4]], align 1
+; LE-NEXT: call void @sink(i8* nonnull [[DST]], i64 5)
+; 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]* @a5, i64 0, i64 0), i64 5, i1 false)
+; LE-NEXT: [[TMP5:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 5
+; LE-NEXT: store i8 0, i8* [[TMP5]], align 1
+; LE-NEXT: call void @sink(i8* nonnull [[DST]], i64 5)
+; LE-NEXT: ret void
+;
+ %pa5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 0
+ %na5_0 = call i64 @strlcpy(i8* %dst, i8* %pa5, i64 0)
+ call void @sink(i8* %dst, i64 %na5_0)
+
+ %na5_1 = call i64 @strlcpy(i8* %dst, i8* %pa5, i64 1)
+ call void @sink(i8* %dst, i64 %na5_1)
+
+ %na5_2 = call i64 @strlcpy(i8* %dst, i8* %pa5, i64 2)
+ call void @sink(i8* %dst, i64 %na5_2)
+
+ %na5_5 = call i64 @strlcpy(i8* %dst, i8* %pa5, i64 5)
+ call void @sink(i8* %dst, i64 %na5_5)
+
+ %na5_6 = call i64 @strlcpy(i8* %dst, i8* %pa5, i64 6)
+ call void @sink(i8* %dst, i64 %na5_6)
+
+ %na5_9 = call i64 @strlcpy(i8* %dst, i8* %pa5, i64 9)
+ call void @sink(i8* %dst, i64 %na5_9)
+
+ ret void
+}
More information about the llvm-commits
mailing list