Nikita Popov 624af932a8 [MemCpyOpt] Port to MemorySSA
This is a straightforward port of MemCpyOpt to MemorySSA following
the approach of D26739. MemDep queries are replaced with MSSA queries
without changing the overall structure of the pass. Some care has
to be taken to account for differences between these APIs
(MemDep also returns reads, MSSA doesn't).

Differential Revision: https://reviews.llvm.org/D89207
2020-12-01 17:57:41 +01:00

246 lines
11 KiB
LLVM

; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt -S -memcpyopt < %s -enable-memcpyopt-memoryssa=0 | FileCheck %s --check-prefixes=CHECK,NO_MSSA
; RUN: opt -S -memcpyopt < %s -enable-memcpyopt-memoryssa=1 -verify-memoryssa | FileCheck %s --check-prefixes=CHECK,MSSA
define i8 @read_dest_between_call_and_memcpy() {
; CHECK-LABEL: @read_dest_between_call_and_memcpy(
; CHECK-NEXT: [[DEST:%.*]] = alloca [16 x i8], align 1
; CHECK-NEXT: [[SRC:%.*]] = alloca [16 x i8], align 1
; CHECK-NEXT: [[DEST_I8:%.*]] = bitcast [16 x i8]* [[DEST]] to i8*
; CHECK-NEXT: [[SRC_I8:%.*]] = bitcast [16 x i8]* [[SRC]] to i8*
; CHECK-NEXT: store i8 1, i8* [[DEST_I8]], align 1
; CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* [[SRC_I8]], i8 0, i64 16, i1 false)
; CHECK-NEXT: [[X:%.*]] = load i8, i8* [[DEST_I8]], align 1
; CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* [[DEST_I8]], i8 0, i64 16, i1 false)
; CHECK-NEXT: ret i8 [[X]]
;
%dest = alloca [16 x i8]
%src = alloca [16 x i8]
%dest.i8 = bitcast [16 x i8]* %dest to i8*
%src.i8 = bitcast [16 x i8]* %src to i8*
store i8 1, i8* %dest.i8
call void @llvm.memset.p0i8.i64(i8* %src.i8, i8 0, i64 16, i1 false)
%x = load i8, i8* %dest.i8
call void @llvm.memcpy.p0i8.p0i8.i64(i8* %dest.i8, i8* %src.i8, i64 16, i1 false)
ret i8 %x
}
define i8 @read_src_between_call_and_memcpy() {
; NO_MSSA-LABEL: @read_src_between_call_and_memcpy(
; NO_MSSA-NEXT: [[DEST:%.*]] = alloca [16 x i8], align 1
; NO_MSSA-NEXT: [[SRC:%.*]] = alloca [16 x i8], align 1
; NO_MSSA-NEXT: [[DEST_I8:%.*]] = bitcast [16 x i8]* [[DEST]] to i8*
; NO_MSSA-NEXT: [[SRC_I8:%.*]] = bitcast [16 x i8]* [[SRC]] to i8*
; NO_MSSA-NEXT: call void @llvm.memset.p0i8.i64(i8* [[SRC_I8]], i8 0, i64 16, i1 false)
; NO_MSSA-NEXT: [[X:%.*]] = load i8, i8* [[SRC_I8]], align 1
; NO_MSSA-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* [[DEST_I8]], i8* [[SRC_I8]], i64 16, i1 false)
; NO_MSSA-NEXT: ret i8 [[X]]
;
; MSSA-LABEL: @read_src_between_call_and_memcpy(
; MSSA-NEXT: [[DEST:%.*]] = alloca [16 x i8], align 1
; MSSA-NEXT: [[SRC:%.*]] = alloca [16 x i8], align 1
; MSSA-NEXT: [[DEST_I8:%.*]] = bitcast [16 x i8]* [[DEST]] to i8*
; MSSA-NEXT: [[SRC_I8:%.*]] = bitcast [16 x i8]* [[SRC]] to i8*
; MSSA-NEXT: call void @llvm.memset.p0i8.i64(i8* [[SRC_I8]], i8 0, i64 16, i1 false)
; MSSA-NEXT: [[X:%.*]] = load i8, i8* [[SRC_I8]], align 1
; MSSA-NEXT: call void @llvm.memset.p0i8.i64(i8* [[DEST_I8]], i8 0, i64 16, i1 false)
; MSSA-NEXT: ret i8 [[X]]
;
%dest = alloca [16 x i8]
%src = alloca [16 x i8]
%dest.i8 = bitcast [16 x i8]* %dest to i8*
%src.i8 = bitcast [16 x i8]* %src to i8*
call void @llvm.memset.p0i8.i64(i8* %src.i8, i8 0, i64 16, i1 false)
%x = load i8, i8* %src.i8
call void @llvm.memcpy.p0i8.p0i8.i64(i8* %dest.i8, i8* %src.i8, i64 16, i1 false)
ret i8 %x
}
define void @write_dest_between_call_and_memcpy() {
; CHECK-LABEL: @write_dest_between_call_and_memcpy(
; CHECK-NEXT: [[DEST:%.*]] = alloca [16 x i8], align 1
; CHECK-NEXT: [[SRC:%.*]] = alloca [16 x i8], align 1
; CHECK-NEXT: [[DEST_I8:%.*]] = bitcast [16 x i8]* [[DEST]] to i8*
; CHECK-NEXT: [[SRC_I8:%.*]] = bitcast [16 x i8]* [[SRC]] to i8*
; CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* [[SRC_I8]], i8 0, i64 16, i1 false)
; CHECK-NEXT: store i8 1, i8* [[DEST_I8]], align 1
; CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* [[DEST_I8]], i8 0, i64 16, i1 false)
; CHECK-NEXT: ret void
;
%dest = alloca [16 x i8]
%src = alloca [16 x i8]
%dest.i8 = bitcast [16 x i8]* %dest to i8*
%src.i8 = bitcast [16 x i8]* %src to i8*
call void @llvm.memset.p0i8.i64(i8* %src.i8, i8 0, i64 16, i1 false)
store i8 1, i8* %dest.i8
call void @llvm.memcpy.p0i8.p0i8.i64(i8* %dest.i8, i8* %src.i8, i64 16, i1 false)
ret void
}
define void @write_src_between_call_and_memcpy() {
; CHECK-LABEL: @write_src_between_call_and_memcpy(
; CHECK-NEXT: [[DEST:%.*]] = alloca [16 x i8], align 1
; CHECK-NEXT: [[SRC:%.*]] = alloca [16 x i8], align 1
; CHECK-NEXT: [[DEST_I8:%.*]] = bitcast [16 x i8]* [[DEST]] to i8*
; CHECK-NEXT: [[SRC_I8:%.*]] = bitcast [16 x i8]* [[SRC]] to i8*
; CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* [[SRC_I8]], i8 0, i64 16, i1 false)
; CHECK-NEXT: store i8 1, i8* [[SRC_I8]], align 1
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* [[DEST_I8]], i8* [[SRC_I8]], i64 16, i1 false)
; CHECK-NEXT: ret void
;
%dest = alloca [16 x i8]
%src = alloca [16 x i8]
%dest.i8 = bitcast [16 x i8]* %dest to i8*
%src.i8 = bitcast [16 x i8]* %src to i8*
call void @llvm.memset.p0i8.i64(i8* %src.i8, i8 0, i64 16, i1 false)
store i8 1, i8* %src.i8
call void @llvm.memcpy.p0i8.p0i8.i64(i8* %dest.i8, i8* %src.i8, i64 16, i1 false)
ret void
}
define void @throw_between_call_and_mempy(i8* dereferenceable(16) %dest.i8) {
; CHECK-LABEL: @throw_between_call_and_mempy(
; CHECK-NEXT: [[SRC:%.*]] = alloca [16 x i8], align 1
; CHECK-NEXT: [[SRC_I8:%.*]] = bitcast [16 x i8]* [[SRC]] to i8*
; CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* [[SRC_I8]], i8 0, i64 16, i1 false)
; CHECK-NEXT: call void @may_throw() [[ATTR2:#.*]]
; CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* [[DEST_I8:%.*]], i8 0, i64 16, i1 false)
; CHECK-NEXT: ret void
;
%src = alloca [16 x i8]
%src.i8 = bitcast [16 x i8]* %src to i8*
call void @llvm.memset.p0i8.i64(i8* %src.i8, i8 0, i64 16, i1 false)
call void @may_throw() readnone
call void @llvm.memcpy.p0i8.p0i8.i64(i8* %dest.i8, i8* %src.i8, i64 16, i1 false)
ret void
}
define void @dest_is_gep_nounwind_call() {
; CHECK-LABEL: @dest_is_gep_nounwind_call(
; CHECK-NEXT: [[DEST:%.*]] = alloca [16 x i8], align 1
; CHECK-NEXT: [[SRC:%.*]] = alloca [8 x i8], align 1
; CHECK-NEXT: [[SRC_I8:%.*]] = bitcast [8 x i8]* [[SRC]] to i8*
; CHECK-NEXT: [[DEST_I8:%.*]] = getelementptr [16 x i8], [16 x i8]* [[DEST]], i64 0, i64 8
; CHECK-NEXT: [[DEST_I81:%.*]] = bitcast i8* [[DEST_I8]] to [8 x i8]*
; CHECK-NEXT: [[DEST_I812:%.*]] = bitcast [8 x i8]* [[DEST_I81]] to i8*
; CHECK-NEXT: call void @accept_ptr(i8* [[DEST_I812]]) [[ATTR3:#.*]]
; CHECK-NEXT: ret void
;
%dest = alloca [16 x i8]
%src = alloca [8 x i8]
%src.i8 = bitcast [8 x i8]* %src to i8*
%dest.i8 = getelementptr [16 x i8], [16 x i8]* %dest, i64 0, i64 8
call void @accept_ptr(i8* %src.i8) nounwind
call void @llvm.memcpy.p0i8.p0i8.i64(i8* %dest.i8, i8* %src.i8, i64 8, i1 false)
ret void
}
define void @dest_is_gep_may_throw_call() {
; CHECK-LABEL: @dest_is_gep_may_throw_call(
; CHECK-NEXT: [[DEST:%.*]] = alloca [16 x i8], align 1
; CHECK-NEXT: [[SRC:%.*]] = alloca [8 x i8], align 1
; CHECK-NEXT: [[SRC_I8:%.*]] = bitcast [8 x i8]* [[SRC]] to i8*
; CHECK-NEXT: [[DEST_I8:%.*]] = getelementptr [16 x i8], [16 x i8]* [[DEST]], i64 0, i64 8
; CHECK-NEXT: [[DEST_I81:%.*]] = bitcast i8* [[DEST_I8]] to [8 x i8]*
; CHECK-NEXT: [[DEST_I812:%.*]] = bitcast [8 x i8]* [[DEST_I81]] to i8*
; CHECK-NEXT: call void @accept_ptr(i8* [[DEST_I812]])
; CHECK-NEXT: ret void
;
%dest = alloca [16 x i8]
%src = alloca [8 x i8]
%src.i8 = bitcast [8 x i8]* %src to i8*
%dest.i8 = getelementptr [16 x i8], [16 x i8]* %dest, i64 0, i64 8
call void @accept_ptr(i8* %src.i8)
call void @llvm.memcpy.p0i8.p0i8.i64(i8* %dest.i8, i8* %src.i8, i64 8, i1 false)
ret void
}
define void @dest_is_gep_requires_movement() {
; CHECK-LABEL: @dest_is_gep_requires_movement(
; CHECK-NEXT: [[DEST:%.*]] = alloca [16 x i8], align 1
; CHECK-NEXT: [[SRC:%.*]] = alloca [8 x i8], align 1
; CHECK-NEXT: [[SRC_I8:%.*]] = bitcast [8 x i8]* [[SRC]] to i8*
; CHECK-NEXT: [[DEST_I8:%.*]] = getelementptr [16 x i8], [16 x i8]* [[DEST]], i64 0, i64 8
; CHECK-NEXT: [[DEST_I81:%.*]] = bitcast i8* [[DEST_I8]] to [8 x i8]*
; CHECK-NEXT: [[DEST_I812:%.*]] = bitcast [8 x i8]* [[DEST_I81]] to i8*
; CHECK-NEXT: call void @accept_ptr(i8* [[DEST_I812]]) [[ATTR3]]
; CHECK-NEXT: ret void
;
%dest = alloca [16 x i8]
%src = alloca [8 x i8]
%src.i8 = bitcast [8 x i8]* %src to i8*
call void @accept_ptr(i8* %src.i8) nounwind
%dest.i8 = getelementptr [16 x i8], [16 x i8]* %dest, i64 0, i64 8
call void @llvm.memcpy.p0i8.p0i8.i64(i8* %dest.i8, i8* %src.i8, i64 8, i1 false)
ret void
}
define void @capture_before_call_argmemonly() {
; CHECK-LABEL: @capture_before_call_argmemonly(
; CHECK-NEXT: [[DEST:%.*]] = alloca [16 x i8], align 1
; CHECK-NEXT: [[SRC:%.*]] = alloca [16 x i8], align 1
; CHECK-NEXT: [[DEST_I8:%.*]] = bitcast [16 x i8]* [[DEST]] to i8*
; CHECK-NEXT: [[SRC_I8:%.*]] = bitcast [16 x i8]* [[SRC]] to i8*
; CHECK-NEXT: call void @accept_ptr(i8* [[DEST_I8]])
; CHECK-NEXT: [[DEST1:%.*]] = bitcast [16 x i8]* [[DEST]] to i8*
; CHECK-NEXT: call void @accept_ptr(i8* [[DEST1]]) [[ATTR4:#.*]]
; CHECK-NEXT: ret void
;
%dest = alloca [16 x i8]
%src = alloca [16 x i8]
%dest.i8 = bitcast [16 x i8]* %dest to i8*
%src.i8 = bitcast [16 x i8]* %src to i8*
call void @accept_ptr(i8* %dest.i8) ; capture
call void @accept_ptr(i8* %src.i8) argmemonly
call void @llvm.memcpy.p0i8.p0i8.i64(i8* %dest.i8, i8* %src.i8, i64 16, i1 false)
ret void
}
define void @capture_before_call_argmemonly_nounwind() {
; CHECK-LABEL: @capture_before_call_argmemonly_nounwind(
; CHECK-NEXT: [[DEST:%.*]] = alloca [16 x i8], align 1
; CHECK-NEXT: [[SRC:%.*]] = alloca [16 x i8], align 1
; CHECK-NEXT: [[DEST_I8:%.*]] = bitcast [16 x i8]* [[DEST]] to i8*
; CHECK-NEXT: [[SRC_I8:%.*]] = bitcast [16 x i8]* [[SRC]] to i8*
; CHECK-NEXT: call void @accept_ptr(i8* [[DEST_I8]])
; CHECK-NEXT: [[DEST1:%.*]] = bitcast [16 x i8]* [[DEST]] to i8*
; CHECK-NEXT: call void @accept_ptr(i8* [[DEST1]]) [[ATTR5:#.*]]
; CHECK-NEXT: ret void
;
%dest = alloca [16 x i8]
%src = alloca [16 x i8]
%dest.i8 = bitcast [16 x i8]* %dest to i8*
%src.i8 = bitcast [16 x i8]* %src to i8*
call void @accept_ptr(i8* %dest.i8) ; capture
; NB: argmemonly currently implies willreturn.
call void @accept_ptr(i8* %src.i8) argmemonly nounwind
call void @llvm.memcpy.p0i8.p0i8.i64(i8* %dest.i8, i8* %src.i8, i64 16, i1 false)
ret void
}
define void @capture_before_call_argmemonly_nounwind_willreturn() {
; CHECK-LABEL: @capture_before_call_argmemonly_nounwind_willreturn(
; CHECK-NEXT: [[DEST:%.*]] = alloca [16 x i8], align 1
; CHECK-NEXT: [[SRC:%.*]] = alloca [16 x i8], align 1
; CHECK-NEXT: [[DEST_I8:%.*]] = bitcast [16 x i8]* [[DEST]] to i8*
; CHECK-NEXT: [[SRC_I8:%.*]] = bitcast [16 x i8]* [[SRC]] to i8*
; CHECK-NEXT: call void @accept_ptr(i8* [[DEST_I8]])
; CHECK-NEXT: [[DEST1:%.*]] = bitcast [16 x i8]* [[DEST]] to i8*
; CHECK-NEXT: call void @accept_ptr(i8* [[DEST1]]) [[ATTR0:#.*]]
; CHECK-NEXT: ret void
;
%dest = alloca [16 x i8]
%src = alloca [16 x i8]
%dest.i8 = bitcast [16 x i8]* %dest to i8*
%src.i8 = bitcast [16 x i8]* %src to i8*
call void @accept_ptr(i8* %dest.i8) ; capture
call void @accept_ptr(i8* %src.i8) argmemonly nounwind willreturn
call void @llvm.memcpy.p0i8.p0i8.i64(i8* %dest.i8, i8* %src.i8, i64 16, i1 false)
ret void
}
declare void @may_throw()
declare void @accept_ptr(i8*)
declare void @llvm.memcpy.p0i8.p0i8.i64(i8*, i8*, i64, i1)
declare void @llvm.memset.p0i8.i64(i8*, i8, i64, i1)