467 lines
24 KiB
LLVM
467 lines
24 KiB
LLVM
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals
|
|
;
|
|
; Test that the stpncpy 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 i8* @stpncpy(i8*, i8*, i64)
|
|
|
|
declare void @sink(i8*, i8*)
|
|
|
|
@a4 = constant [4 x i8] c"1234"
|
|
@s4 = constant [5 x i8] c"1234\00"
|
|
|
|
|
|
; The following are generated by the stpncpy -> memcpy transformation
|
|
; (trading space for speed).
|
|
@str = private constant [4 x i8] c"4\00\00\00"
|
|
@str.1 = private constant [10 x i8] c"4\00\00\00\00\00\00\00\00\00"
|
|
@str.2 = private constant [10 x i8] c"1234\00\00\00\00\00\00"
|
|
@str.3 = private unnamed_addr constant [4 x i8] c"4\00\00\00", align 1
|
|
@str.4 = private unnamed_addr constant [10 x i8] c"4\00\00\00\00\00\00\00\00\00", align 1
|
|
@str.5 = private unnamed_addr constant [10 x i8] c"1234\00\00\00\00\00\00", align 1
|
|
|
|
; Verify that the generated constants have the expected contents.
|
|
; ANY: @[[A4:[a-zA-Z0-9_$"\\.-]+]] = constant [4 x i8] c"1234"
|
|
; ANY: @[[S4:[a-zA-Z0-9_$"\\.-]+]] = constant [5 x i8] c"1234\00"
|
|
; ANY: @[[STR:[a-zA-Z0-9_$"\\.-]+]] = private constant [4 x i8] c"4\00\00\00"
|
|
; ANY: @[[STR_1:[a-zA-Z0-9_$"\\.-]+]] = private constant [10 x i8] c"4\00\00\00\00\00\00\00\00\00"
|
|
; ANY: @[[STR_2:[a-zA-Z0-9_$"\\.-]+]] = private constant [10 x i8] c"1234\00\00\00\00\00\00"
|
|
; ANY: @[[STR_3:[a-zA-Z0-9_$"\\.-]+]] = private unnamed_addr constant [4 x i8] c"4\00\00\00", align 1
|
|
; ANY: @[[STR_4:[a-zA-Z0-9_$"\\.-]+]] = private unnamed_addr constant [10 x i8] c"4\00\00\00\00\00\00\00\00\00", align 1
|
|
; ANY: @[[STR_5:[a-zA-Z0-9_$"\\.-]+]] = private unnamed_addr constant [10 x i8] c"1234\00\00\00\00\00\00", align 1
|
|
; ANY: @[[STR_6:[a-zA-Z0-9_$"\\.-]+]] = private unnamed_addr constant [4 x i8] c"4\00\00\00", align 1
|
|
; ANY: @[[STR_7:[a-zA-Z0-9_$"\\.-]+]] = private unnamed_addr constant [10 x i8] c"4\00\00\00\00\00\00\00\00\00", align 1
|
|
; ANY: @[[STR_8:[a-zA-Z0-9_$"\\.-]+]] = private unnamed_addr constant [10 x i8] c"1234\00\00\00\00\00\00", align 1
|
|
; ANY: @[[STR_9:[a-zA-Z0-9_$"\\.-]+]] = private unnamed_addr constant [10 x i8] c"1234\00\00\00\00\00\00", align 1
|
|
|
|
; Verify that exactly overlapping stpncpy(D, D, N) calls are transformed
|
|
; to D + strnlen(D, N) or, equivalently, D + (*D != '\0'), when N < 2.
|
|
|
|
define void @fold_stpncpy_overlap(i8* %dst, i64 %n) {
|
|
; ANY-LABEL: @fold_stpncpy_overlap(
|
|
; ANY-NEXT: call void @sink(i8* [[DST:%.*]], i8* [[DST]])
|
|
; ANY-NEXT: [[STXNCPY_CHAR0:%.*]] = load i8, i8* [[DST]], align 1
|
|
; ANY-NEXT: [[STPNCPY_CHAR0CMP:%.*]] = icmp ne i8 [[STXNCPY_CHAR0]], 0
|
|
; ANY-NEXT: [[STPNCPY_SEL_IDX:%.*]] = zext i1 [[STPNCPY_CHAR0CMP]] to i64
|
|
; ANY-NEXT: [[STPNCPY_SEL:%.*]] = getelementptr i8, i8* [[DST]], i64 [[STPNCPY_SEL_IDX]]
|
|
; ANY-NEXT: call void @sink(i8* nonnull [[DST]], i8* [[STPNCPY_SEL]])
|
|
; ANY-NEXT: ret void
|
|
;
|
|
; Fold stpncpy(D, D, 0) to just D.
|
|
%es_0 = call i8* @stpncpy(i8* %dst, i8* %dst, i64 0)
|
|
call void @sink(i8* %dst, i8* %es_0)
|
|
|
|
; Fold stpncpy(D, D, 1) to D + (*D != '\0').
|
|
%es_1 = call i8* @stpncpy(i8* %dst, i8* %dst, i64 1)
|
|
call void @sink(i8* %dst, i8* %es_1)
|
|
|
|
ret void
|
|
}
|
|
|
|
|
|
; Verify that exactly overlapping stpncpy(D, D, N) calls are left alone
|
|
; when N >= 2. Such calls are strictly undefined and while simplifying
|
|
; them to the expected result is possible there is little to gain from it.
|
|
|
|
define void @call_stpncpy_overlap(i8* %dst, i64 %n) {
|
|
; ANY-LABEL: @call_stpncpy_overlap(
|
|
; ANY-NEXT: [[ES_2:%.*]] = call i8* @stpncpy(i8* noundef nonnull dereferenceable(1) [[DST:%.*]], i8* noundef nonnull dereferenceable(1) [[DST]], i64 2)
|
|
; ANY-NEXT: call void @sink(i8* [[DST]], i8* [[ES_2]])
|
|
; ANY-NEXT: [[ES_3:%.*]] = call i8* @stpncpy(i8* noundef nonnull dereferenceable(1) [[DST]], i8* noundef nonnull dereferenceable(1) [[DST]], i64 3)
|
|
; ANY-NEXT: call void @sink(i8* [[DST]], i8* [[ES_3]])
|
|
; ANY-NEXT: [[ES_N:%.*]] = call i8* @stpncpy(i8* [[DST]], i8* [[DST]], i64 [[N:%.*]])
|
|
; ANY-NEXT: call void @sink(i8* [[DST]], i8* [[ES_N]])
|
|
; ANY-NEXT: ret void
|
|
;
|
|
; Do not transform stpncpy(D, D, 2).
|
|
%es_2 = call i8* @stpncpy(i8* %dst, i8* %dst, i64 2)
|
|
call void @sink(i8* %dst, i8* %es_2)
|
|
|
|
; Do not transform stpncpy(D, D, 3).
|
|
%es_3 = call i8* @stpncpy(i8* %dst, i8* %dst, i64 3)
|
|
call void @sink(i8* %dst, i8* %es_3)
|
|
|
|
; Do not transform stpncpy(D, D, N).
|
|
%es_n = call i8* @stpncpy(i8* %dst, i8* %dst, i64 %n)
|
|
call void @sink(i8* %dst, i8* %es_n)
|
|
|
|
ret void
|
|
}
|
|
|
|
|
|
; Verify that stpncpy(D, "", N) calls are transformed to memset(D, 0, N).
|
|
|
|
define void @fold_stpncpy_s0(i8* %dst, i64 %n) {
|
|
; ANY-LABEL: @fold_stpncpy_s0(
|
|
; ANY-NEXT: call void @sink(i8* [[DST:%.*]], i8* [[DST]])
|
|
; ANY-NEXT: store i8 0, i8* [[DST]], align 1
|
|
; ANY-NEXT: call void @sink(i8* nonnull [[DST]], i8* nonnull [[DST]])
|
|
; ANY-NEXT: [[TMP1:%.*]] = bitcast i8* [[DST]] to i16*
|
|
; ANY-NEXT: store i16 0, i16* [[TMP1]], align 1
|
|
; ANY-NEXT: call void @sink(i8* nonnull [[DST]], i8* nonnull [[DST]])
|
|
; ANY-NEXT: call void @llvm.memset.p0i8.i64(i8* noundef nonnull align 1 dereferenceable(9) [[DST]], i8 0, i64 9, i1 false)
|
|
; ANY-NEXT: call void @sink(i8* nonnull [[DST]], i8* nonnull [[DST]])
|
|
; ANY-NEXT: call void @llvm.memset.p0i8.i64(i8* nonnull align 1 [[DST]], i8 0, i64 [[N:%.*]], i1 false)
|
|
; ANY-NEXT: call void @sink(i8* nonnull [[DST]], i8* nonnull [[DST]])
|
|
; ANY-NEXT: ret void
|
|
;
|
|
%ps0 = getelementptr [5 x i8], [5 x i8]* @s4, i32 0, i32 4
|
|
|
|
; Fold stpncpy(D, "", 0) to just D.
|
|
%es0_0 = call i8* @stpncpy(i8* %dst, i8* %ps0, i64 0)
|
|
call void @sink(i8* %dst, i8* %es0_0)
|
|
|
|
; Transform stpncpy(D, "", 1) to *D = '\0, D.
|
|
%es0_1 = call i8* @stpncpy(i8* %dst, i8* %ps0, i64 1)
|
|
call void @sink(i8* %dst, i8* %es0_1)
|
|
|
|
; Transform stpncpy(D, "", 2) to memset(D, 0, 2), D.
|
|
%es0_2 = call i8* @stpncpy(i8* %dst, i8* %ps0, i64 2)
|
|
call void @sink(i8* %dst, i8* %es0_2)
|
|
|
|
; Transform stpncpy(D, "", 9) to memset(D, 0, 9), D.
|
|
%es0_9 = call i8* @stpncpy(i8* %dst, i8* %ps0, i64 9)
|
|
call void @sink(i8* %dst, i8* %es0_9)
|
|
|
|
; Transform stpncpy(D, "", n) to memset(D, 0, n), D.
|
|
%es0_n = call i8* @stpncpy(i8* %dst, i8* %ps0, i64 %n)
|
|
call void @sink(i8* %dst, i8* %es0_n)
|
|
|
|
ret void
|
|
}
|
|
|
|
|
|
; Verify that stpncpy(D, "4", N) calls are transformed to the equivalent
|
|
; of strncpy(D, "4", N) and the result folded to D + (N != 0).
|
|
|
|
define void @fold_stpncpy_s1(i8* %dst) {
|
|
; BE-LABEL: @fold_stpncpy_s1(
|
|
; BE-NEXT: call void @sink(i8* [[DST:%.*]], i8* [[DST]])
|
|
; BE-NEXT: store i8 52, i8* [[DST]], align 1
|
|
; BE-NEXT: [[STPNCPY_END:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 1
|
|
; BE-NEXT: call void @sink(i8* nonnull [[DST]], i8* nonnull [[STPNCPY_END]])
|
|
; BE-NEXT: [[TMP1:%.*]] = bitcast i8* [[DST]] to i16*
|
|
; BE-NEXT: store i16 13312, i16* [[TMP1]], align 1
|
|
; BE-NEXT: [[ENDPTR:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 1
|
|
; BE-NEXT: call void @sink(i8* nonnull [[DST]], i8* nonnull [[ENDPTR]])
|
|
; 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 ([4 x i8], [4 x i8]* @str.6, i64 0, i64 0), i64 3, i1 false)
|
|
; BE-NEXT: [[ENDPTR1:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 1
|
|
; BE-NEXT: call void @sink(i8* nonnull [[DST]], i8* nonnull [[ENDPTR1]])
|
|
; BE-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 1 dereferenceable(9) [[DST]], i8* noundef nonnull align 1 dereferenceable(9) getelementptr inbounds ([10 x i8], [10 x i8]* @str.7, i64 0, i64 0), i64 9, i1 false)
|
|
; BE-NEXT: [[ENDPTR2:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 1
|
|
; BE-NEXT: call void @sink(i8* nonnull [[DST]], i8* nonnull [[ENDPTR2]])
|
|
; BE-NEXT: ret void
|
|
;
|
|
; LE-LABEL: @fold_stpncpy_s1(
|
|
; LE-NEXT: call void @sink(i8* [[DST:%.*]], i8* [[DST]])
|
|
; LE-NEXT: store i8 52, i8* [[DST]], align 1
|
|
; LE-NEXT: [[STPNCPY_END:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 1
|
|
; LE-NEXT: call void @sink(i8* nonnull [[DST]], i8* nonnull [[STPNCPY_END]])
|
|
; LE-NEXT: [[TMP1:%.*]] = bitcast i8* [[DST]] to i16*
|
|
; LE-NEXT: store i16 52, i16* [[TMP1]], align 1
|
|
; LE-NEXT: [[ENDPTR:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 1
|
|
; LE-NEXT: call void @sink(i8* nonnull [[DST]], i8* nonnull [[ENDPTR]])
|
|
; 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 ([4 x i8], [4 x i8]* @str.6, i64 0, i64 0), i64 3, i1 false)
|
|
; LE-NEXT: [[ENDPTR1:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 1
|
|
; LE-NEXT: call void @sink(i8* nonnull [[DST]], i8* nonnull [[ENDPTR1]])
|
|
; LE-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 1 dereferenceable(9) [[DST]], i8* noundef nonnull align 1 dereferenceable(9) getelementptr inbounds ([10 x i8], [10 x i8]* @str.7, i64 0, i64 0), i64 9, i1 false)
|
|
; LE-NEXT: [[ENDPTR2:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 1
|
|
; LE-NEXT: call void @sink(i8* nonnull [[DST]], i8* nonnull [[ENDPTR2]])
|
|
; LE-NEXT: ret void
|
|
;
|
|
%ps1 = getelementptr [5 x i8], [5 x i8]* @s4, i32 0, i32 3
|
|
|
|
; Fold stpncpy(D, "4", 0) to just D.
|
|
%es1_0 = call i8* @stpncpy(i8* %dst, i8* %ps1, i64 0)
|
|
call void @sink(i8* %dst, i8* %es1_0)
|
|
|
|
; Transform stpncpy(D, "4", 1) to *D = '4', D + 1.
|
|
%es1_1 = call i8* @stpncpy(i8* %dst, i8* %ps1, i64 1)
|
|
call void @sink(i8* %dst, i8* %es1_1)
|
|
|
|
; Transform stpncpy(D, "4", 2) to strncpy(D, "4", 2) + 1.
|
|
%es1_2 = call i8* @stpncpy(i8* %dst, i8* %ps1, i64 2)
|
|
call void @sink(i8* %dst, i8* %es1_2)
|
|
|
|
; Transform stpncpy(D, "4", 3) to strncpy(D, "4", 3) + 1, which is then
|
|
; transformed to memcpy(D, "4", 2), D[2] = '\0', D + 1.
|
|
%es1_3 = call i8* @stpncpy(i8* %dst, i8* %ps1, i64 3)
|
|
call void @sink(i8* %dst, i8* %es1_3)
|
|
|
|
; Transform stpncpy(D, "4", 9) to strncpy(D, "4", 9) + 1.
|
|
%es1_9 = call i8* @stpncpy(i8* %dst, i8* %ps1, i64 9)
|
|
call void @sink(i8* %dst, i8* %es1_9)
|
|
|
|
ret void
|
|
}
|
|
|
|
|
|
; Verify that stpncpy(D, "1234", N) calls are transformed to the equivalent
|
|
; of strncpy(D, "1234", N) and the result folded to D + min(4, N).
|
|
|
|
define void @fold_stpncpy_s4(i8* %dst, i64 %n) {
|
|
; BE-LABEL: @fold_stpncpy_s4(
|
|
; BE-NEXT: call void @sink(i8* [[DST:%.*]], i8* [[DST]])
|
|
; BE-NEXT: store i8 49, i8* [[DST]], align 1
|
|
; BE-NEXT: [[STPNCPY_END:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 1
|
|
; BE-NEXT: call void @sink(i8* nonnull [[DST]], i8* nonnull [[STPNCPY_END]])
|
|
; BE-NEXT: [[TMP1:%.*]] = bitcast i8* [[DST]] to i16*
|
|
; BE-NEXT: store i16 12594, i16* [[TMP1]], align 1
|
|
; BE-NEXT: [[ENDPTR:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 2
|
|
; BE-NEXT: call void @sink(i8* nonnull [[DST]], i8* nonnull [[ENDPTR]])
|
|
; BE-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 1 dereferenceable(3) [[DST]], i8* noundef nonnull align 1 dereferenceable(5) getelementptr inbounds ([5 x i8], [5 x i8]* @s4, i64 0, i64 0), i64 3, i1 false)
|
|
; BE-NEXT: [[ENDPTR1:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 3
|
|
; BE-NEXT: call void @sink(i8* nonnull [[DST]], i8* nonnull [[ENDPTR1]])
|
|
; BE-NEXT: [[TMP2:%.*]] = bitcast i8* [[DST]] to i32*
|
|
; BE-NEXT: store i32 825373492, i32* [[TMP2]], align 1
|
|
; BE-NEXT: [[ENDPTR2:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 4
|
|
; BE-NEXT: call void @sink(i8* nonnull [[DST]], i8* nonnull [[ENDPTR2]])
|
|
; BE-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 1 dereferenceable(9) [[DST]], i8* noundef nonnull align 1 dereferenceable(9) getelementptr inbounds ([10 x i8], [10 x i8]* @str.8, i64 0, i64 0), i64 9, i1 false)
|
|
; BE-NEXT: [[ENDPTR3:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 4
|
|
; BE-NEXT: call void @sink(i8* nonnull [[DST]], i8* nonnull [[ENDPTR3]])
|
|
; BE-NEXT: ret void
|
|
;
|
|
; LE-LABEL: @fold_stpncpy_s4(
|
|
; LE-NEXT: call void @sink(i8* [[DST:%.*]], i8* [[DST]])
|
|
; LE-NEXT: store i8 49, i8* [[DST]], align 1
|
|
; LE-NEXT: [[STPNCPY_END:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 1
|
|
; LE-NEXT: call void @sink(i8* nonnull [[DST]], i8* nonnull [[STPNCPY_END]])
|
|
; LE-NEXT: [[TMP1:%.*]] = bitcast i8* [[DST]] to i16*
|
|
; LE-NEXT: store i16 12849, i16* [[TMP1]], align 1
|
|
; LE-NEXT: [[ENDPTR:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 2
|
|
; LE-NEXT: call void @sink(i8* nonnull [[DST]], i8* nonnull [[ENDPTR]])
|
|
; LE-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 1 dereferenceable(3) [[DST]], i8* noundef nonnull align 1 dereferenceable(5) getelementptr inbounds ([5 x i8], [5 x i8]* @s4, i64 0, i64 0), i64 3, i1 false)
|
|
; LE-NEXT: [[ENDPTR1:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 3
|
|
; LE-NEXT: call void @sink(i8* nonnull [[DST]], i8* nonnull [[ENDPTR1]])
|
|
; LE-NEXT: [[TMP2:%.*]] = bitcast i8* [[DST]] to i32*
|
|
; LE-NEXT: store i32 875770417, i32* [[TMP2]], align 1
|
|
; LE-NEXT: [[ENDPTR2:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 4
|
|
; LE-NEXT: call void @sink(i8* nonnull [[DST]], i8* nonnull [[ENDPTR2]])
|
|
; LE-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 1 dereferenceable(9) [[DST]], i8* noundef nonnull align 1 dereferenceable(9) getelementptr inbounds ([10 x i8], [10 x i8]* @str.8, i64 0, i64 0), i64 9, i1 false)
|
|
; LE-NEXT: [[ENDPTR3:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 4
|
|
; LE-NEXT: call void @sink(i8* nonnull [[DST]], i8* nonnull [[ENDPTR3]])
|
|
; LE-NEXT: ret void
|
|
;
|
|
%ps4 = getelementptr [5 x i8], [5 x i8]* @s4, i32 0, i32 0
|
|
|
|
; Fold stpncpy(D, "1234", 0) to just D.
|
|
%es4_0 = call i8* @stpncpy(i8* %dst, i8* %ps4, i64 0)
|
|
call void @sink(i8* %dst, i8* %es4_0)
|
|
|
|
; Transform stpncpy(D, "1234", 1) to *D = '4', D + 1.
|
|
%es4_1 = call i8* @stpncpy(i8* %dst, i8* %ps4, i64 1)
|
|
call void @sink(i8* %dst, i8* %es4_1)
|
|
|
|
; Transform stpncpy(D, "1234", 2) to strncpy(D, "1234", 2) + 2.
|
|
%es4_2 = call i8* @stpncpy(i8* %dst, i8* %ps4, i64 2)
|
|
call void @sink(i8* %dst, i8* %es4_2)
|
|
|
|
; Transform stpncpy(D, "1234", 3) to strncpy(D, "1234", 3) + 3
|
|
%es4_3 = call i8* @stpncpy(i8* %dst, i8* %ps4, i64 3)
|
|
call void @sink(i8* %dst, i8* %es4_3)
|
|
|
|
; Transform stpncpy(D, "1234", 4) to strncpy(D, "1234", 4) + 4.
|
|
%es4_4 = call i8* @stpncpy(i8* %dst, i8* %ps4, i64 4)
|
|
call void @sink(i8* %dst, i8* %es4_4)
|
|
|
|
; Transform stpncpy(D, "1234", 9) to strncpy(D, "1234", 9) + 4.
|
|
%es4_9 = call i8* @stpncpy(i8* %dst, i8* %ps4, i64 9)
|
|
call void @sink(i8* %dst, i8* %es4_9)
|
|
|
|
ret void
|
|
}
|
|
|
|
|
|
; Verify that a call to stpncpy(D, A, N) with a constant source larger
|
|
; than one byte is left alone when N is unknown.
|
|
|
|
define void @call_stpncpy_xx_n(i8* %dst, i64 %n) {
|
|
; ANY-LABEL: @call_stpncpy_xx_n(
|
|
; ANY-NEXT: [[EA1_N:%.*]] = call i8* @stpncpy(i8* [[DST:%.*]], i8* dereferenceable(2) getelementptr inbounds ([4 x i8], [4 x i8]* @a4, i64 0, i64 3), i64 [[N:%.*]])
|
|
; ANY-NEXT: call void @sink(i8* [[DST]], i8* [[EA1_N]])
|
|
; ANY-NEXT: [[EA4_N:%.*]] = call i8* @stpncpy(i8* [[DST]], i8* dereferenceable(5) getelementptr inbounds ([4 x i8], [4 x i8]* @a4, i64 0, i64 0), i64 [[N]])
|
|
; ANY-NEXT: call void @sink(i8* [[DST]], i8* [[EA4_N]])
|
|
; ANY-NEXT: [[ES1_N:%.*]] = call i8* @stpncpy(i8* [[DST]], i8* dereferenceable(2) getelementptr inbounds ([5 x i8], [5 x i8]* @s4, i64 0, i64 3), i64 [[N]])
|
|
; ANY-NEXT: call void @sink(i8* [[DST]], i8* [[ES1_N]])
|
|
; ANY-NEXT: [[ES4_N:%.*]] = call i8* @stpncpy(i8* [[DST]], i8* dereferenceable(5) getelementptr inbounds ([5 x i8], [5 x i8]* @s4, i64 0, i64 0), i64 [[N]])
|
|
; ANY-NEXT: call void @sink(i8* [[DST]], i8* [[ES4_N]])
|
|
; ANY-NEXT: ret void
|
|
;
|
|
; Do not transform stpncpy(D, A4 + 3, N) when N is unknown.
|
|
%pa1 = getelementptr [4 x i8], [4 x i8]* @a4, i32 0, i32 3
|
|
%ea1_n = call i8* @stpncpy(i8* %dst, i8* %pa1, i64 %n)
|
|
call void @sink(i8* %dst, i8* %ea1_n)
|
|
|
|
; Do not transform stpncpy(D, A4, N) when N is unknown.
|
|
%pa4 = getelementptr [4 x i8], [4 x i8]* @a4, i32 0, i32 0
|
|
%ea4_n = call i8* @stpncpy(i8* %dst, i8* %pa4, i64 %n)
|
|
call void @sink(i8* %dst, i8* %ea4_n)
|
|
|
|
; Do not transform stpncpy(D, "4", N) when N is unknown.
|
|
%ps1 = getelementptr [5 x i8], [5 x i8]* @s4, i32 0, i32 3
|
|
%es1_n = call i8* @stpncpy(i8* %dst, i8* %ps1, i64 %n)
|
|
call void @sink(i8* %dst, i8* %es1_n)
|
|
|
|
; Likewise, do not transform stpncpy(D, "1234", N) when N is unknown.
|
|
%ps4 = getelementptr [5 x i8], [5 x i8]* @s4, i32 0, i32 0
|
|
%es4_n = call i8* @stpncpy(i8* %dst, i8* %ps4, i64 %n)
|
|
call void @sink(i8* %dst, i8* %es4_n)
|
|
|
|
ret void
|
|
}
|
|
|
|
; Verify that stpncpy(D, (char[4]){"1234"}, N) calls with an unterminated
|
|
; source array are transformed to the equivalent strncpy call and the result
|
|
; folded to D + min(4, N).
|
|
|
|
define void @fold_stpncpy_a4(i8* %dst, i64 %n) {
|
|
; BE-LABEL: @fold_stpncpy_a4(
|
|
; BE-NEXT: call void @sink(i8* [[DST:%.*]], i8* [[DST]])
|
|
; BE-NEXT: store i8 49, i8* [[DST]], align 1
|
|
; BE-NEXT: [[STPNCPY_END:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 1
|
|
; BE-NEXT: call void @sink(i8* nonnull [[DST]], i8* nonnull [[STPNCPY_END]])
|
|
; BE-NEXT: [[TMP1:%.*]] = bitcast i8* [[DST]] to i16*
|
|
; BE-NEXT: store i16 12594, i16* [[TMP1]], align 1
|
|
; BE-NEXT: [[ENDPTR:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 2
|
|
; BE-NEXT: call void @sink(i8* nonnull [[DST]], i8* nonnull [[ENDPTR]])
|
|
; BE-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 1 dereferenceable(3) [[DST]], i8* noundef nonnull align 1 dereferenceable(5) getelementptr inbounds ([4 x i8], [4 x i8]* @a4, i64 0, i64 0), i64 3, i1 false)
|
|
; BE-NEXT: [[ENDPTR1:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 3
|
|
; BE-NEXT: call void @sink(i8* nonnull [[DST]], i8* nonnull [[ENDPTR1]])
|
|
; BE-NEXT: [[TMP2:%.*]] = bitcast i8* [[DST]] to i32*
|
|
; BE-NEXT: store i32 825373492, i32* [[TMP2]], align 1
|
|
; BE-NEXT: [[ENDPTR2:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 4
|
|
; BE-NEXT: call void @sink(i8* nonnull [[DST]], i8* nonnull [[ENDPTR2]])
|
|
; 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 ([4 x i8], [4 x i8]* @a4, i64 0, i64 0), i64 5, i1 false)
|
|
; BE-NEXT: [[ENDPTR3:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 4
|
|
; BE-NEXT: call void @sink(i8* nonnull [[DST]], i8* nonnull [[ENDPTR3]])
|
|
; BE-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 1 dereferenceable(9) [[DST]], i8* noundef nonnull align 1 dereferenceable(9) getelementptr inbounds ([10 x i8], [10 x i8]* @str.9, i64 0, i64 0), i64 9, i1 false)
|
|
; BE-NEXT: [[ENDPTR4:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 4
|
|
; BE-NEXT: call void @sink(i8* nonnull [[DST]], i8* nonnull [[ENDPTR4]])
|
|
; BE-NEXT: ret void
|
|
;
|
|
; LE-LABEL: @fold_stpncpy_a4(
|
|
; LE-NEXT: call void @sink(i8* [[DST:%.*]], i8* [[DST]])
|
|
; LE-NEXT: store i8 49, i8* [[DST]], align 1
|
|
; LE-NEXT: [[STPNCPY_END:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 1
|
|
; LE-NEXT: call void @sink(i8* nonnull [[DST]], i8* nonnull [[STPNCPY_END]])
|
|
; LE-NEXT: [[TMP1:%.*]] = bitcast i8* [[DST]] to i16*
|
|
; LE-NEXT: store i16 12849, i16* [[TMP1]], align 1
|
|
; LE-NEXT: [[ENDPTR:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 2
|
|
; LE-NEXT: call void @sink(i8* nonnull [[DST]], i8* nonnull [[ENDPTR]])
|
|
; LE-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 1 dereferenceable(3) [[DST]], i8* noundef nonnull align 1 dereferenceable(5) getelementptr inbounds ([4 x i8], [4 x i8]* @a4, i64 0, i64 0), i64 3, i1 false)
|
|
; LE-NEXT: [[ENDPTR1:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 3
|
|
; LE-NEXT: call void @sink(i8* nonnull [[DST]], i8* nonnull [[ENDPTR1]])
|
|
; LE-NEXT: [[TMP2:%.*]] = bitcast i8* [[DST]] to i32*
|
|
; LE-NEXT: store i32 875770417, i32* [[TMP2]], align 1
|
|
; LE-NEXT: [[ENDPTR2:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 4
|
|
; LE-NEXT: call void @sink(i8* nonnull [[DST]], i8* nonnull [[ENDPTR2]])
|
|
; 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 ([4 x i8], [4 x i8]* @a4, i64 0, i64 0), i64 5, i1 false)
|
|
; LE-NEXT: [[ENDPTR3:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 4
|
|
; LE-NEXT: call void @sink(i8* nonnull [[DST]], i8* nonnull [[ENDPTR3]])
|
|
; LE-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 1 dereferenceable(9) [[DST]], i8* noundef nonnull align 1 dereferenceable(9) getelementptr inbounds ([10 x i8], [10 x i8]* @str.9, i64 0, i64 0), i64 9, i1 false)
|
|
; LE-NEXT: [[ENDPTR4:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 4
|
|
; LE-NEXT: call void @sink(i8* nonnull [[DST]], i8* nonnull [[ENDPTR4]])
|
|
; LE-NEXT: ret void
|
|
;
|
|
|
|
%pa4 = getelementptr [4 x i8], [4 x i8]* @a4, i32 0, i32 0
|
|
|
|
; Fold stpncpy(D, A4, 0) to just D.
|
|
%ea4_0 = call i8* @stpncpy(i8* %dst, i8* %pa4, i64 0)
|
|
call void @sink(i8* %dst, i8* %ea4_0)
|
|
|
|
; Transform stpncpy(D, A4, 1) to *D = '4', D + 1.
|
|
%ea4_1 = call i8* @stpncpy(i8* %dst, i8* %pa4, i64 1)
|
|
call void @sink(i8* %dst, i8* %ea4_1)
|
|
|
|
; Transform stpncpy(D, A4, 2) to strncpy(D, A4, 2) + 2.
|
|
%ea4_2 = call i8* @stpncpy(i8* %dst, i8* %pa4, i64 2)
|
|
call void @sink(i8* %dst, i8* %ea4_2)
|
|
|
|
; Transform stpncpy(D, A4, 3) to strncpy(D, A4, 3) + 3
|
|
%ea4_3 = call i8* @stpncpy(i8* %dst, i8* %pa4, i64 3)
|
|
call void @sink(i8* %dst, i8* %ea4_3)
|
|
|
|
; Transform stpncpy(D, A4, 4) to strncpy(D, A4, 4) + 4.
|
|
%ea4_4 = call i8* @stpncpy(i8* %dst, i8* %pa4, i64 4)
|
|
call void @sink(i8* %dst, i8* %ea4_4)
|
|
|
|
; Transform stpncpy(D, A4, 5) to strncpy(D, A4, 5) + 4.
|
|
%ea4_5 = call i8* @stpncpy(i8* %dst, i8* %pa4, i64 5)
|
|
call void @sink(i8* %dst, i8* %ea4_5)
|
|
|
|
; Transform stpncpy(D, A4, 9) to strncpy(D, A4, 9) + 4.
|
|
%ea4_9 = call i8* @stpncpy(i8* %dst, i8* %pa4, i64 9)
|
|
call void @sink(i8* %dst, i8* %ea4_9)
|
|
|
|
ret void
|
|
}
|
|
|
|
|
|
; Verify that stpncpy(D, S, N) calls with N < 2 are transformed to
|
|
; the equivalent of strncpy and either folded to D if N == 0 or to
|
|
; *D ? D + 1 : D otherwise.
|
|
|
|
define void @fold_stpncpy_s(i8* %dst, i8* %src) {
|
|
; ANY-LABEL: @fold_stpncpy_s(
|
|
; ANY-NEXT: call void @sink(i8* [[DST:%.*]], i8* [[DST]])
|
|
; ANY-NEXT: [[STXNCPY_CHAR0:%.*]] = load i8, i8* [[SRC:%.*]], align 1
|
|
; ANY-NEXT: store i8 [[STXNCPY_CHAR0]], i8* [[DST]], align 1
|
|
; ANY-NEXT: [[STPNCPY_CHAR0CMP:%.*]] = icmp ne i8 [[STXNCPY_CHAR0]], 0
|
|
; ANY-NEXT: [[STPNCPY_SEL_IDX:%.*]] = zext i1 [[STPNCPY_CHAR0CMP]] to i64
|
|
; ANY-NEXT: [[STPNCPY_SEL:%.*]] = getelementptr i8, i8* [[DST]], i64 [[STPNCPY_SEL_IDX]]
|
|
; ANY-NEXT: call void @sink(i8* nonnull [[DST]], i8* [[STPNCPY_SEL]])
|
|
; ANY-NEXT: ret void
|
|
;
|
|
; Fold stpncpy(D, S, 0) to just D.
|
|
%es_0 = call i8* @stpncpy(i8* %dst, i8* %src, i64 0)
|
|
call void @sink(i8* %dst, i8* %es_0)
|
|
|
|
; Transform stpncpy(D, "", 1) to *D = '\0, D.
|
|
%es_1 = call i8* @stpncpy(i8* %dst, i8* %src, i64 1)
|
|
call void @sink(i8* %dst, i8* %es_1)
|
|
|
|
ret void
|
|
}
|
|
|
|
|
|
; Verify that stpncpy(D, S, N) calls with N >= 2 are not transformed.
|
|
; In theory they could be transformed to the equivalent of the following
|
|
; though it's not clear that it would be a win:
|
|
; P = memccpy(D, S, 0, N)
|
|
; N' = P ? N - (P - D) : 0
|
|
; Q = P ? P : D + N
|
|
; memset(Q, 0, N')
|
|
; Q
|
|
; Also verify that the arguments of the call are annotated with the right
|
|
; attributes.
|
|
|
|
define void @call_stpncpy_s(i8* %dst, i8* %src, i64 %n) {
|
|
; ANY-LABEL: @call_stpncpy_s(
|
|
; ANY-NEXT: [[ES_2:%.*]] = call i8* @stpncpy(i8* noundef nonnull dereferenceable(1) [[DST:%.*]], i8* noundef nonnull dereferenceable(1) [[SRC:%.*]], i64 2)
|
|
; ANY-NEXT: call void @sink(i8* [[DST]], i8* [[ES_2]])
|
|
; ANY-NEXT: [[ES_N:%.*]] = call i8* @stpncpy(i8* [[DST]], i8* [[SRC]], i64 [[N:%.*]])
|
|
; ANY-NEXT: call void @sink(i8* [[DST]], i8* [[ES_N]])
|
|
; ANY-NEXT: ret void
|
|
;
|
|
; Do not transform stpncpy(D, S, 2). Both *D and *S must be derefernceable
|
|
; but neither D[1] nor S[1] need be.
|
|
%es_2 = call i8* @stpncpy(i8* %dst, i8* %src, i64 2)
|
|
call void @sink(i8* %dst, i8* %es_2)
|
|
|
|
; Do not transform stpncpy(D, S, N). Both D and S must be nonnull but
|
|
; neither *D nor *S need be dereferenceable.
|
|
; TODO: Both D and S should be annotated nonnull and noundef regardless
|
|
; of the value of N. See https://reviews.llvm.org/D124633.
|
|
%es_n = call i8* @stpncpy(i8* %dst, i8* %src, i64 %n)
|
|
call void @sink(i8* %dst, i8* %es_n)
|
|
|
|
ret void
|
|
}
|
|
;.
|
|
; ANY: attributes #[[ATTR0:[0-9]+]] = { argmemonly nocallback nofree nounwind willreturn writeonly }
|
|
; ANY: attributes #[[ATTR1:[0-9]+]] = { argmemonly nocallback nofree nounwind willreturn }
|
|
;.
|