Nikita Popov c23b4fbdbb
[IR] Remove size argument from lifetime intrinsics (#150248)
Now that #149310 has restricted lifetime intrinsics to only work on
allocas, we can also drop the explicit size argument. Instead, the size
is implied by the alloca.

This removes the ability to only mark a prefix of an alloca alive/dead.
We never used that capability, so we should remove the need to handle
that possibility everywhere (though many key places, including stack
coloring, did not actually respect this).
2025-08-08 11:09:34 +02:00

477 lines
18 KiB
LLVM

; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -S -aa-pipeline=basic-aa -passes=inferattrs,dse | FileCheck %s
target triple = "x86_64-unknown-linux-gnu"
declare ptr @strcpy(ptr %dest, ptr %src) nounwind
define void @test1(ptr %src) {
; CHECK-LABEL: @test1(
; CHECK-NEXT: ret void
;
%B = alloca [16 x i8]
%call = call ptr @strcpy(ptr %B, ptr %src)
ret void
}
define void @strcpy_reads_after(ptr noalias %dest, ptr %src) {
; CHECK-LABEL: @strcpy_reads_after(
; CHECK-NEXT: [[SRC_2:%.*]] = getelementptr inbounds i8, ptr [[SRC:%.*]], i64 1
; CHECK-NEXT: store i8 99, ptr [[SRC_2]], align 1
; CHECK-NEXT: [[SRC_1:%.*]] = getelementptr inbounds i8, ptr [[SRC]], i64 1
; CHECK-NEXT: [[CALL:%.*]] = call ptr @strcpy(ptr [[DEST:%.*]], ptr [[SRC_1]])
; CHECK-NEXT: store i8 2, ptr [[SRC]], align 1
; CHECK-NEXT: store i8 2, ptr [[SRC_2]], align 1
; CHECK-NEXT: ret void
;
%src.2 = getelementptr inbounds i8, ptr %src, i64 1
store i8 1, ptr %src
store i8 99, ptr %src.2
%src.1 = getelementptr inbounds i8, ptr %src, i64 1
%call = call ptr @strcpy(ptr %dest, ptr %src.1)
store i8 2, ptr %src
store i8 2, ptr %src.2
ret void
}
declare ptr @strncpy(ptr %dest, ptr %src, i64 %n) nounwind
define void @test2(ptr %src) {
; CHECK-LABEL: @test2(
; CHECK-NEXT: ret void
;
%B = alloca [16 x i8]
%call = call ptr @strncpy(ptr %B, ptr %src, i64 12)
ret void
}
declare ptr @strcat(ptr %B, ptr %src) nounwind
define void @test3(ptr %src) {
; CHECK-LABEL: @test3(
; CHECK-NEXT: ret void
;
%B = alloca [16 x i8]
%call = call ptr @strcat(ptr %B, ptr %src)
ret void
}
define void @test_strcat_with_lifetime(ptr %src) {
; CHECK-LABEL: @test_strcat_with_lifetime(
; CHECK-NEXT: [[B:%.*]] = alloca [16 x i8], align 1
; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr nonnull [[B]])
; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr nonnull [[B]])
; CHECK-NEXT: ret void
;
%B = alloca [16 x i8]
call void @llvm.lifetime.start.p0(ptr nonnull %B)
%call = call ptr @strcat(ptr %B, ptr %src)
call void @llvm.lifetime.end.p0(ptr nonnull %B)
ret void
}
declare ptr @strncat(ptr %dest, ptr %src, i64 %n) nounwind
define void @test4(ptr %src) {
; CHECK-LABEL: @test4(
; CHECK-NEXT: ret void
;
%B = alloca [16 x i8]
%call = call ptr @strncat(ptr %B, ptr %src, i64 12)
ret void
}
define void @test5(ptr nocapture %src) {
; CHECK-LABEL: @test5(
; CHECK-NEXT: ret void
;
%dest = alloca [100 x i8], align 16
%call = call ptr @strcpy(ptr %dest, ptr %src)
%arrayidx = getelementptr inbounds i8, ptr %call, i64 10
store i8 97, ptr %arrayidx, align 1
ret void
}
declare void @user(ptr %p)
define void @test6(ptr %src) {
; CHECK-LABEL: @test6(
; CHECK-NEXT: [[B:%.*]] = alloca [16 x i8], align 1
; CHECK-NEXT: [[CALL:%.*]] = call ptr @strcpy(ptr [[B]], ptr [[SRC:%.*]])
; CHECK-NEXT: call void @user(ptr [[B]])
; CHECK-NEXT: ret void
;
%B = alloca [16 x i8]
%call = call ptr @strcpy(ptr %B, ptr %src)
call void @user(ptr %B)
ret void
}
declare i32 @memcmp(ptr, ptr, i64)
define i32 @test_memcmp_const_size(ptr noalias %foo) {
; CHECK-LABEL: @test_memcmp_const_size(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[STACK:%.*]] = alloca [10 x i8], align 1
; CHECK-NEXT: store i8 49, ptr [[STACK]], align 1
; CHECK-NEXT: [[GEP_1:%.*]] = getelementptr i8, ptr [[STACK]], i64 1
; CHECK-NEXT: store i8 50, ptr [[GEP_1]], align 1
; CHECK-NEXT: [[RES:%.*]] = call i32 @memcmp(ptr nonnull dereferenceable(2) [[FOO:%.*]], ptr nonnull dereferenceable(2) [[STACK]], i64 2)
; CHECK-NEXT: ret i32 [[RES]]
;
entry:
%stack = alloca [10 x i8]
store i8 49, ptr %stack, align 1
%gep.1 = getelementptr i8, ptr %stack, i64 1
store i8 50, ptr %gep.1, align 1
%gep.2 = getelementptr i8, ptr %stack, i64 2
store i8 51, ptr %gep.2, align 1
%gep.3 = getelementptr i8, ptr %stack, i64 3
store i8 52, ptr %gep.3, align 1
%res = call i32 @memcmp(ptr nonnull dereferenceable(2) %foo, ptr nonnull dereferenceable(2) %stack, i64 2)
ret i32 %res
}
define i32 @test_memcmp_variable_size(ptr noalias %foo, i64 %n) {
; CHECK-LABEL: @test_memcmp_variable_size(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[STACK:%.*]] = alloca [10 x i8], align 1
; CHECK-NEXT: store i8 49, ptr [[STACK]], align 1
; CHECK-NEXT: [[GEP_1:%.*]] = getelementptr i8, ptr [[STACK]], i64 1
; CHECK-NEXT: store i8 50, ptr [[GEP_1]], align 1
; CHECK-NEXT: [[GEP_2:%.*]] = getelementptr i8, ptr [[STACK]], i64 2
; CHECK-NEXT: store i8 51, ptr [[GEP_2]], align 1
; CHECK-NEXT: [[GEP_3:%.*]] = getelementptr i8, ptr [[STACK]], i64 3
; CHECK-NEXT: store i8 52, ptr [[GEP_3]], align 1
; CHECK-NEXT: [[RES:%.*]] = call i32 @memcmp(ptr nonnull [[FOO:%.*]], ptr nonnull [[STACK]], i64 [[N:%.*]])
; CHECK-NEXT: ret i32 [[RES]]
;
entry:
%stack = alloca [10 x i8]
store i8 49, ptr %stack, align 1
%gep.1 = getelementptr i8, ptr %stack, i64 1
store i8 50, ptr %gep.1, align 1
%gep.2 = getelementptr i8, ptr %stack, i64 2
store i8 51, ptr %gep.2, align 1
%gep.3 = getelementptr i8, ptr %stack, i64 3
store i8 52, ptr %gep.3, align 1
%res = call i32 @memcmp(ptr nonnull %foo, ptr nonnull %stack, i64 %n)
ret i32 %res
}
declare i32 @bcmp(ptr, ptr, i64)
define i1 @test_bcmp_const_size(ptr noalias %foo) {
; CHECK-LABEL: @test_bcmp_const_size(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[STACK:%.*]] = alloca [10 x i8], align 1
; CHECK-NEXT: store i8 49, ptr [[STACK]], align 1
; CHECK-NEXT: [[GEP_1:%.*]] = getelementptr i8, ptr [[STACK]], i64 1
; CHECK-NEXT: store i8 50, ptr [[GEP_1]], align 1
; CHECK-NEXT: [[CALL:%.*]] = call i32 @bcmp(ptr nonnull dereferenceable(2) [[FOO:%.*]], ptr nonnull dereferenceable(2) [[STACK]], i64 2)
; CHECK-NEXT: [[RES:%.*]] = icmp eq i32 [[CALL]], 0
; CHECK-NEXT: ret i1 [[RES]]
;
entry:
%stack = alloca [10 x i8]
store i8 49, ptr %stack, align 1
%gep.1 = getelementptr i8, ptr %stack, i64 1
store i8 50, ptr %gep.1, align 1
%gep.2 = getelementptr i8, ptr %stack, i64 2
store i8 51, ptr %gep.2, align 1
%gep.3 = getelementptr i8, ptr %stack, i64 3
store i8 52, ptr %gep.3, align 1
%call = call i32 @bcmp(ptr nonnull dereferenceable(2) %foo, ptr nonnull dereferenceable(2) %stack, i64 2)
%res = icmp eq i32 %call, 0
ret i1 %res
}
define i1 @test_bcmp_variable_size(ptr noalias %foo, i64 %n) {
; CHECK-LABEL: @test_bcmp_variable_size(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[STACK:%.*]] = alloca [10 x i8], align 1
; CHECK-NEXT: store i8 49, ptr [[STACK]], align 1
; CHECK-NEXT: [[GEP_1:%.*]] = getelementptr i8, ptr [[STACK]], i64 1
; CHECK-NEXT: store i8 50, ptr [[GEP_1]], align 1
; CHECK-NEXT: [[GEP_2:%.*]] = getelementptr i8, ptr [[STACK]], i64 2
; CHECK-NEXT: store i8 51, ptr [[GEP_2]], align 1
; CHECK-NEXT: [[GEP_3:%.*]] = getelementptr i8, ptr [[STACK]], i64 3
; CHECK-NEXT: store i8 52, ptr [[GEP_3]], align 1
; CHECK-NEXT: [[CALL:%.*]] = call i32 @bcmp(ptr nonnull [[FOO:%.*]], ptr nonnull [[STACK]], i64 [[N:%.*]])
; CHECK-NEXT: [[RES:%.*]] = icmp eq i32 [[CALL]], 0
; CHECK-NEXT: ret i1 [[RES]]
;
entry:
%stack = alloca [10 x i8]
store i8 49, ptr %stack, align 1
%gep.1 = getelementptr i8, ptr %stack, i64 1
store i8 50, ptr %gep.1, align 1
%gep.2 = getelementptr i8, ptr %stack, i64 2
store i8 51, ptr %gep.2, align 1
%gep.3 = getelementptr i8, ptr %stack, i64 3
store i8 52, ptr %gep.3, align 1
%call = call i32 @bcmp(ptr nonnull %foo, ptr nonnull %stack, i64 %n)
%res = icmp eq i32 %call, 0
ret i1 %res
}
declare ptr @memchr(ptr, i32, i64)
define ptr @test_memchr_const_size() {
; CHECK-LABEL: @test_memchr_const_size(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[STACK:%.*]] = alloca [10 x i8], align 1
; CHECK-NEXT: store i8 49, ptr [[STACK]], align 1
; CHECK-NEXT: [[GEP_1:%.*]] = getelementptr i8, ptr [[STACK]], i64 1
; CHECK-NEXT: store i8 50, ptr [[GEP_1]], align 1
; CHECK-NEXT: [[CALL:%.*]] = call ptr @memchr(ptr [[STACK]], i32 42, i64 2)
; CHECK-NEXT: ret ptr [[CALL]]
;
entry:
%stack = alloca [10 x i8]
store i8 49, ptr %stack, align 1
%gep.1 = getelementptr i8, ptr %stack, i64 1
store i8 50, ptr %gep.1, align 1
%gep.2 = getelementptr i8, ptr %stack, i64 2
store i8 51, ptr %gep.2, align 1
%gep.3 = getelementptr i8, ptr %stack, i64 3
store i8 52, ptr %gep.3, align 1
%call = call ptr @memchr(ptr %stack, i32 42, i64 2)
ret ptr %call
}
define ptr @test_memchr_variable_size(i64 %n) {
; CHECK-LABEL: @test_memchr_variable_size(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[STACK:%.*]] = alloca [10 x i8], align 1
; CHECK-NEXT: store i8 49, ptr [[STACK]], align 1
; CHECK-NEXT: [[GEP_1:%.*]] = getelementptr i8, ptr [[STACK]], i64 1
; CHECK-NEXT: store i8 50, ptr [[GEP_1]], align 1
; CHECK-NEXT: [[GEP_2:%.*]] = getelementptr i8, ptr [[STACK]], i64 2
; CHECK-NEXT: store i8 51, ptr [[GEP_2]], align 1
; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, ptr [[STACK]], i64 4
; CHECK-NEXT: store i8 52, ptr [[GEP]], align 1
; CHECK-NEXT: [[CALL:%.*]] = call ptr @memchr(ptr [[STACK]], i32 42, i64 [[N:%.*]])
; CHECK-NEXT: ret ptr [[CALL]]
;
entry:
%stack = alloca [10 x i8]
store i8 49, ptr %stack, align 1
%gep.1 = getelementptr i8, ptr %stack, i64 1
store i8 50, ptr %gep.1, align 1
%gep.2 = getelementptr i8, ptr %stack, i64 2
store i8 51, ptr %gep.2, align 1
%gep = getelementptr i8, ptr %stack, i64 4
store i8 52, ptr %gep, align 1
%call = call ptr @memchr(ptr %stack, i32 42, i64 %n)
ret ptr %call
}
declare ptr @memccpy(ptr, ptr, i32, i64)
define ptr @test_memccpy_const_size(ptr %foo) {
; CHECK-LABEL: @test_memccpy_const_size(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[STACK:%.*]] = alloca [10 x i8], align 1
; CHECK-NEXT: store i8 49, ptr [[STACK]], align 1
; CHECK-NEXT: [[GEP_1:%.*]] = getelementptr i8, ptr [[STACK]], i64 1
; CHECK-NEXT: store i8 50, ptr [[GEP_1]], align 1
; CHECK-NEXT: [[RES:%.*]] = call ptr @memccpy(ptr [[FOO:%.*]], ptr [[STACK]], i32 42, i64 2)
; CHECK-NEXT: ret ptr [[RES]]
;
entry:
%stack = alloca [10 x i8]
store i8 49, ptr %stack, align 1
%gep.1 = getelementptr i8, ptr %stack, i64 1
store i8 50, ptr %gep.1, align 1
%gep.2 = getelementptr i8, ptr %stack, i64 2
store i8 51, ptr %gep.2, align 1
%gep.3 = getelementptr i8, ptr %stack, i64 3
store i8 52, ptr %gep.3, align 1
%res = call ptr @memccpy(ptr %foo, ptr %stack, i32 42, i64 2)
ret ptr %res
}
define ptr @test_memccpy_variable_size(ptr %foo, i64 %n) {
; CHECK-LABEL: @test_memccpy_variable_size(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[STACK:%.*]] = alloca [10 x i8], align 1
; CHECK-NEXT: store i8 49, ptr [[STACK]], align 1
; CHECK-NEXT: [[GEP_1:%.*]] = getelementptr i8, ptr [[STACK]], i64 1
; CHECK-NEXT: store i8 50, ptr [[GEP_1]], align 1
; CHECK-NEXT: [[GEP_2:%.*]] = getelementptr i8, ptr [[STACK]], i64 2
; CHECK-NEXT: store i8 51, ptr [[GEP_2]], align 1
; CHECK-NEXT: [[GEP_3:%.*]] = getelementptr i8, ptr [[STACK]], i64 3
; CHECK-NEXT: store i8 52, ptr [[GEP_3]], align 1
; CHECK-NEXT: [[RES:%.*]] = call ptr @memccpy(ptr [[FOO:%.*]], ptr [[STACK]], i32 42, i64 [[N:%.*]])
; CHECK-NEXT: ret ptr [[RES]]
;
entry:
%stack = alloca [10 x i8]
store i8 49, ptr %stack, align 1
%gep.1 = getelementptr i8, ptr %stack, i64 1
store i8 50, ptr %gep.1, align 1
%gep.2 = getelementptr i8, ptr %stack, i64 2
store i8 51, ptr %gep.2, align 1
%gep.3 = getelementptr i8, ptr %stack, i64 3
store i8 52, ptr %gep.3, align 1
%res = call ptr @memccpy(ptr %foo, ptr %stack, i32 42, i64 %n)
ret ptr %res
}
; Make sure memccpy does not kill any stores, because it is not known how many
; bytes are written.
define ptr @test_memccpy_const_size_does_not_kill_stores(ptr noalias %dest, ptr noalias %foo) {
; CHECK-LABEL: @test_memccpy_const_size_does_not_kill_stores(
; CHECK-NEXT: entry:
; CHECK-NEXT: store i8 49, ptr [[DEST:%.*]], align 1
; CHECK-NEXT: [[GEP_1:%.*]] = getelementptr i8, ptr [[DEST]], i64 1
; CHECK-NEXT: store i8 50, ptr [[GEP_1]], align 1
; CHECK-NEXT: [[GEP_2:%.*]] = getelementptr i8, ptr [[DEST]], i64 2
; CHECK-NEXT: store i8 51, ptr [[GEP_2]], align 1
; CHECK-NEXT: [[GEP_3:%.*]] = getelementptr i8, ptr [[DEST]], i64 3
; CHECK-NEXT: store i8 52, ptr [[GEP_3]], align 1
; CHECK-NEXT: [[RES:%.*]] = call ptr @memccpy(ptr [[DEST]], ptr [[FOO:%.*]], i32 42, i64 2)
; CHECK-NEXT: ret ptr [[RES]]
;
entry:
store i8 49, ptr %dest, align 1
%gep.1 = getelementptr i8, ptr %dest, i64 1
store i8 50, ptr %gep.1, align 1
%gep.2 = getelementptr i8, ptr %dest, i64 2
store i8 51, ptr %gep.2, align 1
%gep.3 = getelementptr i8, ptr %dest, i64 3
store i8 52, ptr %gep.3, align 1
%res = call ptr @memccpy(ptr %dest, ptr %foo, i32 42, i64 2)
ret ptr %res
}
define void @dse_strcpy(ptr nocapture readonly %src) {
; CHECK-LABEL: @dse_strcpy(
; CHECK-NEXT: [[A:%.*]] = alloca [256 x i8], align 16
; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr nonnull [[A]])
; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr nonnull [[A]])
; CHECK-NEXT: ret void
;
%a = alloca [256 x i8], align 16
call void @llvm.lifetime.start.p0(ptr nonnull %a)
call ptr @strcpy(ptr nonnull %a, ptr nonnull dereferenceable(1) %src)
call void @llvm.lifetime.end.p0(ptr nonnull %a)
ret void
}
define void @dse_strncpy(ptr nocapture readonly %src) {
; CHECK-LABEL: @dse_strncpy(
; CHECK-NEXT: [[A:%.*]] = alloca [256 x i8], align 16
; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr nonnull [[A]])
; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr nonnull [[A]])
; CHECK-NEXT: ret void
;
%a = alloca [256 x i8], align 16
call void @llvm.lifetime.start.p0(ptr nonnull %a)
call ptr @strncpy(ptr nonnull %a, ptr nonnull dereferenceable(1) %src, i64 6)
call void @llvm.lifetime.end.p0(ptr nonnull %a)
ret void
}
define void @dse_strcat(ptr nocapture readonly %src) {
; CHECK-LABEL: @dse_strcat(
; CHECK-NEXT: [[A:%.*]] = alloca [256 x i8], align 16
; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr nonnull [[A]])
; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr nonnull [[A]])
; CHECK-NEXT: ret void
;
%a = alloca [256 x i8], align 16
call void @llvm.lifetime.start.p0(ptr nonnull %a)
call ptr @strcat(ptr nonnull %a, ptr nonnull dereferenceable(1) %src)
call void @llvm.lifetime.end.p0(ptr nonnull %a)
ret void
}
define void @dse_strncat(ptr nocapture readonly %src) {
; CHECK-LABEL: @dse_strncat(
; CHECK-NEXT: [[A:%.*]] = alloca [256 x i8], align 16
; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr nonnull [[A]])
; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr nonnull [[A]])
; CHECK-NEXT: ret void
;
%a = alloca [256 x i8], align 16
call void @llvm.lifetime.start.p0(ptr nonnull %a)
call ptr @strncat(ptr nonnull %a, ptr nonnull dereferenceable(1) %src, i64 6)
call void @llvm.lifetime.end.p0(ptr nonnull %a)
ret void
}
declare void @llvm.lifetime.start.p0(ptr nocapture)
declare void @llvm.lifetime.end.p0(ptr nocapture)
declare void @llvm.memset.p0.i64(ptr nocapture, i8, i64, i1) nounwind
; Test that strncpy/memset overwriting each other is optimized out
; strncpy -> memset, full overwrite
define void @dse_strncpy_test1(ptr noalias %out, ptr noalias %in) {
; CHECK-LABEL: @dse_strncpy_test1(
; CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr [[OUT:%.*]], i8 42, i64 100, i1 false)
; CHECK-NEXT: ret void
;
%call = tail call ptr @strncpy(ptr %out, ptr %in, i64 100)
tail call void @llvm.memset.p0.i64(ptr %out, i8 42, i64 100, i1 false)
ret void
}
; strncpy -> memset, partial overwrite
define void @dse_strncpy_test2(ptr noalias %out, ptr noalias %in) {
; CHECK-LABEL: @dse_strncpy_test2(
; CHECK-NEXT: [[CALL:%.*]] = tail call ptr @strncpy(ptr [[OUT:%.*]], ptr [[IN:%.*]], i64 100)
; CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr [[OUT]], i8 42, i64 99, i1 false)
; CHECK-NEXT: ret void
;
%call = tail call ptr @strncpy(ptr %out, ptr %in, i64 100)
tail call void @llvm.memset.p0.i64(ptr %out, i8 42, i64 99, i1 false)
ret void
}
; strncpy -> memset, different destination
define void @dse_strncpy_test3(ptr noalias %out1, ptr noalias %out2, ptr noalias %in) {
; CHECK-LABEL: @dse_strncpy_test3(
; CHECK-NEXT: [[CALL:%.*]] = tail call ptr @strncpy(ptr [[OUT1:%.*]], ptr [[IN:%.*]], i64 100)
; CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr [[OUT2:%.*]], i8 42, i64 100, i1 false)
; CHECK-NEXT: ret void
;
%call = tail call ptr @strncpy(ptr %out1, ptr %in, i64 100)
tail call void @llvm.memset.p0.i64(ptr %out2, i8 42, i64 100, i1 false)
ret void
}
; memset -> strncpy, full overwrite
define void @dse_strncpy_test4(ptr noalias %out, ptr noalias %in) {
; CHECK-LABEL: @dse_strncpy_test4(
; CHECK-NEXT: [[CALL:%.*]] = tail call ptr @strncpy(ptr [[OUT:%.*]], ptr [[IN:%.*]], i64 100)
; CHECK-NEXT: ret void
;
tail call void @llvm.memset.p0.i64(ptr %out, i8 42, i64 100, i1 false)
%call = tail call ptr @strncpy(ptr %out, ptr %in, i64 100)
ret void
}
; memset -> strncpy, partial overwrite
define void @dse_strncpy_test5(ptr noalias %out, ptr noalias %in) {
; CHECK-LABEL: @dse_strncpy_test5(
; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[OUT:%.*]], i64 99
; CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr align 1 [[TMP1]], i8 42, i64 1, i1 false)
; CHECK-NEXT: [[CALL:%.*]] = tail call ptr @strncpy(ptr [[OUT]], ptr [[IN:%.*]], i64 99)
; CHECK-NEXT: ret void
;
tail call void @llvm.memset.p0.i64(ptr %out, i8 42, i64 100, i1 false)
%call = tail call ptr @strncpy(ptr %out, ptr %in, i64 99)
ret void
}
; memset -> strncpy, different destination
define void @dse_strncpy_test6(ptr noalias %out1, ptr noalias %out2, ptr noalias %in) {
; CHECK-LABEL: @dse_strncpy_test6(
; CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr [[OUT1:%.*]], i8 42, i64 100, i1 false)
; CHECK-NEXT: [[CALL:%.*]] = tail call ptr @strncpy(ptr [[OUT2:%.*]], ptr [[IN:%.*]], i64 100)
; CHECK-NEXT: ret void
;
tail call void @llvm.memset.p0.i64(ptr %out1, i8 42, i64 100, i1 false)
%call = tail call ptr @strncpy(ptr %out2, ptr %in, i64 100)
ret void
}