llvm-project/llvm/test/Transforms/MemCpyOpt/memset-memcpy-redundant-memset.ll
Nikita Popov 71051deff2
[MemCpyOpt] Fix infinite loop in memset+memcpy fold (#98638)
For the case where the memcpy size is zero, this transform is a complex
no-op. This can lead to an infinite loop when the size is zero in a way
that BasicAA understands, because it can still understand that dst and
dst + src_size are MustAlias.

I've tried to mitigate this before using the isZeroSize() check, but we
can hit cases where InstSimplify doesn't understand that the size is
zero, but BasicAA does.

As such, this bites the bullet and adds an explicit isKnownNonZero()
check to guard against no-op transforms.

Fixes https://github.com/llvm/llvm-project/issues/98610.
2024-07-15 09:41:11 +02:00

405 lines
21 KiB
LLVM

; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt -passes=memcpyopt -S %s -verify-memoryssa | FileCheck %s
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
@C = external constant [0 x i8]
define void @test_constant(i64 %src_size, ptr %dst, i64 %dst_size, i8 %c) {
; CHECK-LABEL: @test_constant(
; CHECK-NEXT: [[NON_ZERO:%.*]] = icmp ne i64 [[SRC_SIZE:%.*]], 0
; CHECK-NEXT: call void @llvm.assume(i1 [[NON_ZERO]])
; CHECK-NEXT: [[TMP1:%.*]] = icmp ule i64 [[DST_SIZE:%.*]], [[SRC_SIZE]]
; CHECK-NEXT: [[TMP2:%.*]] = sub i64 [[DST_SIZE]], [[SRC_SIZE]]
; CHECK-NEXT: [[TMP3:%.*]] = select i1 [[TMP1]], i64 0, i64 [[TMP2]]
; CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[DST:%.*]], i64 [[SRC_SIZE]]
; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 1 [[TMP4]], i8 [[C:%.*]], i64 [[TMP3]], i1 false)
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[DST]], ptr @C, i64 [[SRC_SIZE]], i1 false)
; CHECK-NEXT: ret void
;
%non.zero = icmp ne i64 %src_size, 0
call void @llvm.assume(i1 %non.zero)
call void @llvm.memset.p0.i64(ptr %dst, i8 %c, i64 %dst_size, i1 false)
call void @llvm.memcpy.p0.p0.i64(ptr %dst, ptr @C, i64 %src_size, i1 false)
ret void
}
define void @test(ptr %src, i64 %src_size, ptr noalias %dst, i64 %dst_size, i8 %c) {
; CHECK-LABEL: @test(
; CHECK-NEXT: [[NON_ZERO:%.*]] = icmp ne i64 [[SRC_SIZE:%.*]], 0
; CHECK-NEXT: call void @llvm.assume(i1 [[NON_ZERO]])
; CHECK-NEXT: [[TMP1:%.*]] = icmp ule i64 [[DST_SIZE:%.*]], [[SRC_SIZE]]
; CHECK-NEXT: [[TMP2:%.*]] = sub i64 [[DST_SIZE]], [[SRC_SIZE]]
; CHECK-NEXT: [[TMP3:%.*]] = select i1 [[TMP1]], i64 0, i64 [[TMP2]]
; CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[DST:%.*]], i64 [[SRC_SIZE]]
; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 1 [[TMP4]], i8 [[C:%.*]], i64 [[TMP3]], i1 false)
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[DST]], ptr [[SRC:%.*]], i64 [[SRC_SIZE]], i1 false)
; CHECK-NEXT: ret void
;
%non.zero = icmp ne i64 %src_size, 0
call void @llvm.assume(i1 %non.zero)
call void @llvm.memset.p0.i64(ptr %dst, i8 %c, i64 %dst_size, i1 false)
call void @llvm.memcpy.p0.p0.i64(ptr %dst, ptr %src, i64 %src_size, i1 false)
ret void
}
define void @test_different_types_i32_i64(ptr noalias %dst, ptr %src, i32 %dst_size, i64 %src_size, i8 %c) {
; CHECK-LABEL: @test_different_types_i32_i64(
; CHECK-NEXT: [[NON_ZERO:%.*]] = icmp ne i64 [[SRC_SIZE:%.*]], 0
; CHECK-NEXT: call void @llvm.assume(i1 [[NON_ZERO]])
; CHECK-NEXT: [[TMP1:%.*]] = zext i32 [[DST_SIZE:%.*]] to i64
; CHECK-NEXT: [[TMP2:%.*]] = icmp ule i64 [[TMP1]], [[SRC_SIZE]]
; CHECK-NEXT: [[TMP3:%.*]] = sub i64 [[TMP1]], [[SRC_SIZE]]
; CHECK-NEXT: [[TMP4:%.*]] = select i1 [[TMP2]], i64 0, i64 [[TMP3]]
; CHECK-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[DST:%.*]], i64 [[SRC_SIZE]]
; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 1 [[TMP5]], i8 [[C:%.*]], i64 [[TMP4]], i1 false)
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[DST]], ptr [[SRC:%.*]], i64 [[SRC_SIZE]], i1 false)
; CHECK-NEXT: ret void
;
%non.zero = icmp ne i64 %src_size, 0
call void @llvm.assume(i1 %non.zero)
call void @llvm.memset.p0.i32(ptr %dst, i8 %c, i32 %dst_size, i1 false)
call void @llvm.memcpy.p0.p0.i64(ptr %dst, ptr %src, i64 %src_size, i1 false)
ret void
}
define void @test_different_types_i128_i32(ptr noalias %dst, ptr %src, i128 %dst_size, i32 %src_size, i8 %c) {
; CHECK-LABEL: @test_different_types_i128_i32(
; CHECK-NEXT: [[NON_ZERO:%.*]] = icmp ne i32 [[SRC_SIZE:%.*]], 0
; CHECK-NEXT: call void @llvm.assume(i1 [[NON_ZERO]])
; CHECK-NEXT: [[TMP1:%.*]] = zext i32 [[SRC_SIZE]] to i128
; CHECK-NEXT: [[TMP2:%.*]] = icmp ule i128 [[DST_SIZE:%.*]], [[TMP1]]
; CHECK-NEXT: [[TMP3:%.*]] = sub i128 [[DST_SIZE]], [[TMP1]]
; CHECK-NEXT: [[TMP4:%.*]] = select i1 [[TMP2]], i128 0, i128 [[TMP3]]
; CHECK-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[DST:%.*]], i128 [[TMP1]]
; CHECK-NEXT: call void @llvm.memset.p0.i128(ptr align 1 [[TMP5]], i8 [[C:%.*]], i128 [[TMP4]], i1 false)
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr [[DST]], ptr [[SRC:%.*]], i32 [[SRC_SIZE]], i1 false)
; CHECK-NEXT: ret void
;
%non.zero = icmp ne i32 %src_size, 0
call void @llvm.assume(i1 %non.zero)
call void @llvm.memset.p0.i128(ptr %dst, i8 %c, i128 %dst_size, i1 false)
call void @llvm.memcpy.p0.p0.i32(ptr %dst, ptr %src, i32 %src_size, i1 false)
ret void
}
define void @test_different_types_i32_i128(ptr noalias %dst, ptr %src, i32 %dst_size, i128 %src_size, i8 %c) {
; CHECK-LABEL: @test_different_types_i32_i128(
; CHECK-NEXT: [[NON_ZERO:%.*]] = icmp ne i128 [[SRC_SIZE:%.*]], 0
; CHECK-NEXT: call void @llvm.assume(i1 [[NON_ZERO]])
; CHECK-NEXT: [[TMP1:%.*]] = zext i32 [[DST_SIZE:%.*]] to i128
; CHECK-NEXT: [[TMP2:%.*]] = icmp ule i128 [[TMP1]], [[SRC_SIZE]]
; CHECK-NEXT: [[TMP3:%.*]] = sub i128 [[TMP1]], [[SRC_SIZE]]
; CHECK-NEXT: [[TMP4:%.*]] = select i1 [[TMP2]], i128 0, i128 [[TMP3]]
; CHECK-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[DST:%.*]], i128 [[SRC_SIZE]]
; CHECK-NEXT: call void @llvm.memset.p0.i128(ptr align 1 [[TMP5]], i8 [[C:%.*]], i128 [[TMP4]], i1 false)
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i128(ptr [[DST]], ptr [[SRC:%.*]], i128 [[SRC_SIZE]], i1 false)
; CHECK-NEXT: ret void
;
%non.zero = icmp ne i128 %src_size, 0
call void @llvm.assume(i1 %non.zero)
call void @llvm.memset.p0.i32(ptr %dst, i8 %c, i32 %dst_size, i1 false)
call void @llvm.memcpy.p0.p0.i128(ptr %dst, ptr %src, i128 %src_size, i1 false)
ret void
}
define void @test_different_types_i64_i32(ptr noalias %dst, ptr %src, i64 %dst_size, i32 %src_size, i8 %c) {
; CHECK-LABEL: @test_different_types_i64_i32(
; CHECK-NEXT: [[NON_ZERO:%.*]] = icmp ne i32 [[SRC_SIZE:%.*]], 0
; CHECK-NEXT: call void @llvm.assume(i1 [[NON_ZERO]])
; CHECK-NEXT: [[TMP1:%.*]] = zext i32 [[SRC_SIZE]] to i64
; CHECK-NEXT: [[TMP2:%.*]] = icmp ule i64 [[DST_SIZE:%.*]], [[TMP1]]
; CHECK-NEXT: [[TMP3:%.*]] = sub i64 [[DST_SIZE]], [[TMP1]]
; CHECK-NEXT: [[TMP4:%.*]] = select i1 [[TMP2]], i64 0, i64 [[TMP3]]
; CHECK-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[DST:%.*]], i64 [[TMP1]]
; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 1 [[TMP5]], i8 [[C:%.*]], i64 [[TMP4]], i1 false)
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr [[DST]], ptr [[SRC:%.*]], i32 [[SRC_SIZE]], i1 false)
; CHECK-NEXT: ret void
;
%non.zero = icmp ne i32 %src_size, 0
call void @llvm.assume(i1 %non.zero)
call void @llvm.memset.p0.i64(ptr %dst, i8 %c, i64 %dst_size, i1 false)
call void @llvm.memcpy.p0.p0.i32(ptr %dst, ptr %src, i32 %src_size, i1 false)
ret void
}
define void @test_align_same(ptr %src, ptr noalias %dst, i64 %dst_size) {
; CHECK-LABEL: @test_align_same(
; CHECK-NEXT: [[TMP1:%.*]] = icmp ule i64 [[DST_SIZE:%.*]], 80
; CHECK-NEXT: [[TMP2:%.*]] = sub i64 [[DST_SIZE]], 80
; CHECK-NEXT: [[TMP3:%.*]] = select i1 [[TMP1]], i64 0, i64 [[TMP2]]
; CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[DST:%.*]], i64 80
; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[TMP4]], i8 0, i64 [[TMP3]], i1 false)
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[DST]], ptr [[SRC:%.*]], i64 80, i1 false)
; CHECK-NEXT: ret void
;
call void @llvm.memset.p0.i64(ptr align 8 %dst, i8 0, i64 %dst_size, i1 false)
call void @llvm.memcpy.p0.p0.i64(ptr %dst, ptr %src, i64 80, i1 false)
ret void
}
define void @test_align_min(ptr %src, ptr noalias %dst, i64 %dst_size) {
; CHECK-LABEL: @test_align_min(
; CHECK-NEXT: [[TMP1:%.*]] = icmp ule i64 [[DST_SIZE:%.*]], 36
; CHECK-NEXT: [[TMP2:%.*]] = sub i64 [[DST_SIZE]], 36
; CHECK-NEXT: [[TMP3:%.*]] = select i1 [[TMP1]], i64 0, i64 [[TMP2]]
; CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[DST:%.*]], i64 36
; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 [[TMP3]], i1 false)
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[DST]], ptr [[SRC:%.*]], i64 36, i1 false)
; CHECK-NEXT: ret void
;
call void @llvm.memset.p0.i64(ptr align 8 %dst, i8 0, i64 %dst_size, i1 false)
call void @llvm.memcpy.p0.p0.i64(ptr %dst, ptr %src, i64 36, i1 false)
ret void
}
define void @test_align_memcpy(ptr %src, ptr noalias %dst, i64 %dst_size) {
; CHECK-LABEL: @test_align_memcpy(
; CHECK-NEXT: [[TMP1:%.*]] = icmp ule i64 [[DST_SIZE:%.*]], 80
; CHECK-NEXT: [[TMP2:%.*]] = sub i64 [[DST_SIZE]], 80
; CHECK-NEXT: [[TMP3:%.*]] = select i1 [[TMP1]], i64 0, i64 [[TMP2]]
; CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[DST:%.*]], i64 80
; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[TMP4]], i8 0, i64 [[TMP3]], i1 false)
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[DST]], ptr align 8 [[SRC:%.*]], i64 80, i1 false)
; CHECK-NEXT: ret void
;
call void @llvm.memset.p0.i64(ptr %dst, i8 0, i64 %dst_size, i1 false)
call void @llvm.memcpy.p0.p0.i64(ptr align 8 %dst, ptr align 8 %src, i64 80, i1 false)
ret void
}
define void @test_non_i8_dst_type(ptr %src, i64 %src_size, ptr noalias %dst_pi64, i64 %dst_size, i8 %c) {
; CHECK-LABEL: @test_non_i8_dst_type(
; CHECK-NEXT: [[NON_ZERO:%.*]] = icmp ne i64 [[SRC_SIZE:%.*]], 0
; CHECK-NEXT: call void @llvm.assume(i1 [[NON_ZERO]])
; CHECK-NEXT: [[TMP1:%.*]] = icmp ule i64 [[DST_SIZE:%.*]], [[SRC_SIZE]]
; CHECK-NEXT: [[TMP2:%.*]] = sub i64 [[DST_SIZE]], [[SRC_SIZE]]
; CHECK-NEXT: [[TMP3:%.*]] = select i1 [[TMP1]], i64 0, i64 [[TMP2]]
; CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[DST_PI64:%.*]], i64 [[SRC_SIZE]]
; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 1 [[TMP4]], i8 [[C:%.*]], i64 [[TMP3]], i1 false)
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[DST_PI64]], ptr [[SRC:%.*]], i64 [[SRC_SIZE]], i1 false)
; CHECK-NEXT: ret void
;
%non.zero = icmp ne i64 %src_size, 0
call void @llvm.assume(i1 %non.zero)
call void @llvm.memset.p0.i64(ptr %dst_pi64, i8 %c, i64 %dst_size, i1 false)
call void @llvm.memcpy.p0.p0.i64(ptr %dst_pi64, ptr %src, i64 %src_size, i1 false)
ret void
}
define void @test_different_dst(ptr noalias %dst2, ptr %src, i64 %src_size, ptr noalias %dst, i64 %dst_size) {
; CHECK-LABEL: @test_different_dst(
; CHECK-NEXT: [[NON_ZERO:%.*]] = icmp ne i64 [[SRC_SIZE:%.*]], 0
; CHECK-NEXT: call void @llvm.assume(i1 [[NON_ZERO]])
; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr [[DST:%.*]], i8 0, i64 [[DST_SIZE:%.*]], i1 false)
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[DST2:%.*]], ptr [[SRC:%.*]], i64 [[SRC_SIZE]], i1 false)
; CHECK-NEXT: ret void
;
%non.zero = icmp ne i64 %src_size, 0
call void @llvm.assume(i1 %non.zero)
call void @llvm.memset.p0.i64(ptr %dst, i8 0, i64 %dst_size, i1 false)
call void @llvm.memcpy.p0.p0.i64(ptr %dst2, ptr %src, i64 %src_size, i1 false)
ret void
}
; Make sure we also take into account dependencies on the destination.
define i8 @test_intermediate_read(ptr noalias %a, ptr %b) #0 {
; CHECK-LABEL: @test_intermediate_read(
; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr [[A:%.*]], i8 0, i64 64, i1 false)
; CHECK-NEXT: [[R:%.*]] = load i8, ptr [[A]], align 1
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[A]], ptr [[B:%.*]], i64 24, i1 false)
; CHECK-NEXT: ret i8 [[R]]
;
call void @llvm.memset.p0.i64(ptr %a, i8 0, i64 64, i1 false)
%r = load i8, ptr %a
call void @llvm.memcpy.p0.p0.i64(ptr %a, ptr %b, i64 24, i1 false)
ret i8 %r
}
%struct = type { [8 x i8], [8 x i8] }
define void @test_intermediate_write(ptr %b) #0 {
; CHECK-LABEL: @test_intermediate_write(
; CHECK-NEXT: [[A:%.*]] = alloca [[STRUCT:%.*]], align 8
; CHECK-NEXT: [[A1:%.*]] = getelementptr [[STRUCT]], ptr [[A]], i32 0, i32 1, i32 0
; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr [[A]], i8 0, i64 16, i1 false)
; CHECK-NEXT: store i8 1, ptr [[A1]], align 1
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[A]], ptr [[B:%.*]], i64 8, i1 false)
; CHECK-NEXT: ret void
;
%a = alloca %struct
%a1 = getelementptr %struct, ptr %a, i32 0, i32 1, i32 0
call void @llvm.memset.p0.i64(ptr %a, i8 0, i64 16, i1 false)
store i8 1, ptr %a1
call void @llvm.memcpy.p0.p0.i64(ptr %a, ptr %b, i64 8, i1 false)
ret void
}
define void @test_throwing_call(ptr %src, i64 %src_size, ptr noalias %dst, i64 %dst_size, i8 %c) {
; CHECK-LABEL: @test_throwing_call(
; CHECK-NEXT: [[NON_ZERO:%.*]] = icmp ne i64 [[SRC_SIZE:%.*]], 0
; CHECK-NEXT: call void @llvm.assume(i1 [[NON_ZERO]])
; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr [[DST:%.*]], i8 [[C:%.*]], i64 [[DST_SIZE:%.*]], i1 false)
; CHECK-NEXT: call void @call() #[[ATTR3:[0-9]+]]
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[DST]], ptr [[SRC:%.*]], i64 [[SRC_SIZE]], i1 false)
; CHECK-NEXT: ret void
;
%non.zero = icmp ne i64 %src_size, 0
call void @llvm.assume(i1 %non.zero)
call void @llvm.memset.p0.i64(ptr %dst, i8 %c, i64 %dst_size, i1 false)
call void @call() readnone
call void @llvm.memcpy.p0.p0.i64(ptr %dst, ptr %src, i64 %src_size, i1 false)
ret void
}
define void @test_throwing_call_alloca(ptr %src, i64 %src_size, i64 %dst_size, i8 %c) {
; CHECK-LABEL: @test_throwing_call_alloca(
; CHECK-NEXT: [[DST:%.*]] = alloca i8, align 1
; CHECK-NEXT: [[NON_ZERO:%.*]] = icmp ne i64 [[SRC_SIZE:%.*]], 0
; CHECK-NEXT: call void @llvm.assume(i1 [[NON_ZERO]])
; CHECK-NEXT: call void @call() #[[ATTR3]]
; CHECK-NEXT: [[TMP1:%.*]] = icmp ule i64 [[DST_SIZE:%.*]], [[SRC_SIZE]]
; CHECK-NEXT: [[TMP2:%.*]] = sub i64 [[DST_SIZE]], [[SRC_SIZE]]
; CHECK-NEXT: [[TMP3:%.*]] = select i1 [[TMP1]], i64 0, i64 [[TMP2]]
; CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[DST]], i64 [[SRC_SIZE]]
; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 1 [[TMP4]], i8 [[C:%.*]], i64 [[TMP3]], i1 false)
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[DST]], ptr [[SRC:%.*]], i64 [[SRC_SIZE]], i1 false)
; CHECK-NEXT: ret void
;
%dst = alloca i8
%non.zero = icmp ne i64 %src_size, 0
call void @llvm.assume(i1 %non.zero)
call void @llvm.memset.p0.i64(ptr %dst, i8 %c, i64 %dst_size, i1 false)
call void @call() readnone
call void @llvm.memcpy.p0.p0.i64(ptr %dst, ptr %src, i64 %src_size, i1 false)
ret void
}
; %dst and %src in the memcpy may be equal, in which case shorting the memset
; is not legal.
define void @test_missing_noalias(ptr %src, i64 %src_size, ptr %dst, i64 %dst_size, i8 %c) {
; CHECK-LABEL: @test_missing_noalias(
; CHECK-NEXT: [[NON_ZERO:%.*]] = icmp ne i64 [[SRC_SIZE:%.*]], 0
; CHECK-NEXT: call void @llvm.assume(i1 [[NON_ZERO]])
; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr [[DST:%.*]], i8 [[C:%.*]], i64 [[DST_SIZE:%.*]], i1 false)
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[DST]], ptr [[SRC:%.*]], i64 [[SRC_SIZE]], i1 false)
; CHECK-NEXT: ret void
;
%non.zero = icmp ne i64 %src_size, 0
call void @llvm.assume(i1 %non.zero)
call void @llvm.memset.p0.i64(ptr %dst, i8 %c, i64 %dst_size, i1 false)
call void @llvm.memcpy.p0.p0.i64(ptr %dst, ptr %src, i64 %src_size, i1 false)
ret void
}
define void @test_same_const_size(ptr noalias %src, ptr noalias %dst, i8 %c) {
; CHECK-LABEL: @test_same_const_size(
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[DST:%.*]], ptr [[SRC:%.*]], i64 16, i1 false)
; CHECK-NEXT: ret void
;
call void @llvm.memset.p0.i64(ptr %dst, i8 %c, i64 16, i1 false)
call void @llvm.memcpy.p0.p0.i64(ptr %dst, ptr %src, i64 16, i1 false)
ret void
}
define void @test_same_dynamic_size(ptr noalias %src, ptr noalias %dst, i64 %size, i8 %c) {
; CHECK-LABEL: @test_same_dynamic_size(
; CHECK-NEXT: [[NON_ZERO:%.*]] = icmp ne i64 [[SIZE:%.*]], 0
; CHECK-NEXT: call void @llvm.assume(i1 [[NON_ZERO]])
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[DST:%.*]], ptr [[SRC:%.*]], i64 [[SIZE]], i1 false)
; CHECK-NEXT: ret void
;
%non.zero = icmp ne i64 %size, 0
call void @llvm.assume(i1 %non.zero)
call void @llvm.memset.p0.i64(ptr %dst, i8 %c, i64 %size, i1 false)
call void @llvm.memcpy.p0.p0.i64(ptr %dst, ptr %src, i64 %size, i1 false)
ret void
}
; Destinations must alias, but are not trivially equal.
define void @test_must_alias_same_size(ptr noalias %src, ptr noalias %dst, i8 %c) {
; CHECK-LABEL: @test_must_alias_same_size(
; CHECK-NEXT: [[GEP1:%.*]] = getelementptr i8, ptr [[DST:%.*]], i64 16
; CHECK-NEXT: [[GEP2:%.*]] = getelementptr i8, ptr [[DST]], i64 16
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[GEP2]], ptr [[SRC:%.*]], i64 16, i1 false)
; CHECK-NEXT: ret void
;
%gep1 = getelementptr i8, ptr %dst, i64 16
call void @llvm.memset.p0.i64(ptr %gep1, i8 %c, i64 16, i1 false)
%gep2 = getelementptr i8, ptr %dst, i64 16
call void @llvm.memcpy.p0.p0.i64(ptr %gep2, ptr %src, i64 16, i1 false)
ret void
}
define void @test_must_alias_different_size(ptr noalias %src, i64 %src_size, ptr noalias %dst, i64 %dst_size, i8 %c) {
; CHECK-LABEL: @test_must_alias_different_size(
; CHECK-NEXT: [[NON_ZERO:%.*]] = icmp ne i64 [[SRC_SIZE:%.*]], 0
; CHECK-NEXT: call void @llvm.assume(i1 [[NON_ZERO]])
; CHECK-NEXT: [[GEP1:%.*]] = getelementptr i8, ptr [[DST:%.*]], i64 16
; CHECK-NEXT: [[GEP2:%.*]] = getelementptr i8, ptr [[DST]], i64 16
; CHECK-NEXT: [[TMP1:%.*]] = icmp ule i64 [[DST_SIZE:%.*]], [[SRC_SIZE]]
; CHECK-NEXT: [[TMP2:%.*]] = sub i64 [[DST_SIZE]], [[SRC_SIZE]]
; CHECK-NEXT: [[TMP3:%.*]] = select i1 [[TMP1]], i64 0, i64 [[TMP2]]
; CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[GEP2]], i64 [[SRC_SIZE]]
; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 1 [[TMP4]], i8 [[C:%.*]], i64 [[TMP3]], i1 false)
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[GEP2]], ptr [[SRC:%.*]], i64 [[SRC_SIZE]], i1 false)
; CHECK-NEXT: ret void
;
%non.zero = icmp ne i64 %src_size, 0
call void @llvm.assume(i1 %non.zero)
%gep1 = getelementptr i8, ptr %dst, i64 16
call void @llvm.memset.p0.i64(ptr %gep1, i8 %c, i64 %dst_size, i1 false)
%gep2 = getelementptr i8, ptr %dst, i64 16
call void @llvm.memcpy.p0.p0.i64(ptr %gep2, ptr %src, i64 %src_size, i1 false)
ret void
}
define void @test_weird_element_type(ptr %src, i64 %src_size, ptr noalias %dst, i64 %dst_size, i8 %c) {
; CHECK-LABEL: @test_weird_element_type(
; CHECK-NEXT: [[NON_ZERO:%.*]] = icmp ne i64 [[SRC_SIZE:%.*]], 0
; CHECK-NEXT: call void @llvm.assume(i1 [[NON_ZERO]])
; CHECK-NEXT: [[TMP1:%.*]] = icmp ule i64 [[DST_SIZE:%.*]], [[SRC_SIZE]]
; CHECK-NEXT: [[TMP2:%.*]] = sub i64 [[DST_SIZE]], [[SRC_SIZE]]
; CHECK-NEXT: [[TMP3:%.*]] = select i1 [[TMP1]], i64 0, i64 [[TMP2]]
; CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[DST:%.*]], i64 [[SRC_SIZE]]
; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 1 [[TMP4]], i8 [[C:%.*]], i64 [[TMP3]], i1 false)
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[DST]], ptr [[SRC:%.*]], i64 [[SRC_SIZE]], i1 false)
; CHECK-NEXT: ret void
;
%non.zero = icmp ne i64 %src_size, 0
call void @llvm.assume(i1 %non.zero)
call void @llvm.memset.p0.i64(ptr %dst, i8 %c, i64 %dst_size, i1 false)
call void @llvm.memcpy.p0.p0.i64(ptr %dst, ptr %src, i64 %src_size, i1 false)
ret void
}
define void @test_addrspace(ptr addrspace(1) %src, i64 %src_size, ptr addrspace(1) noalias %dst, i64 %dst_size, i8 %c) {
; CHECK-LABEL: @test_addrspace(
; CHECK-NEXT: [[NON_ZERO:%.*]] = icmp ne i64 [[SRC_SIZE:%.*]], 0
; CHECK-NEXT: call void @llvm.assume(i1 [[NON_ZERO]])
; CHECK-NEXT: [[TMP1:%.*]] = icmp ule i64 [[DST_SIZE:%.*]], [[SRC_SIZE]]
; CHECK-NEXT: [[TMP2:%.*]] = sub i64 [[DST_SIZE]], [[SRC_SIZE]]
; CHECK-NEXT: [[TMP3:%.*]] = select i1 [[TMP1]], i64 0, i64 [[TMP2]]
; CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr addrspace(1) [[DST:%.*]], i64 [[SRC_SIZE]]
; CHECK-NEXT: call void @llvm.memset.p1.i64(ptr addrspace(1) align 1 [[TMP4]], i8 [[C:%.*]], i64 [[TMP3]], i1 false)
; CHECK-NEXT: call void @llvm.memcpy.p1.p1.i64(ptr addrspace(1) [[DST]], ptr addrspace(1) [[SRC:%.*]], i64 [[SRC_SIZE]], i1 false)
; CHECK-NEXT: ret void
;
%non.zero = icmp ne i64 %src_size, 0
call void @llvm.assume(i1 %non.zero)
call void @llvm.memset.p1.i64(ptr addrspace(1) %dst, i8 %c, i64 %dst_size, i1 false)
call void @llvm.memcpy.p1.p1.i64(ptr addrspace(1) %dst, ptr addrspace(1) %src, i64 %src_size, i1 false)
ret void
}
declare void @llvm.memset.p0.i64(ptr nocapture, i8, i64, i1)
declare void @llvm.memcpy.p0.p0.i64(ptr nocapture, ptr nocapture readonly, i64, i1)
declare void @llvm.memset.p0.i32(ptr nocapture, i8, i32, i1)
declare void @llvm.memcpy.p0.p0.i32(ptr nocapture, ptr nocapture readonly, i32, i1)
declare void @llvm.memset.p0.i128(ptr nocapture, i8, i128, i1)
declare void @llvm.memcpy.p0.p0.i128(ptr nocapture, ptr nocapture readonly, i128, i1)
declare void @llvm.memset.p1.i64(ptr addrspace(1) nocapture, i8, i64, i1)
declare void @llvm.memcpy.p1.p1.i64(ptr addrspace(1) nocapture, ptr addrspace(1) nocapture readonly, i64, i1)
declare void @call()